class_docstr: Preprocess one multimodal pd.DataFrame including image paths, image bytearrays, texts, numerical features,
  and categorical features. Each modality may have multiple columns.
  The preprocessor is designed to output model-agnostic features.
class_docstr_tok: ["Preprocess","one","multimodal","pd.DataFrame","including","image","paths",",","image","bytearrays",",","texts",",","numerical","features",",","and","categorical","features",".","Each","modality","may","have","multiple","columns",".","The","preprocessor","is","designed","to","output","model-agnostic","features","."]
docstr: Fit the pd.DataFrame by grouping column names by their modality types. For example, all the
  names of text columns will be put in a list. The CategoryFeatureGenerator, SimpleImputer, and
  StandardScaler will also be initialized.
  
  Parameters
  ----------
  X
      A multimodal pd.DataFrame.
code: def _fit_x(self, X: pd.DataFrame):
      """
      Fit the pd.DataFrame by grouping column names by their modality types. For example, all the
      names of text columns will be put in a list. The CategoryFeatureGenerator, SimpleImputer, and
      StandardScaler will also be initialized.
  
      Parameters
      ----------
      X
          A multimodal pd.DataFrame.
      """
  
      if self._fit_x_called:
          raise RuntimeError(
              "fit_x() has been called. Please create a new preprocessor and call it again!"
          )
      self._fit_x_called = True
  
      for col_name in sorted(X.columns):
          # Just in case X accidentally contains the label column
          if col_name == self._label_column:
              continue
          col_type = self._column_types[col_name]
          logger.debug(
              f'Process col "{col_name}" with type "{col_type}"'
          )
          col_value = X[col_name]
          if col_type == NULL:
              self._ignore_columns_set.add(col_name)
          elif col_type.startswith(TEXT_NER):
              self._ner_feature_names.append(col_name)
          elif col_type.startswith(TEXT):
              self._text_feature_names.append(col_name)
          elif col_type == CATEGORICAL:
              if self._config.categorical.convert_to_text:
                  # Convert categorical column as text column
                  col_value = col_value.astype("object")
                  processed_data = col_value.apply(
                      lambda ele: "" if pd.isnull(ele) else str(ele)
                  )
                  if len(processed_data.unique()) == 1:
                      self._ignore_columns_set.add(col_name)
                      continue
                  self._text_feature_names.append(col_name)
              else:
                  processed_data = col_value.astype("category")
                  generator = self._feature_generators[col_name]
                  processed_data = generator.fit_transform(
                      pd.DataFrame({col_name: processed_data})
                  )[col_name].cat.codes.to_numpy(np.int32, copy=True)
                  if len(np.unique(processed_data)) == 1:
                      self._ignore_columns_set.add(col_name)
                      continue
                  num_categories = len(generator.category_map[col_name])
                  # Add one unknown category
                  self._categorical_num_categories.append(
                      num_categories + 1
                  )
                  self._categorical_feature_names.append(col_name)
          elif col_type == NUMERICAL:
              processed_data = pd.to_numeric(col_value)
              if len(processed_data.unique()) == 1:
                  self._ignore_columns_set.add(col_name)
                  continue
              if self._config.numerical.convert_to_text:
                  self._text_feature_names.append(col_name)
              else:
                  generator = self._feature_generators[col_name]
                  generator.fit(
                      np.expand_dims(processed_data.to_numpy(), axis=-1)
                  )
                  self._numerical_feature_names.append(col_name)
          elif col_type.startswith(IMAGE):
              self._image_feature_names.append(col_name)
          elif col_type.startswith(DOCUMENT):
              self._document_feature_names.append(col_name)
          elif col_type == ROIS:
              self._rois_feature_names.append(col_name)
          elif col_type == SEMANTIC_SEGMENTATION_IMG:
              self._semantic_segmentation_feature_names.append(col_name)
          else:
              raise NotImplementedError(
                  f"Type of the column is not supported currently. Received {col_name}={col_type}."
              )
  
imports: import logging
  from typing import Any, Dict, List, Optional, Tuple, Union
  import numpy
  import pandas
  from nptyping import NDArray
  from omegaconf import DictConfig, OmegaConf
  from sklearn.base import BaseEstimator, TransformerMixin
  from sklearn.impute import SimpleImputer
  from sklearn.pipeline import Pipeline
  from sklearn.preprocessing import MinMaxScaler, StandardScaler
  from autogluon.features import CategoryFeatureGenerator
  from ..constants import AUTOMM, CATEGORICAL, DOCUMENT, DOCUMENT_IMAGE, IDENTIFIER, IMAGE, IMAGE_BYTEARRAY, IMAGE_PATH, LABEL, NER, NER_ANNOTATION, NULL, NUMERICAL, OVD, ROIS, SEMANTIC_SEGMENTATION, SEMANTIC_SEGMENTATION_GT, SEMANTIC_SEGMENTATION_IMG, TEXT, TEXT_NER
  from .label_encoder import CustomLabelEncoder
label_desc: Use image node_id 25 for calling the MultiModalFeaturePreprocessor obj's underlying member method code with example usage: obj._fit_x(X) without return types