class_docstr: DataHandler with **(L)earnable (P)rocessor**
  
  This handler will produce three pieces of data in pd.DataFrame format.
  
  - DK_R / self._data: the raw data loaded from the loader
  - DK_I / self._infer: the data processed for inference
  - DK_L / self._learn: the data processed for learning model.
  
  The motivation of using different processor workflows for learning and inference
  Here are some examples.
  
  - The instrument universe for learning and inference may be different.
  - The processing of some samples may rely on label (for example, some samples hit the limit may need extra processing or be dropped).
  
      - These processors only apply to the learning phase.
  
  Tips for data handler
  
  - To reduce the memory cost
  
      - `drop_raw=True`: this will modify the data inplace on raw data;
  
  - Please note processed data like `self._infer` or `self._learn` are concepts different from `segments` in Qlib's `Dataset` like "train" and "test"
  
      - Processed data like `self._infer` or `self._learn` are underlying data processed with different processors
      - `segments` in Qlib's `Dataset` like "train" and "test" are simply the time segmentations when querying data("train" are often before "test" in time-series).
      - For example, you can query `data._infer` processed by `infer_processors` in the "train" time segmentation.
docstr: process_data data. Fun `processor.fit` if necessary
  
  Notation: (data)  [processor]
  
  # data processing flow of self.process_type == DataHandlerLP.PTYPE_I
  
  .. code-block:: text
  
      (self._data)-[shared_processors]-(_shared_df)-[learn_processors]-(_learn_df)
                                             \
                                              -[infer_processors]-(_infer_df)
  
  # data processing flow of self.process_type == DataHandlerLP.PTYPE_A
  
  .. code-block:: text
  
      (self._data)-[shared_processors]-(_shared_df)-[infer_processors]-(_infer_df)-[learn_processors]-(_learn_df)
  
  Parameters
  ----------
  with_fit : bool
      The input of the `fit` will be the output of the previous processor
code: def process_data(self, with_fit: bool = False):
      """
          process_data data. Fun `processor.fit` if necessary
  
          Notation: (data)  [processor]
  
          # data processing flow of self.process_type == DataHandlerLP.PTYPE_I
  
          .. code-block:: text
  
              (self._data)-[shared_processors]-(_shared_df)-[learn_processors]-(_learn_df)
                                                     \\
                                                      -[infer_processors]-(_infer_df)
  
          # data processing flow of self.process_type == DataHandlerLP.PTYPE_A
  
          .. code-block:: text
  
              (self._data)-[shared_processors]-(_shared_df)-[infer_processors]-(_infer_df)-[learn_processors]-(_learn_df)
  
          Parameters
          ----------
          with_fit : bool
              The input of the `fit` will be the output of the previous processor
          """
      # shared data processors
      # 1) assign
      _shared_df = self._data
      if not self._is_proc_readonly(
          self.shared_processors
      ):  # avoid modifying the original data
          _shared_df = _shared_df.copy()
      # 2) process
      _shared_df = self._run_proc_l(
          _shared_df,
          self.shared_processors,
          with_fit=with_fit,
          check_for_infer=True,
      )
  
      # data for inference
      # 1) assign
      _infer_df = _shared_df
      if not self._is_proc_readonly(
          self.infer_processors
      ):  # avoid modifying the original data
          _infer_df = _infer_df.copy()
      # 2) process
      _infer_df = self._run_proc_l(
          _infer_df,
          self.infer_processors,
          with_fit=with_fit,
          check_for_infer=True,
      )
  
      self._infer = _infer_df
  
      # data for learning
      # 1) assign
      if self.process_type == DataHandlerLP.PTYPE_I:
          _learn_df = _shared_df
      elif self.process_type == DataHandlerLP.PTYPE_A:
          # based on `infer_df` and append the processor
          _learn_df = _infer_df
      else:
          raise NotImplementedError(
              f"This type of input is not supported"
          )
      if not self._is_proc_readonly(
          self.learn_processors
      ):  # avoid modifying the original  data
          _learn_df = _learn_df.copy()
      # 2) process
      _learn_df = self._run_proc_l(
          _learn_df,
          self.learn_processors,
          with_fit=with_fit,
          check_for_infer=False,
      )
  
      self._learn = _learn_df
  
      if self.drop_raw:
          del self._data
  
imports: import warnings
  from typing import Callable, Union, Tuple, List, Iterator, Optional
  import pandas
  from qlib.typehint import Literal
  from ...log import get_module_logger, TimeInspector
  from ...utils import init_instance_by_config
  from ...utils.serial import Serializable
  from .utils import fetch_df_by_index, fetch_df_by_col
  from ...utils import lazy_sort_index
  from .loader import DataLoader
  from .None import processor
  from .None import loader
label_desc: Use image node_id 7 for calling the DataHandlerLP obj's underlying member method code with example usage: obj.process_data(with_fit) without return types