class_docstr: Create a FeatureCollection.
  
  Parameters
  ----------
  feature_descriptors : Union[FeatureDescriptor, MultipleFeatureDescriptors, FeatureCollection, List[Union[FeatureDescriptor, MultipleFeatureDescriptors, FeatureCollection]]], optional
      Initial (list of) feature(s) to add to collection, by default None
  
  Notes
  -----
  * The `series_name` property of the `FeatureDescriptor`s should **not withhold a "|"
    character**, since "|" is used to join the series names of features which use
    multiple series as input).<br>
    e.g.<br>
      * `ACC|x` is **not** allowed as series name, as this is ambiguous and could
        represent that this feature is constructed with a combination of the `ACC`
        and `x` signal.<br>
        Note that `max|feat` is allowed as feature output name.
  * Both the `series_name` and `output_name` property of the `FeatureDescriptor`s
    **should not withhold "__"** in its string representations. This constraint is
    mainly made for readability purposes.
  
  The two statements above will be asserted
docstr: Calculate features on the passed data.
  
  Parameters
  ----------
  data : Union[pd.Series, pd.DataFrame, List[Union[pd.Series, pd.DataFrame]]]
      Dataframe or Series or list thereof, with all the required data for the
      feature calculation. 
  
      **Assumptions**: 
  
      * each Series / DataFrame must have a sortable index. This index represents
      the sequence position of the corresponding values, the index can be either
      numeric or a ``pd.DatetimeIndex``.
      * each Series / DataFrame index must be comparable with all others
      * we assume that each series-name / dataframe-column-name is unique.
  stride: Union[float, str, pd.Timedelta, List[Union[float, str, pd.Timedelta], None], optional
      The stride size. By default None. This argument supports multiple types: 
  
      * If None, the stride of the `FeatureDescriptor` objects will be used.
      * If the type is an `float` or an `int`, its value represents the series:
  
          - its stride **range** when a **non time-indexed** series is passed.
          - the stride in **number of samples**, when a **time-indexed** series
          is passed (must then be and `int`)
      * If the stride's type is a `pd.Timedelta`, the stride size represents
      the stride-time delta. The passed data **must have a time-index**.
      * If a `str`, it must represent a stride-time-delta-string. Hence, the
      **passed data must have a time-index**. 
  
      .. Note::
          When set, this stride argument takes precedence over the stride property
          of the `FeatureDescriptor`s in this `FeatureCollection` (i.e., when a
          not None value for `stride` passed to this method).
  segment_start_idxs: Union[list, np.ndarray, pd.Series, pd.Index], optional
      The start indices of the segments. If None, the start indices will be
      computed from the data using either:
  
      - the `segment_end_idxs` - the `window` size property of the
          `FeatureDescriptor` in this `FeatureCollection` (if `segment_end_idxs`
          is not None)
      - strided-window rolling on the data using `window` and `stride` of the
          `FeatureDescriptor` in this `FeatureCollection` (if `segment_end_idxs`
           is also None). (Note that the `stride` argument of this method takes
           precedence over the `stride` property of the `FeatureDescriptor`s).
      By default None.
  segment_end_idxs: Union[list, np.ndarray, pd.Series, pd.Index], optional
      The end indices for the segmented windows. If None, the end indices will be
      computed from the data using either:
  
      - the `segment_start_idxs` + the `window` size property of the
          `FeatureDescriptor` in this `FeatureCollection` (if `segment_start_idxs`
          is not None)
      - strided-window rolling on the data using `window` and `stride` of the
          `FeatureDescriptor` in this `FeatureCollection` (if `segment_start_idxs`
           is also None). (Note that the `stride` argument of this method takes
           precedence over the `stride` property of the `FeatureDescriptor`s).
      By default None.
  
      ..Note::
          When passing both `segment_start_idxs` and `segment_end_idxs`, these two
          arguments must have the same length and every start index must be <=
          than the corresponding end index.
          Note that passing both arguments, discards any meaning of the `window`
          and `stride` values (as these segment indices define the segmented data,
          and thus no strided-window rolling index calculation has to be executed).
          As such, the user can create variable-length segmented windows. However,
          in such cases, the user should be weary that the feature functions are
          invariant to these (potentially variable-length) windows.
  return_df : bool, optional
      Whether the output needs to be a DataFrame or a list thereof, by default
      False. If `True` the output dataframes will be merged to a DataFrame with an
      outer merge.
  window_idx : str, optional
      The window's index position which will be used as index for the
      feature_window aggregation. Must be either of: `["begin", "middle", "end"]`.
      by **default "end"**. All features in this collection will use the same
      window_idx.
  
      ..Note::
          `window_idx`="end" uses the window's end (= right open bound) as
          output index. 
  
          `window_idx`="begin" uses the window's start idx (= left closed bound)
          as output index.
  include_final_window : bool, optional
      Whether the final (possibly incomplete) window should be included in the
      strided-window segmentation, by default False.
  
      .. Note::
          The remarks below apply when `include_final_window` is set to True.
          The user should be aware that the last window *might* be incomplete,
          i.e.;
  
          - when equally sampled, the last window *might* be smaller than the
            the other windows.
          - when not equally sampled, the last window *might* not include all the
            data points (as the begin-time + window-size comes after the last data
            point).
  
          Note, that when equally sampled, the last window *will* be a full window
          when:
  
          - the stride is the sampling rate of the data (or stride = 1 for
          sample-based configurations).<br>
          **Remark**: that when `include_final_window` is set to False, the last
          window (which is a full) window will not be included!
          - *(len * sampling_rate - window_size) % stride = 0*. Remark that the
            above case is a base case of this.
  bound_method: str, optional
      The start-end bound methodology which is used to generate the slice ranges
      when ``data`` consists of multiple series / columns.
      Must be either of: `["inner", "inner-outer", "outer"]`, by default "inner".
  
      * if ``inner``, the inner-bounds of the series are returned.
      * if ``inner-outer``, the left-inner and right-outer bounds of the series
        are returned.
      * if ``outer``, the outer-bounds of the series are returned.
  approve_sparsity: bool, optional
      Bool indicating whether the user acknowledges that there may be sparsity
      (i.e., irregularly sampled data), by default False.
      If False and sparsity is observed, a warning is raised.
  show_progress: bool, optional
      If True, the progress will be shown with a progressbar, by default False.
  logging_file_path : Union[str, Path], optional
      The file path where the logged messages are stored. If `None`, then no
      logging `FileHandler` will be used and the logging messages are only pushed
      to stdout. Otherwise, a logging `FileHandler` will write the logged messages
      to the given file path. See also the `tsflex.features.logger` module.
  n_jobs : int, optional
      The number of processes used for the feature calculation. If `None`, then
      the number returned by _os.cpu_count()_ is used, by default None. 
  
      If n_jobs is either 0 or 1, the code will be executed sequentially without
      creating a process pool. This is very useful when debugging, as the stack
      trace will be more comprehensible.
      .. note::
          Multiprocessed execution is not supported on Windows. Even when,
          `n_jobs` is set > 1, the feature extraction will still be executed
          sequentially.
          Why do we not support multiprocessing on Windows; see this issue
          https://github.com/predict-idlab/tsflex/issues/51
  
      .. tip::
          It takes on avg. _300ms_ to schedule everything with
          multiprocessing. So if your sequential feature extraction code runs
          faster than ~1s, it might not be worth it to parallelize the process
          (and thus better leave `n_jobs` to 0 or 1).
  
  Returns
  -------
  Union[List[pd.DataFrame], pd.DataFrame]
      The calculated features.
  
  Raises
  ------
  KeyError
      Raised when a required key is not found in `data`.
  
  Notes
  ------
  * The (column-)names of the series in `data` represent the `series_names`.
  * If a `logging_file_path` is provided, the execution (time) info can be
    retrieved by calling `logger.get_feature_logs(logging_file_path)`.
    Be aware that the `logging_file_path` gets cleared before the logger pushes
    logged messages. Hence, one should use a separate logging file for each
    constructed processing and feature instance with this library.
docstr_tok: ["Calculate","features","on","the","passed","data",".","Parameters","--","--","--","--","--","data",":","Union","[","pd.Series",",","pd.DataFrame",",","List","[","Union","[","pd.Series",",","pd.DataFrame","]","]","]","Dataframe","or","Series","or","list","thereof",",","with","all","the","required","data","for","the","feature","calculation",".","*","*","Assumptions","*","*",":","*","each","Series","\/","DataFrame","must","have","a","sortable","index",".","This","index","represents","the","sequence","position","of","the","corresponding","values",",","the","index","can","be","either","numeric","or","a","``","pd.DatetimeIndex","``",".","*","each","Series","\/","DataFrame","index","must","be","comparable","with","all","others","*","we","assume","that","each","series-name","\/","dataframe-column-name","is","unique",".","stride",":","Union","[","float",",","str",",","pd.Timedelta",",","List","[","Union","[","float",",","str",",","pd.Timedelta","]",",","None","]",",","optional","The","stride","size",".","By","default","None",".","This","argument","supports","multiple","types",":","*","If","None",",","the","stride","of","the","`","FeatureDescriptor","`","objects","will","be","used",".","*","If","the","type","is","an","`","float","`","or","an","`","int","`",",","its","value","represents","the","series",":","-","its","stride","*","*","range","*","*","when","a","*","*","non","time-indexed","*","*","series","is","passed",".","-","the","stride","in","*","*","number","of","samples","*","*",",","when","a","*","*","time-indexed","*","*","series","is","passed","(","must","then","be","and","`","int","`",")","*","If","the","stride","'s","type","is","a","`","pd.Timedelta","`",",","the","stride","size","represents","the","stride-time","delta",".","The","passed","data","*","*","must","have","a","time-index","*","*",".","*","If","a","`","str","`",",","it","must","represent","a","stride-time-delta-string",".","Hence",",","the","*","*","passed","data","must","have","a","time-index","*","*",".","..","Note",":",":","When","set",",","this","stride","argument","takes","precedence","over","the","stride","property","of","the","`","FeatureDescriptor","`","s","in","this","`","FeatureCollection","`","(","i.e.",",","when","a","not","None","value","for","`","stride","`","passed","to","this","method",")",".","segment_start_idxs",":","Union","[","list",",","np.ndarray",",","pd.Series",",","pd.Index","]",",","optional","The","start","indices","of","the","segments",".","If","None",",","the","start","indices","will","be","computed","from","the","data","using","either",":","-","the","`","segment_end_idxs","`","-","the","`","window","`","size","property","of","the","`","FeatureDescriptor","`","in","this","`","FeatureCollection","`","(","if","`","segment_end_idxs","`","is","not","None",")","-","strided-window","rolling","on","the","data","using","`","window","`","and","`","stride","`","of","the","`","FeatureDescriptor","`","in","this","`","FeatureCollection","`","(","if","`","segment_end_idxs","`","is","also","None",")",".","(","Note","that","the","`","stride","`","argument","of","this","method","takes","precedence","over","the","`","stride","`","property","of","the","`","FeatureDescriptor","`","s",")",".","By","default","None",".","segment_end_idxs",":","Union","[","list",",","np.ndarray",",","pd.Series",",","pd.Index","]",",","optional","The","end","indices","for","the","segmented","windows",".","If","None",",","the","end","indices","will","be","computed","from","the","data","using","either",":","-","the","`","segment_start_idxs","`","+","the","`","window","`","size","property","of","the","`","FeatureDescriptor","`","in","this","`","FeatureCollection","`","(","if","`","segment_start_idxs","`","is","not","None",")","-","strided-window","rolling","on","the","data","using","`","window","`","and","`","stride","`","of","the","`","FeatureDescriptor","`","in","this","`","FeatureCollection","`","(","if","`","segment_start_idxs","`","is","also","None",")",".","(","Note","that","the","`","stride","`","argument","of","this","method","takes","precedence","over","the","`","stride","`","property","of","the","`","FeatureDescriptor","`","s",")",".","By","default","None",".","..","Note",":",":","When","passing","both","`","segment_start_idxs","`","and","`","segment_end_idxs","`",",","these","two","arguments","must","have","the","same","length","and","every","start","index","must","be","<","=","than","the","corresponding","end","index",".","Note","that","passing","both","arguments",",","discards","any","meaning","of","the","`","window","`","and","`","stride","`","values","(","as","these","segment","indices","define","the","segmented","data",",","and","thus","no","strided-window","rolling","index","calculation","has","to","be","executed",")",".","As","such",",","the","user","can","create","variable-length","segmented","windows",".","However",",","in","such","cases",",","the","user","should","be","weary","that","the","feature","functions","are","invariant","to","these","(","potentially","variable-length",")","windows",".","return_df",":","bool",",","optional","Whether","the","output","needs","to","be","a","DataFrame","or","a","list","thereof",",","by","default","False",".","If","`","True","`","the","output","dataframes","will","be","merged","to","a","DataFrame","with","an","outer","merge",".","window_idx",":","str",",","optional","The","window","'s","index","position","which","will","be","used","as","index","for","the","feature_window","aggregation",".","Must","be","either","of",":","`","[","``","begin","''",",","``","middle","''",",","``","end","''","]","`",".","by","*","*","default","``","end","''","*","*",".","All","features","in","this","collection","will","use","the","same","window_idx",".","..","Note",":",":","`","window_idx","`","=","''","end","''","uses","the","window","'s","end","(","=","right","open","bound",")","as","output","index",".","`","window_idx","`","=","''","begin","''","uses","the","window","'s","start","idx","(","=","left","closed","bound",")","as","output","index",".","include_final_window",":","bool",",","optional","Whether","the","final","(","possibly","incomplete",")","window","should","be","included","in","the","strided-window","segmentation",",","by","default","False",".","..","Note",":",":","The","remarks","below","apply","when","`","include_final_window","`","is","set","to","True",".","The","user","should","be","aware","that","the","last","window","*","might","*","be","incomplete",",","i.e",".",";","-","when","equally","sampled",",","the","last","window","*","might","*","be","smaller","than","the","the","other","windows",".","-","when","not","equally","sampled",",","the","last","window","*","might","*","not","include","all","the","data","points","(","as","the","begin-time","+","window-size","comes","after","the","last","data","point",")",".","Note",",","that","when","equally","sampled",",","the","last","window","*","will","*","be","a","full","window","when",":","-","the","stride","is","the","sampling","rate","of","the","data","(","or","stride","=","1","for","sample-based","configurations",")",".","<","br",">","*","*","Remark","*","*",":","that","when","`","include_final_window","`","is","set","to","False",",","the","last","window","(","which","is","a","full",")","window","will","not","be","included","!","-","*","(","len","*","sampling_rate","-","window_size",")","%","stride","=","0","*",".","Remark","that","the","above","case","is","a","base","case","of","this",".","bound_method",":","str",",","optional","The","start-end","bound","methodology","which","is","used","to","generate","the","slice","ranges","when","``","data","``","consists","of","multiple","series","\/","columns",".","Must","be","either","of",":","`","[","``","inner","''",",","``","inner-outer","''",",","``","outer","''","]","`",",","by","default","``","inner","''",".","*","if","``","inner","``",",","the","inner-bounds","of","the","series","are","returned",".","*","if","``","inner-outer","``",",","the","left-inner","and","right-outer","bounds","of","the","series","are","returned",".","*","if","``","outer","``",",","the","outer-bounds","of","the","series","are","returned",".","approve_sparsity",":","bool",",","optional","Bool","indicating","whether","the","user","acknowledges","that","there","may","be","sparsity","(","i.e.",",","irregularly","sampled","data",")",",","by","default","False",".","If","False","and","sparsity","is","observed",",","a","warning","is","raised",".","show_progress",":","bool",",","optional","If","True",",","the","progress","will","be","shown","with","a","progressbar",",","by","default","False",".","logging_file_path",":","Union","[","str",",","Path","]",",","optional","The","file","path","where","the","logged","messages","are","stored",".","If","`","None","`",",","then","no","logging","`","FileHandler","`","will","be","used","and","the","logging","messages","are","only","pushed","to","stdout",".","Otherwise",",","a","logging","`","FileHandler","`","will","write","the","logged","messages","to","the","given","file","path",".","See","also","the","`","tsflex.features.logger","`","module",".","n_jobs",":","int",",","optional","The","number","of","processes","used","for","the","feature","calculation",".","If","`","None","`",",","then","the","number","returned","by","_os.cpu_count","(",")","_","is","used",",","by","default","None",".","If","n_jobs","is","either","0","or","1",",","the","code","will","be","executed","sequentially","without","creating","a","process","pool",".","This","is","very","useful","when","debugging",",","as","the","stack","trace","will","be","more","comprehensible",".","..","note",":",":","Multiprocessed","execution","is","not","supported","on","Windows",".","Even","when",",","`","n_jobs","`","is","set",">","1",",","the","feature","extraction","will","still","be","executed","sequentially",".","Why","do","we","not","support","multiprocessing","on","Windows",";","see","this","issue","https",":","\/\/github.com\/predict-idlab\/tsflex\/issues\/51","..","tip",":",":","It","takes","on","avg",".","_300ms_","to","schedule","everything","with","multiprocessing",".","So","if","your","sequential","feature","extraction","code","runs","faster","than","~1s",",","it","might","not","be","worth","it","to","parallelize","the","process","(","and","thus","better","leave","`","n_jobs","`","to","0","or","1",")",".","Returns","--","--","--","-","Union","[","List","[","pd.DataFrame","]",",","pd.DataFrame","]","The","calculated","features",".","Raises","--","--","--","KeyError","Raised","when","a","required","key","is","not","found","in","`","data","`",".","Notes","--","--","--","*","The","(","column-",")","names","of","the","series","in","`","data","`","represent","the","`","series_names","`",".","*","If","a","`","logging_file_path","`","is","provided",",","the","execution","(","time",")","info","can","be","retrieved","by","calling","`","logger.get_feature_logs","(","logging_file_path",")","`",".","Be","aware","that","the","`","logging_file_path","`","gets","cleared","before","the","logger","pushes","logged","messages",".","Hence",",","one","should","use","a","separate","logging","file","for","each","constructed","processing","and","feature","instance","with","this","library","."]
code: def calculate(
      self,
      data: Union[
          pd.Series, pd.DataFrame, List[Union[pd.Series, pd.DataFrame]]
      ],
      stride: Optional[
          Union[float, str, pd.Timedelta, List, None]
      ] = None,
      segment_start_idxs: Optional[
          Union[list, np.ndarray, pd.Series, pd.Index]
      ] = None,
      segment_end_idxs: Optional[
          Union[list, np.ndarray, pd.Series, pd.Index]
      ] = None,
      return_df: Optional[bool] = False,
      window_idx: Optional[str] = "end",
      include_final_window: Optional[bool] = False,
      bound_method: Optional[str] = "inner",
      approve_sparsity: Optional[bool] = False,
      show_progress: Optional[bool] = False,
      logging_file_path: Optional[Union[str, Path]] = None,
      n_jobs: Optional[int] = None,
  ) -> Union[List[pd.DataFrame], pd.DataFrame]:
      """Calculate features on the passed data.
  
      Parameters
      ----------
      data : Union[pd.Series, pd.DataFrame, List[Union[pd.Series, pd.DataFrame]]]
          Dataframe or Series or list thereof, with all the required data for the
          feature calculation. \n
          **Assumptions**: \n
          * each Series / DataFrame must have a sortable index. This index represents
          the sequence position of the corresponding values, the index can be either
          numeric or a ``pd.DatetimeIndex``.
          * each Series / DataFrame index must be comparable with all others
          * we assume that each series-name / dataframe-column-name is unique.
      stride: Union[float, str, pd.Timedelta, List[Union[float, str, pd.Timedelta], None], optional
          The stride size. By default None. This argument supports multiple types: \n
          * If None, the stride of the `FeatureDescriptor` objects will be used.
          * If the type is an `float` or an `int`, its value represents the series:\n
              - its stride **range** when a **non time-indexed** series is passed.
              - the stride in **number of samples**, when a **time-indexed** series
              is passed (must then be and `int`)
          * If the stride's type is a `pd.Timedelta`, the stride size represents
          the stride-time delta. The passed data **must have a time-index**.
          * If a `str`, it must represent a stride-time-delta-string. Hence, the
          **passed data must have a time-index**. \n
          .. Note::
              When set, this stride argument takes precedence over the stride property
              of the `FeatureDescriptor`s in this `FeatureCollection` (i.e., when a
              not None value for `stride` passed to this method).
      segment_start_idxs: Union[list, np.ndarray, pd.Series, pd.Index], optional
          The start indices of the segments. If None, the start indices will be
          computed from the data using either:\n
          - the `segment_end_idxs` - the `window` size property of the
              `FeatureDescriptor` in this `FeatureCollection` (if `segment_end_idxs`
              is not None)
          - strided-window rolling on the data using `window` and `stride` of the
              `FeatureDescriptor` in this `FeatureCollection` (if `segment_end_idxs`
               is also None). (Note that the `stride` argument of this method takes
               precedence over the `stride` property of the `FeatureDescriptor`s).
          By default None.
      segment_end_idxs: Union[list, np.ndarray, pd.Series, pd.Index], optional
          The end indices for the segmented windows. If None, the end indices will be
          computed from the data using either:\n
          - the `segment_start_idxs` + the `window` size property of the
              `FeatureDescriptor` in this `FeatureCollection` (if `segment_start_idxs`
              is not None)
          - strided-window rolling on the data using `window` and `stride` of the
              `FeatureDescriptor` in this `FeatureCollection` (if `segment_start_idxs`
               is also None). (Note that the `stride` argument of this method takes
               precedence over the `stride` property of the `FeatureDescriptor`s).
          By default None.
  
          ..Note::
              When passing both `segment_start_idxs` and `segment_end_idxs`, these two
              arguments must have the same length and every start index must be <=
              than the corresponding end index.
              Note that passing both arguments, discards any meaning of the `window`
              and `stride` values (as these segment indices define the segmented data,
              and thus no strided-window rolling index calculation has to be executed).
              As such, the user can create variable-length segmented windows. However,
              in such cases, the user should be weary that the feature functions are
              invariant to these (potentially variable-length) windows.
      return_df : bool, optional
          Whether the output needs to be a DataFrame or a list thereof, by default
          False. If `True` the output dataframes will be merged to a DataFrame with an
          outer merge.
      window_idx : str, optional
          The window's index position which will be used as index for the
          feature_window aggregation. Must be either of: `["begin", "middle", "end"]`.
          by **default "end"**. All features in this collection will use the same
          window_idx.
  
          ..Note::
              `window_idx`="end" uses the window's end (= right open bound) as
              output index. \n
              `window_idx`="begin" uses the window's start idx (= left closed bound)
              as output index.
      include_final_window : bool, optional
          Whether the final (possibly incomplete) window should be included in the
          strided-window segmentation, by default False.
  
          .. Note::
              The remarks below apply when `include_final_window` is set to True.
              The user should be aware that the last window *might* be incomplete,
              i.e.;
  
              - when equally sampled, the last window *might* be smaller than the
                the other windows.
              - when not equally sampled, the last window *might* not include all the
                data points (as the begin-time + window-size comes after the last data
                point).
  
              Note, that when equally sampled, the last window *will* be a full window
              when:
  
              - the stride is the sampling rate of the data (or stride = 1 for
              sample-based configurations).<br>
              **Remark**: that when `include_final_window` is set to False, the last
              window (which is a full) window will not be included!
              - *(len * sampling_rate - window_size) % stride = 0*. Remark that the
                above case is a base case of this.
      bound_method: str, optional
          The start-end bound methodology which is used to generate the slice ranges
          when ``data`` consists of multiple series / columns.
          Must be either of: `["inner", "inner-outer", "outer"]`, by default "inner".
  
          * if ``inner``, the inner-bounds of the series are returned.
          * if ``inner-outer``, the left-inner and right-outer bounds of the series
            are returned.
          * if ``outer``, the outer-bounds of the series are returned.
      approve_sparsity: bool, optional
          Bool indicating whether the user acknowledges that there may be sparsity
          (i.e., irregularly sampled data), by default False.
          If False and sparsity is observed, a warning is raised.
      show_progress: bool, optional
          If True, the progress will be shown with a progressbar, by default False.
      logging_file_path : Union[str, Path], optional
          The file path where the logged messages are stored. If `None`, then no
          logging `FileHandler` will be used and the logging messages are only pushed
          to stdout. Otherwise, a logging `FileHandler` will write the logged messages
          to the given file path. See also the `tsflex.features.logger` module.
      n_jobs : int, optional
          The number of processes used for the feature calculation. If `None`, then
          the number returned by _os.cpu_count()_ is used, by default None. \n
          If n_jobs is either 0 or 1, the code will be executed sequentially without
          creating a process pool. This is very useful when debugging, as the stack
          trace will be more comprehensible.
          .. note::
              Multiprocessed execution is not supported on Windows. Even when,
              `n_jobs` is set > 1, the feature extraction will still be executed
              sequentially.
              Why do we not support multiprocessing on Windows; see this issue
              https://github.com/predict-idlab/tsflex/issues/51
  
          .. tip::
              It takes on avg. _300ms_ to schedule everything with
              multiprocessing. So if your sequential feature extraction code runs
              faster than ~1s, it might not be worth it to parallelize the process
              (and thus better leave `n_jobs` to 0 or 1).
  
      Returns
      -------
      Union[List[pd.DataFrame], pd.DataFrame]
          The calculated features.
  
      Raises
      ------
      KeyError
          Raised when a required key is not found in `data`.
  
      Notes
      ------
      * The (column-)names of the series in `data` represent the `series_names`.
      * If a `logging_file_path` is provided, the execution (time) info can be
        retrieved by calling `logger.get_feature_logs(logging_file_path)`.
        Be aware that the `logging_file_path` gets cleared before the logger pushes
        logged messages. Hence, one should use a separate logging file for each
        constructed processing and feature instance with this library.
  
  
      """
      # Delete other logging handlers
      delete_logging_handlers(logger)
      # Add logging handler (if path provided)
      if logging_file_path:
          f_handler = add_logging_handler(logger, logging_file_path)
  
      # Convert to numpy array (if necessary)
      if segment_start_idxs is not None:
          segment_start_idxs = FeatureCollection._process_segment_idxs(
              segment_start_idxs
          )
      if segment_end_idxs is not None:
          segment_end_idxs = FeatureCollection._process_segment_idxs(
              segment_end_idxs
          )
  
      if (
          segment_start_idxs is not None
          and segment_end_idxs is not None
      ):
          # Check if segment indices have same length and whether every start idx
          # <= end idx
          _check_start_end_array(segment_start_idxs, segment_end_idxs)
          # Check if there is either 1 or No(ne) window value for every output name -
          # input_series combination
          self._check_no_multiple_windows()
  
      if segment_start_idxs is None or segment_end_idxs is None:
          assert all(
              fd.window is not None
              for fd in flatten(self._feature_desc_dict.values())
          ), (
              "Each feature descriptor must have a window when not both "
              + "segment_start_idxs and segment_end_idxs are provided"
          )
  
      if (
          stride is None
          and segment_start_idxs is None
          and segment_end_idxs is None
      ):
          assert all(
              fd.stride is not None
              for fd in flatten(self._feature_desc_dict.values())
          ), (
              "Each feature descriptor must have a stride when no stride or "
              + "segment indices are passed to this method!"
          )
      elif stride is not None and (
          segment_start_idxs is not None or segment_end_idxs is not None
      ):
          raise ValueError(
              "The stride and any segment index argument cannot be set together! "
              + "At least one of both should be None."
          )
  
      if stride is not None:
          # Verify whether the stride complies with the input data dtype
          stride = [
              parse_time_arg(s) if isinstance(s, str) else s
              for s in to_list(stride)
          ]
          self._check_feature_descriptors(
              skip_none=False, calc_stride=stride
          )
  
      # Convert the data to a series_dict
      series_dict: Dict[str, pd.Series] = {}
      for s in to_series_list(data):
          if not s.index.is_monotonic_increasing:
              warnings.warn(
                  f"The index of series '{s.name}' is not monotonic increasing. "
                  + "The series will be sorted by the index.",
                  RuntimeWarning,
              )
              s = s.sort_index(
                  ascending=True, inplace=False, ignore_index=False
              )
  
          # Assert the assumptions we make!
          assert s.index.is_monotonic_increasing
  
          if s.name in self.get_required_series():
              series_dict[str(s.name)] = s
  
      # Determine the bounds of the series dict items and slice on them
      # TODO: is dit wel nodig `hier? want we doen dat ook in de strided rolling
      start, end = _determine_bounds(
          bound_method, list(series_dict.values())
      )
      series_dict = {
          n: s.loc[
              s.index.dtype.type(start) : s.index.dtype.type(end)
          ]  # TODO: check memory efficiency of ths
          for n, s, in series_dict.items()
      }
  
      # Note: this variable has a global scope so this is shared in multiprocessing
      # TODO: try to make this more efficient (but is not really the bottleneck)
      global get_stroll_func
      get_stroll_func = self._stroll_feat_generator(
          series_dict,
          calc_stride=stride,
          segment_start_idxs=segment_start_idxs,
          segment_end_idxs=segment_end_idxs,
          start_idx=start,
          end_idx=end,
          window_idx=window_idx,
          include_final_window=include_final_window,
          approve_sparsity=approve_sparsity,
      )
      nb_stroll_funcs = self._get_stroll_feat_length()
  
      if (
          os.name == "nt"
      ):  # On Windows no multiprocessing is supported, see https://github.com/predict-idlab/tsflex/issues/51
          n_jobs = 1
      elif n_jobs is None:
          n_jobs = os.cpu_count()
      n_jobs = min(n_jobs, nb_stroll_funcs)
  
      calculated_feature_list = None
      if n_jobs in [0, 1]:
          idxs = range(nb_stroll_funcs)
          if show_progress:
              idxs = tqdm(idxs)
          try:
              calculated_feature_list = [
                  self._executor(idx) for idx in idxs
              ]
          except Exception:
              traceback.print_exc()
      else:
          with Pool(processes=n_jobs) as pool:
              results = pool.imap_unordered(
                  self._executor, range(nb_stroll_funcs)
              )
              if show_progress:
                  results = tqdm(results, total=nb_stroll_funcs)
              try:
                  calculated_feature_list = [f for f in results]
              except Exception:
                  traceback.print_exc()
                  pool.terminate()
              finally:
                  # Close & join because: https://github.com/uqfoundation/pathos/issues/131
                  pool.close()
                  pool.join()
  
      # Close the file handler (this avoids PermissionError: [WinError 32])
      if logging_file_path:
          f_handler.close()
          logger.removeHandler(f_handler)
  
      if calculated_feature_list is None:
          raise RuntimeError(
              "Feature Extraction halted due to error while extracting one "
              + "(or multiple) feature(s)! See stack trace above."
          )
  
      if return_df:
          # concatenate & sort the columns
          df = pd.concat(
              calculated_feature_list, axis=1, join="outer", copy=False
          )
          return df.reindex(sorted(df.columns), axis=1)
      else:
          return calculated_feature_list
  
imports: from __future__ import annotations
  import warnings
  import os
  import traceback
  import uuid
  from copy import deepcopy
  from pathlib import Path
  from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, Union
  import dill
  import numpy
  import pandas
  from multiprocess import Pool
  from tqdm.auto import tqdm
  from ..features.function_wrapper import FuncWrapper
  from ..utils.attribute_parsing import AttributeParser
  from ..utils.data import flatten, to_list, to_series_list
  from ..utils.logging import add_logging_handler, delete_logging_handlers
  from ..utils.time import parse_time_arg, timedelta_to_str
  from .feature import FeatureDescriptor, MultipleFeatureDescriptors
  from .logger import logger
  from .segmenter import StridedRolling, StridedRollingFactory
  from .utils import _check_start_end_array, _determine_bounds
label_desc: Use image node_id 14 for calling the FeatureCollection obj's underlying member method code with example usage: obj.calculate(data, stride, segment_start_idxs, segment_end_idxs, return_df, window_idx, include_final_window, bound_method, approve_sparsity, show_progress, logging_file_path, n_jobs) and returns: df, calculated_feature_list