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

        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
        
darts/dataprocessing/transformers/midas.py