code: def create_lagged_data(
      target_series: Optional[
          Union[TimeSeries, Sequence[TimeSeries]]
      ] = None,
      past_covariates: Optional[
          Union[TimeSeries, Sequence[TimeSeries]]
      ] = None,
      future_covariates: Optional[
          Union[TimeSeries, Sequence[TimeSeries]]
      ] = None,
      lags: Optional[Union[Sequence[int], Dict[str, List[int]]]] = None,
      lags_past_covariates: Optional[
          Union[Sequence[int], Dict[str, List[int]]]
      ] = None,
      lags_future_covariates: Optional[
          Union[Sequence[int], Dict[str, List[int]]]
      ] = None,
      output_chunk_length: int = 1,
      uses_static_covariates: bool = True,
      last_static_covariates_shape: Optional[Tuple[int, int]] = None,
      max_samples_per_ts: Optional[int] = None,
      multi_models: bool = True,
      check_inputs: bool = True,
      use_moving_windows: bool = True,
      is_training: bool = True,
      concatenate: bool = True,
  ) -> Tuple[
      ArrayOrArraySequence,
      Union[None, ArrayOrArraySequence],
      Sequence[pd.Index],
      Optional[Tuple[int, int]],
  ]:
      """
      Creates the features array `X` and labels array `y` to train a lagged-variables regression model (e.g. an
      `sklearn` model) when `is_training = True`; alternatively, creates the features array `X` to produce a series
      of prediction from an already-trained regression model when `is_training = False`. In both cases, a list of time
      indices corresponding to each generated observation is also returned.
  
      Notes
      -----
      Instead of calling `create_lagged_data` directly, it is instead recommended that:
          - `create_lagged_training_data` be called if one wishes to create the `X` and `y` arrays
          to train a regression model.
          - `create_lagged_prediction_data` be called if one wishes to create the `X` array required
          to generate a prediction from an already-trained regression model.
      This is because even though both of these functions are merely wrappers around `create_lagged_data`, their
      call signatures are more easily interpreted than `create_lagged_data`. For example,
      `create_lagged_prediction_data` does not accept `output_chunk_length` nor `multi_models` as inputs, since
      these inputs are not used when constructing prediction data. Similarly, `create_lagged_prediction_data`
      returns only `X` and `times` as outputs, as opposed to returning `y` as `None` along with `X` and `times`.
  
      The `X` array is constructed from the lagged values of up to three separate timeseries:
          1. The `target_series`, which contains the values we're trying to predict. A regression model that
          uses previous values of the target its predicting is referred to as *auto-regressive*; please refer to
          [1]_ for further details about auto-regressive timeseries models.
          2. The past covariates series, which contains values that are *not* known into the future. Unlike
          the target series, however, past covariates are *not* to be predicted by the regression model.
          3. The future covariates (AKA 'exogenous' covariates) series, which contains values that are known
          into the future, even beyond the data in `target_series` and `past_covariates`.
      See [2]_ for a more detailed discussion about target, past, and future covariates. Conversely, `y` is
      comprised only of the lagged values of `target_series`.
  
      The shape of `X` is:
          `X.shape = (n_observations, n_lagged_features, n_samples)`,
      where `n_observations` equals either the number of time points shared between all specified series,
      or `max_samples_per_ts`, whichever is smallest.
      The shape of `y` is:
          `y.shape = (n_observations, output_chunk_length, n_samples)`,
      if `multi_models = True`, otherwise:
          `y.shape = (n_observations, 1, n_samples)`.
  
      Along the `n_lagged_features` axis, `X` has the following structure (for `*_lags=[-2,-1]` and
      `*_series.n_components = 2`):
          lagged_target | lagged_past_covariates | lagged_future_covariates
      where each `lagged_*` has the following structure:
          lag_-2_comp_1_* | lag_-2_comp_2_* | lag_-1_comp_1_* | lag_-1_comp_2_*
  
      Along the `n_lagged_labels` axis, `y` has the following structure (for `output_chunk_length=4` and
      `target_series.n_components=2`):
          lag_+0_comp_1_target | lag_+0_comp_2_target | ... | lag_+3_comp_1_target | lag_+3_comp_2_target
  
      The `lags` and `lags_past_covariates` must contain only values less than or equal to -1. In other words, one
      cannot use the value of either of these series at time `t` to predict the value of the target series at the
      same time `t`; this is because the values of `target_series` and `past_covariates` at time `t` aren't available
      at prediction time, by definition. Conversely, since the values of `future_covariates` are known into the future,
      `lags_future_covariates` can contain negative, positive, and/or zero lag values (i.e. we *can* use the values of
      `future_covariates` at time `t` or beyond to predict the value of `target_series` at time `t`).
  
      The exact method used to construct `X` and `y` depends on whether all of the specified timeseries are
      of the same frequency or not:
          - If all specified timeseries are of the same frequency, `strided_moving_window` is used to extract
          contiguous time blocks from each timeseries; the lagged variables are then extracted from each window.
          - If all specified timeseries are *not* of the same frequency, then `find_shared_times` is first used
          to find those times common to all three timeseries, after which the lagged features are extracted by
          offsetting the time indices of these common times by the requested lags.
      In cases where it can be validly applied, the 'moving window' method is expected to be faster than the
      'intersecting time' method. However, in exceptional cases where only a small number of lags are being
      extracted, but the difference between the lag values is large (e.g. `lags = [-1, -1000]`), the 'moving
      window' method is expected to consume significantly more memory, since it extracts all of the series values
      between the maximum and minimum lags as 'windows', before actually extracting the specific requested lag values.
  
      In order for the lagged features of a series to be added to `X`, *both* that series and the corresponding lags
      must be specified; if a series is specified without the corresponding lags, that series will be ignored and not
      added to `X`. `X` and `y` arrays are constructed independently over the samples dimension (i.e. the second axis)
      of each series.
  
      If the provided series are stochastic (i.e. `series.n_components > 1`), then an `X` and `y` array will be
      constructed for each sample; the arrays corresponding to each sample are concatenated togather along the `2`nd
      axis of `X` and `y`. In other words, `create_lagged_data` is vectorised over the sample axis of the `target_series`,
      `past_covariates`, and `future_covariates` inputs. Importantly, if stochastic series are provided, each series must
      have the same number of samples, otherwise an error will be thrown.
  
      Each series input (i.e. `target_series`, `past_covariates`, and `future_covariates`) can be specified either as
      a single `TimeSeries`, or as a `Sequence` of `TimeSeries`; the specified series must all be of the same type,
      however (i.e. either all `TimeSeries` or all `Sequence[TimeSeries]`). If `Sequence[TimeSeries]` are specified,
      then a feature matrix `X` and labels array `y` will be constructed using the corresponding `TimeSeries` in
      each `Sequence` (i.e. the first `TimeSeries` in each `Sequence` are used to create an `X` and `y`, then
      the second `TimeSeries` in each `Sequence` are used to create an `X` and `y`, etc.). If `concatenate = True`,
      these `X`'s and `y`'s will be concatenated along the `0`th axis; otherwise, a list of `X` and `y` array will
      be returned. Note that `times` is always returned as a `Sequence[pd.Index]`, however, even when
      `concatenate = True`.
  
      Parameters
      ----------
      target_series
          Optionally, the series for the regression model to predict. Must be specified if `is_training = True`.
          Can be specified as either a `TimeSeries` or as a `Sequence[TimeSeries]`.
      output_chunk_length
          Optionally, the number of timesteps ahead into the future the regression model is to predict. Must
          best specified if `is_training = True`.
      past_covariates
          Optionally, the past covariates series that the regression model will use as inputs. Unlike the
          `target_series`, `past_covariates` are *not* to be predicted by the regression model. Can be
          specified as either a `TimeSeries` or as a `Sequence[TimeSeries]`.
      future_covariates
          Optionally, the future covariates (i.e. exogenous covariates) series that the regression model will
          use as inputs. Can be specified as either a `TimeSeries` or as a `Sequence[TimeSeries]`.
      lags
          Optionally, the lags of the target series to be used as (auto-regressive) features. If not specified,
          auto-regressive features will *not* be added to `X`. Each lag value is assumed to be negative (e.g.
          `lags = [-3, -1]` will extract `target_series` values which are 3 timesteps and 1 timestep away from
          the current value). If the lags are provided as a dictionary, the lags values are specific to each
          component in the target series.
      lags_past_covariates
          Optionally, the lags of `past_covariates` to be used as features. Like `lags`, each lag value is assumed to
          be less than or equal to -1. If the lags are provided as a dictionary, the lags values are specific to each
          component in the past covariates series.
      lags_future_covariates
          Optionally, the lags of `future_covariates` to be used as features. Unlike `lags` and
          `lags_past_covariates`, `lags_future_covariates` values can be positive (i.e. use values *after* time `t`
          to predict target at time `t`), zero (i.e. use values *at* time `t` to predict target at time `t`), and/or
          negative (i.e. use values *before* time `t` to predict target at time `t`). If the lags are provided as
          a dictionary, the lags values are specific to each component in the future covariates series.
      uses_static_covariates
          Whether the model uses/expects static covariates. If `True`, it enforces that static covariates must
          have identical shapes across all target series.
      last_static_covariates_shape
          Optionally, the last observed shape of the static covariates. This is ``None`` before fitting, or when
          `uses_static_covariates` is ``False``.
      max_samples_per_ts
          Optionally, the maximum number of samples to be drawn for training/validation; only the most recent
          samples are kept. In theory, specifying a smaller `max_samples_per_ts` should reduce computation time,
          especially in cases where many observations could be generated.
      multi_models
          Optionally, specifies whether the regression model predicts multiple timesteps into the future. If `True`,
          then the regression model is assumed to predict all of the timesteps from time `t` to `t+output_chunk_length`.
          If `False`, then the regression model is assumed to predict *only* the timestep at `t+output_chunk_length`.
          This input is ignored if `is_training = False`.
      check_inputs
          Optionally, specifies that the `lags_*` and `series_*` inputs should be checked for validity. Should be set
          to `False` if inputs have already been checked for validity (e.g. inside the `__init__` of a class), otherwise
          should be set to `True`.
      use_moving_windows
          Optionally, specifies that the 'moving window' method should be used to construct `X` and `y` if all of the
          provided series are of the same frequency. If `use_moving_windows = False`, the 'time intersection' method
          will always be used, even when all of the provided series are of the same frequency. In general, setting
          to `True` results in faster tabularization at the potential cost of higher memory usage. See Notes for further
          details.
      is_training
          Optionally, specifies whether the constructed lagged data are to be used for training a regression model
          (i.e. `is_training = True`), or for generating predictions from an already-trained regression model (i.e.
          `is_training = False`). If `is_training = True`, `target_series` and `output_chunk_length` must be specified,
          the `multi_models` input is utilised, and a label array `y` is returned. Conversely, if `is_training = False`,
          then `target_series` and `output_chunk_length` do not need to be specified, the `multi_models` input is ignored,
          and the returned `y` value is `None`.
      concatenate
          Optionally, specifies that `X` and `y` should both be returned as single `np.ndarray`s, instead of as
          a `Sequence[np.ndarray]`. If each series input is specified as a `Sequence[TimeSeries]` and
          `concatenate = False`, `X` and `y` will be lists whose `i`th element corresponds to the feature matrix or label
          array formed by the `i`th `TimeSeries` in each `Sequence[TimeSeries]` input. Conversely, if `concatenate = True`
          when `Sequence[TimeSeries]` are provided, then `X` and `y` will be arrays created by concatenating all of the
          feature/label arrays formed by each `TimeSeries` along the `0`th axis. Note that `times` is still returned as
          `Sequence[pd.Index]`, even when `concatenate = True`.
  
      Returns
      -------
      X
          The constructed features array(s), with shape `(n_observations, n_lagged_features, n_samples)`.
          If the series inputs were specified as `Sequence[TimeSeries]` and `concatenate = False`, then `X`
          is returned as a `Sequence[np.array]`; otherwise, `X` is returned as a single `np.array`.
      y
          The constructed labels array. If `multi_models = True`, then `y` is a
          `(n_observations, output_chunk_length, n_samples)`-shaped array; conversely, if
          `multi_models =  False`, then `y` is a `(n_observations, 1, n_samples)`-shaped array.
          If the series inputs were specified as `Sequence[TimeSeries]` and `concatenate = False`, then `y`
          is returned as a `Sequence[np.array]`; otherwise, `y` is returned as a single `np.array`.
      times
          The `time_index` of each observation in `X` and `y`, returned as a `Sequence` of `pd.Index`es.
          If the series inputs were specified as `Sequence[TimeSeries]`, then the `i`th list element
          gives the times of those observations formed using the `i`th `TimeSeries` object in each
          `Sequence`. Otherwise, if the series inputs were specified as `TimeSeries`, the only
          element is the times of those observations formed from the lone `TimeSeries` inputs.
      last_static_covariates_shape
          The last observed shape of the static covariates. This is ``None`` when `uses_static_covariates`
          is ``False``.
  
  
      Raises
      ------
      ValueError
          If the specified time series do not share any times for which features (and labels if `is_training = True`) can
          be constructed.
      ValueError
          If no lags are specified, or if any of the specified lag values are non-negative.
      ValueError
          If any of the series are too short to create features and/or labels for the requested lags and
          `output_chunk_length` values.
      ValueError
          If `target_series` and/or `output_chunk_length` are *not* specified when `is_training = True`.
      ValueError
          If the provided series do not share the same type of `time_index` (e.g. `target_series` uses a
          pd.RangeIndex, but `future_covariates` uses a `pd.DatetimeIndex`).
  
      References
      ----------
      .. [1] https://otexts.com/fpp2/AR.html#AR
      .. [2] https://unit8.com/resources/time-series-forecasting-using-past-and-future-external-data-with-darts/
  
      See Also
      --------
          tabularization.create_lagged_component_names : return the lagged features names as a list of strings.
  
      """
      raise_if(
          is_training and (target_series is None),
          "Must specify `target_series` if `is_training = True`.",
      )
  
      # ensure list of TimeSeries format
      target_series = series2seq(target_series)
      past_covariates = series2seq(past_covariates)
      future_covariates = series2seq(future_covariates)
      seq_ts_lens = [
          len(seq_ts)
          for seq_ts in (
              target_series,
              past_covariates,
              future_covariates,
          )
          if seq_ts is not None
      ]
      seq_ts_lens = set(seq_ts_lens)
      raise_if(
          len(seq_ts_lens) > 1,
          "Must specify the same number of `TimeSeries` for each series input.",
      )
      if max_samples_per_ts is None:
          max_samples_per_ts = inf
      X, y, times = [], [], []
      for i in range(max(seq_ts_lens)):
          target_i = target_series[i] if target_series else None
          past_i = past_covariates[i] if past_covariates else None
          future_i = future_covariates[i] if future_covariates else None
          if use_moving_windows and _all_equal_freq(
              target_i, past_i, future_i
          ):
              X_i, y_i, times_i = _create_lagged_data_by_moving_window(
                  target_i,
                  output_chunk_length,
                  past_i,
                  future_i,
                  lags,
                  lags_past_covariates,
                  lags_future_covariates,
                  max_samples_per_ts,
                  multi_models,
                  check_inputs,
                  is_training,
              )
          else:
              (
                  X_i,
                  y_i,
                  times_i,
              ) = _create_lagged_data_by_intersecting_times(
                  target_i,
                  output_chunk_length,
                  past_i,
                  future_i,
                  lags,
                  lags_past_covariates,
                  lags_future_covariates,
                  max_samples_per_ts,
                  multi_models,
                  check_inputs,
                  is_training,
              )
          (
              X_i,
              last_static_covariates_shape,
          ) = add_static_covariates_to_lagged_data(
              features=X_i,
              target_series=target_i,
              uses_static_covariates=uses_static_covariates,
              last_shape=last_static_covariates_shape,
          )
          X.append(X_i)
          y.append(y_i)
          times.append(times_i)
  
      if concatenate:
          X = np.concatenate(X, axis=0)
      if not is_training:
          y = None
      elif concatenate:
          y = np.concatenate(y, axis=0)
      return X, y, times, last_static_covariates_shape
  
imports: import warnings
  from functools import reduce
  from math import inf
  from typing import Dict, List, Optional, Sequence, Tuple, Union
  from itertools import chain
  import numpy
  import pandas
  from numpy.lib.stride_tricks import as_strided
  from darts.logging import get_logger, raise_if, raise_if_not, raise_log
  from darts.timeseries import TimeSeries
  from darts.utils.utils import get_single_series, series2seq
label_desc: Use image node_id 1 for calling a global function with example usage: create_lagged_data(target_series, past_covariates, future_covariates, lags, lags_past_covariates, lags_future_covariates, output_chunk_length, uses_static_covariates, last_static_covariates_shape, max_samples_per_ts, multi_models, check_inputs, use_moving_windows, is_training, concatenate) and returns: X,  y,  times,  last_static_covariates_shape