class_docstr: Class which contains methods for data preprocessing.
  The class performs two types of preprocessing: obligatory and optional.
  
  obligatory - deletes rows where nans in the target, removes features,
      which full of nans, deletes extra_spaces
  optional - depends on what operations are in the pipeline, gap-filling
      is applied if there is no imputation operation in the pipeline, categorical
      encoding is applied if there is no encoder in the structure of the pipeline etc.
class_docstr_tok: ["Class","which","contains","methods","for","data","preprocessing",".","The","class","performs","two","types","of","preprocessing",":","obligatory","and","optional",".","obligatory","-","deletes","rows","where","nans","in","the","target",",","removes","features",",","which","full","of","nans",",","deletes","extra_spaces","optional","-","depends","on","what","operations","are","in","the","pipeline",",","gap-filling","is","applied","if","there","is","no","imputation","operation","in","the","pipeline",",","categorical","encoding","is","applied","if","there","is","no","encoder","in","the","structure","of","the","pipeline","etc","."]
docstr: Processes InputData for pipeline fit method
  
  Args:
      data: to be preprocessed
      source_name: name of the data source node
  
  Returns:
      obligatory-prepared ``data``
docstr_tok: ["Processes","InputData","for","pipeline","fit","method","Args",":","data",":","to","be","preprocessed","source_name",":","name","of","the","data","source","node","Returns",":","obligatory-prepared","``","data","``"]
code: def _prepare_obligatory_unimodal(
      self,
      data: InputData,
      source_name: str,
      *,
      is_fit_stage: bool = True,
  ) -> InputData:
      """
      Processes InputData for pipeline fit method
  
      Args:
          data: to be preprocessed
          source_name: name of the data source node
  
      Returns:
          obligatory-prepared ``data``
      """
      if data.supplementary_data.obligatorily_preprocessed:
          # Preprocessing was already done - return data
          return data
  
      # Convert datetime data to numerical
      data.features = np_datetime_to_numeric(data.features)
      if data.target is not None:
          data.target = np_datetime_to_numeric(data.target)
  
      # Wrap indices in numpy array if needed
      data.idx = np.asarray(data.idx)
  
      # Fix tables / time series sizes
      data = self._correct_shapes(data)
      replace_inf_with_nans(data)
  
      # Find incorrect features which must be removed
      if is_fit_stage:
          self._find_features_lacking_nans(data, source_name)
      self._take_only_correct_features(data, source_name)
      if is_fit_stage:
          data = self._drop_rows_with_nan_in_target(data)
  
          # Column types processing - launch after correct features selection
          self.types_correctors[source_name].convert_data_for_fit(data)
          if self.types_correctors[
              source_name
          ].target_converting_has_errors:
              data = self._drop_rows_with_nan_in_target(data)
          # Train Label Encoder for categorical target if necessary and apply it
          if source_name not in self.target_encoders:
              self._train_target_encoder(data, source_name)
          data.target = self._apply_target_encoding(data, source_name)
      else:
          self.types_correctors[source_name].convert_data_for_predict(
              data
          )
  
      # TODO andreygetmanov target encoding must be obligatory for all data types
      if data_type_is_text(data):
          # TODO andreygetmanov to new class text preprocessing?
          replace_nans_with_empty_strings(data)
      elif data_type_is_table(data):
          data = self._clean_extra_spaces(data)
          # Process binary categorical features
          if is_fit_stage:
              data = self.binary_categorical_processors[
                  source_name
              ].fit_transform(data)
          else:
              data = self.binary_categorical_processors[
                  source_name
              ].transform(data)
  
      return data
  
imports: from copy import copy
  from typing import Optional, Union
  import numpy
  import pandas
  from golem.core.log import default_log
  from golem.core.paths import copy_doc
  from sklearn.preprocessing import LabelEncoder
  from fedot.core.data.data import InputData, np_datetime_to_numeric
  from fedot.core.data.data import OutputData, data_type_is_table, data_type_is_text, data_type_is_ts
  from fedot.core.data.data_preprocessing import data_has_categorical_features, data_has_missing_values, find_categorical_columns, replace_inf_with_nans, replace_nans_with_empty_strings
  from fedot.core.data.multi_modal import MultiModalData
  from fedot.core.operations.evaluation.operation_implementations.data_operations.categorical_encoders import LabelEncodingImplementation, OneHotEncodingImplementation
  from fedot.core.operations.evaluation.operation_implementations.data_operations.sklearn_transformations import ImputationImplementation
  from fedot.core.repository.dataset_types import DataTypesEnum
  from fedot.core.repository.tasks import TaskTypesEnum
  from fedot.preprocessing.base_preprocessing import BasePreprocessor
  from fedot.preprocessing.categorical import BinaryCategoricalPreprocessor
  from fedot.preprocessing.data_type_check import exclude_image, exclude_multi_ts, exclude_ts
  from fedot.preprocessing.data_types import TYPE_TO_ID, TableTypesCorrector
  from fedot.preprocessing.structure import DEFAULT_SOURCE_NAME, PipelineStructureExplorer
label_desc: Use image node_id 10 for calling the DataPreprocessor obj's underlying member method code with example usage: obj._prepare_obligatory_unimodal(data, source_name) and returns: data, data