code: def preclean_X(
      X, feature_names, feature_types, n_samples=None, sample_source="y"
  ):
      # called under: fit or predict
      min_cols = _determine_min_cols(feature_names, feature_types)
  
      if isinstance(X, np.ndarray):  # this includes ma.masked_array
          if X.ndim != 2:
              X = _reshape_X(X, min_cols, n_samples, sample_source)
          if n_samples is not None and n_samples != X.shape[0]:
              msg = f"{sample_source} has {n_samples} samples, but X has {X.shape[0]}"
              _log.error(msg)
              raise ValueError(msg)
          return X, X.shape[0]
      elif _pandas_installed and isinstance(X, pd.DataFrame):
          if n_samples is not None and n_samples != X.shape[0]:
              msg = f"{sample_source} has {n_samples} samples, but X has {X.shape[0]}"
              _log.error(msg)
              raise ValueError(msg)
          return X, X.shape[0]
      elif _pandas_installed and isinstance(X, pd.Series):
          if min_cols is not None and min_cols != 1:
              msg = "X cannot be a pandas.Series unless there is only 1 feature"
              _log.error(msg)
              raise ValueError(msg)
          if n_samples is not None and n_samples != X.shape[0]:
              msg = f"{sample_source} has {n_samples} samples, but X has {X.shape[0]}"
              _log.error(msg)
              raise ValueError(msg)
          return X, X.shape[0]
      elif _scipy_installed and isinstance(X, sp.sparse.spmatrix):
          if n_samples is not None and n_samples != X.shape[0]:
              msg = f"{sample_source} has {n_samples} samples, but X has {X.shape[0]}"
              _log.error(msg)
              raise ValueError(msg)
          return X, X.shape[0]
      elif isinstance(X, dict):
          for val in X.values():
              if isinstance(val, np.ndarray) and val.ndim == 0:
                  break
              # we don't support iterators for dict, so len should work
              if n_samples is not None and n_samples != len(val):
                  msg = f"{sample_source} has {n_samples} samples, but X has {X.shape[0]}"
                  _log.error(msg)
                  raise ValueError(msg)
              return X, len(val)
          if n_samples is not None and n_samples != 0:
              msg = f"{sample_source} has {n_samples} samples, but X has {X.shape[0]}"
              _log.error(msg)
              raise ValueError(msg)
          return X, 0
      elif isinstance(X, list) or isinstance(X, tuple):
          is_copied = False
      elif callable(getattr(X, "__array__", None)):
          X = X.__array__()
          if X.ndim != 2:
              X = _reshape_X(X, min_cols, n_samples, sample_source)
          if n_samples is not None and n_samples != X.shape[0]:
              msg = f"{sample_source} has {n_samples} samples, but X has {X.shape[0]}"
              _log.error(msg)
              raise ValueError(msg)
          return X, X.shape[0]
      elif X is None:
          msg = "X cannot be None"
          _log.error(msg)
          raise TypeError(msg)
      elif isinstance(X, str):
          # str objects are iterable, so don't allow them to get to the list() conversion below
          msg = "X cannot be a str type"
          _log.error(msg)
          raise TypeError(msg)
      else:
          try:
              X = list(X)
              is_copied = True
          except TypeError:
              msg = "X must be an iterable"
              _log.error(msg)
              raise TypeError(msg)
  
      # for consistency with what the caller expects, we should mirror what np.array([[..], [..], .., [..]]) does
      # [1, 2, 3] is one sample with 3 features
      # [[1], [2], [3]] is three samples with 1 feature
      # [[1], [2], 3] is bug prone.  You could argue that it has to be a single sample since
      #   the 3 only makes sense in that context, but if the 2 value was removed it would change
      #   from being a single sample with 3 features to being two samples with a single feature,
      #   so force the user to have consistent inner lists/objects
  
      for idx in range(len(X)):
          sample = X[idx]
          if isinstance(sample, list) or isinstance(sample, tuple):
              pass
          elif isinstance(sample, ma.masked_array):
              # do this before np.ndarray since ma.masked_array is a subclass of np.ndarray
              if not is_copied:
                  is_copied = True
                  X = list(X)
              X[idx] = _reshape_1D_if_possible(
                  sample.astype(np.object_, copy=False).filled(np.nan)
              )
          elif isinstance(sample, np.ndarray):
              if sample.ndim == 1:
                  pass
              else:
                  if not is_copied:
                      is_copied = True
                      X = list(X)
                  X[idx] = _reshape_1D_if_possible(sample)
          elif _pandas_installed and isinstance(sample, pd.Series):
              if not is_copied:
                  is_copied = True
                  X = list(X)
              X[idx] = sample.astype(np.object_, copy=False).values
          elif _pandas_installed and isinstance(sample, pd.DataFrame):
              if sample.shape[1] == 1 or sample.shape[0] == 1:
                  if not is_copied:
                      is_copied = True
                      X = list(X)
                  X[idx] = sample.astype(
                      np.object_, copy=False
                  ).values.reshape(-1)
              elif sample.shape[1] == 0 or sample.shape[0] == 0:
                  if not is_copied:
                      is_copied = True
                      X = list(X)
                  X[idx] = np.empty(0, np.object_)
              else:
                  msg = f"Cannot reshape to 1D. Original shape was {sample.shape}"
                  _log.error(msg)
                  raise ValueError(msg)
          elif _scipy_installed and isinstance(
              sample, sp.sparse.spmatrix
          ):
              if sample.shape[1] == 1 or sample.shape[0] == 1:
                  if not is_copied:
                      is_copied = True
                      X = list(X)
                  X[idx] = sample.toarray().reshape(-1)
              elif sample.shape[1] == 0 or sample.shape[0] == 0:
                  if not is_copied:
                      is_copied = True
                      X = list(X)
                  X[idx] = np.empty(0, np.object_)
              else:
                  msg = f"Cannot reshape to 1D. Original shape was {sample.shape}"
                  _log.error(msg)
                  raise ValueError(msg)
          elif callable(getattr(sample, "__array__", None)):
              sample = sample.__array__()
              if not is_copied:
                  is_copied = True
                  X = list(X)
              X[idx] = _reshape_1D_if_possible(sample)
          elif isinstance(sample, str):
              break  # this only legal if we have one sample
          else:
              try:
                  sample = list(sample)
                  if not is_copied:
                      is_copied = True
                      X = list(X)
                  X[idx] = sample
              except TypeError:
                  break  # this only legal if we have one sample
  
      # leave these as np.object_ for now and we'll try to densify per column where we're more likely to
      # succeed in densification since columns should generally be a single type
      X = np.array(X, np.object_)
      if X.ndim != 2:
          X = _reshape_X(X, min_cols, n_samples, sample_source)
      if n_samples is not None and n_samples != X.shape[0]:
          msg = f"{sample_source} has {n_samples} samples, but X has {X.shape[0]}"
          _log.error(msg)
          raise ValueError(msg)
      return X, X.shape[0]
  
imports: from collections import Counter
  from itertools import count
  import numpy
  import numpy.ma
  import logging
label_desc: Use image node_id 17 for calling a global function with example usage: preclean_X(X, feature_names, feature_types, n_samples, sample_source) and returns: X,  X, X,  X, X,  X, X,  X, X,  X, X,  int, X,  len, X,  X