docstr: Transforms series from high to low frequency using a mixed-data sampling approach. Uses and relies on
  pandas.DataFrame.resample.
  
  When converting to/from anchorable offset [1]_, the index is rolled backward if the series does not start on
  the anchor date to preserve all the values.
  
  Steps:
      (1) Transform series to pd.DataFrame and get frequency string for PeriodIndex
      (2) Downsample series and then upsample it again
      (3) Replace input series by unsampled series if it's not 'full'
      (4) Transform every column of the high frequency series into multiple columns for the low frequency series
      (5) Transform the low frequency series back into a TimeSeries
docstr_tok: ["Transforms","series","from","high","to","low","frequency","using","a","mixed-data","sampling","approach",".","Uses","and","relies","on","pandas.DataFrame.resample",".","When","converting","to\/from","anchorable","offset","[","1","]","_",",","the","index","is","rolled","backward","if","the","series","does","not","start","on","the","anchor","date","to","preserve","all","the","values",".","Steps",":","(","1",")","Transform","series","to","pd.DataFrame","and","get","frequency","string","for","PeriodIndex","(","2",")","Downsample","series","and","then","upsample","it","again","(","3",")","Replace","input","series","by","unsampled","series","if","it","'s","not","'full'","(","4",")","Transform","every","column","of","the","high","frequency","series","into","multiple","columns","for","the","low","frequency","series","(","5",")","Transform","the","low","frequency","series","back","into","a","TimeSeries"]
code: def ts_transform(
      series: TimeSeries, params: Mapping[str, Any]
  ) -> TimeSeries:
      """
      Transforms series from high to low frequency using a mixed-data sampling approach. Uses and relies on
      pandas.DataFrame.resample.
  
      When converting to/from anchorable offset [1]_, the index is rolled backward if the series does not start on
      the anchor date to preserve all the values.
  
      Steps:
          (1) Transform series to pd.DataFrame and get frequency string for PeriodIndex
          (2) Downsample series and then upsample it again
          (3) Replace input series by unsampled series if it's not 'full'
          (4) Transform every column of the high frequency series into multiple columns for the low frequency series
          (5) Transform the low frequency series back into a TimeSeries
      """
      low_freq = params["fixed"]["_low_freq"]
      strip = params["fixed"]["_strip"]
      drop_static_covariates = params["fixed"][
          "_drop_static_covariates"
      ]
      feature_sep = params["fixed"]["_sep"]
      high_freq = params["fitted"]["high_freq"]
      MIDAS._verify_series(series, high_freq=high_freq)
  
      # TimeSeries to pd.DataFrame
      df = pd.DataFrame(index=series.time_index)
  
      # get high frequency string that's suitable for PeriodIndex
      high_freq_period = pd.tseries.frequencies.get_period_alias(
          series.freq_str
      )
  
      # downsample
      resampled = df.resample(low_freq)
      low_freq_df = resampled.last()
  
      def up_sample(low_df: pd.DataFrame, high_period):
          """up sample a single index DataFrame to a higher frequency"""
          low_df = low_df.copy(deep=True)
          low_df.index = low_df.index.to_period()
          return low_df.resample(rule=high_period).last()
  
      # first and last groups can be shorter than an entire lower freq period
      # we up_sample them from the low to high frequency to get the expected number
      # higher freq time steps in one lower freq
      first_up_sampled = up_sample(
          low_freq_df.iloc[:1], high_freq_period
      )
      last_up_sampled = up_sample(
          low_freq_df.iloc[-1:], high_freq_period
      )
  
      # find unique sizes from: first group size + unique sizes of center groups + last group size
      sizes = np.unique(
          [len(first_up_sampled)]
          + resampled.size()[1:-1].unique().tolist()
          + [len(last_up_sampled)]
      )
  
      # MIDAS requires the high freq to be a round multiple of the low freq -> sizes must be identical
      if not len(sizes) == 1:
          raise_log(
              ValueError(
                  "The frequency of the input series should be an exact multiple of the targeted "
                  f"lower frequency output. Received series frequency `{high_freq}`, and lower frequency "
                  f"{low_freq}. E.g., a valid conversion would be from a monthly (high) to a quarterly "
                  f"(low) frequency."
              ),
              logger=logger,
          )
  
      # max size is the number of higher frequency time steps per lower frequency period
      max_size = sizes[0]
  
      n_samples = series.n_samples
      n_cols_in = series.n_components
      n_cols_out = max_size * n_cols_in
      series_size = len(series)
  
      # how many input time steps are in each down-sampled lower frequency period
      group_sizes = resampled.size()
      n_groups = len(group_sizes)
  
      arr = series.all_values(copy=False)
      time_index = low_freq_df.index
  
      if series_size <= max_size:
          # we can't apply windowing when series is shorter than `max_size`
          # we have at least one group, maximum two
          first_idx = first_up_sampled.index.get_loc(df.index[0])
          last_idx = first_idx + series_size - 1
  
          start_chunk = np.empty((first_idx, 1, 1))
          start_chunk.fill(np.nan)
          end_chunk = np.empty((max_size - 1 - last_idx, 1, 1))
          end_chunk.fill(np.nan)
          arr = np.concatenate([start_chunk, arr, end_chunk])
          # arr has shape (n time steps, n components, n samples)
          # reshape to (1 time step, n midas components, n samples)
          arr = arr.reshape(1, n_cols_out, n_samples)
  
          if strip:
              # results in an empty series
              arr = arr[0:0]
              time_index = time_index[0:0]
      else:
          # guarantee that we have at least two groups since series is longer than `max_size`
          # extract rows from higher frequency and convert them to columns in the lower frequency
          # we can achieve this by extracting all windows with a size of `max_size`;
          # later on we stride to get only the relevant windows each `max_size` steps
  
          # create maximum possible output array
          arr_out = np.empty((n_groups, n_cols_out, n_samples))
          arr_out.fill(np.nan)
  
          arr = np.lib.stride_tricks.sliding_window_view(
              arr, window_shape=(max_size, n_cols_in, n_samples)
          )
          arr = arr.reshape((-1, n_cols_out, n_samples))
  
          # the first resampled index might not have all dates from higher freq
          size_group_first = group_sizes.iloc[0]
          size_group_first = (
              0 if size_group_first == max_size else size_group_first
          )
          components_group_first = size_group_first * n_cols_in
          if components_group_first and not strip:
              arr_out[
                  0, n_cols_out - components_group_first :, :
              ] = arr[0, :components_group_first]
          center_start_idx = 0 if not size_group_first else 1
  
          # the last resampled index might not have all dates from higher freq
          size_group_last = group_sizes.iloc[-1]
          size_group_last = (
              0 if size_group_last == max_size else size_group_last
          )
          components_group_last = size_group_last * n_cols_in
          if components_group_last and not strip:
              arr_out[-1, :components_group_last, :] = arr[
                  -1, -components_group_last:
              ]
  
          # get the center resampled indices
          center_end_idx = None if not size_group_last else -1
          arr_out[center_start_idx:center_end_idx, :, :] = arr[
              size_group_first::max_size
          ]
  
          # potentially strip first and last groups
          if strip:
              first_idx = None if not size_group_first else 1
              last_idx = None if not size_group_last else -1
              arr_out = arr_out[first_idx:last_idx]
              time_index = time_index[first_idx:last_idx]
  
          arr = arr_out
  
      ts = MIDAS._create_midas_df(
          series=series,
          arr=arr,
          time_index=time_index,
          n_midas=max_size,
          drop_static_covariates=drop_static_covariates,
          inverse_transform=False,
          feature_sep=feature_sep,
      )
      return ts
  
file_path: darts/darts/dataprocessing/transformers/midas.py
imports: from typing import Any, Dict, List, Mapping, Optional, Sequence, Union
  import numpy
  import pandas
  from darts import TimeSeries
  from darts.dataprocessing.transformers import FittableDataTransformer, InvertibleDataTransformer
  from darts.logging import get_logger, raise_log
  from darts.timeseries import _finite_rows_boundaries
  from darts.utils.timeseries_generation import generate_index
label_desc: Use image node_id 3 for calling the MIDAS obj's underlying member method code with example usage: obj.ts_transform(series, params) and returns: ts, low_df