class_docstr: This class turns any regressor compatible with the scikit-learn API into a
  autoregressive multivariate direct multi-step forecaster. A separate model 
  is created for each forecast time step. See documentation for more details.
  
  Parameters
  ----------
  regressor : regressor or pipeline compatible with the scikit-learn API
      An instance of a regressor or pipeline compatible with the scikit-learn API.
  level : str
      Name of the time series to be predicted.
  steps : int
      Maximum number of future steps the forecaster will predict when using
      method `predict()`. Since a different model is created for each step,
      this value must be defined before training.
  lags : int, list, numpy ndarray, range, dict
      Lags used as predictors. Index starts at 1, so lag 1 is equal to t-1.
  
      - `int`: include lags from 1 to `lags` (included).
      - `list`, `1d numpy ndarray` or `range`: include only lags present in 
      `lags`, all elements must be int.
      - `dict`: create different lags for each series. {'series_column_name': lags}.
  transformer_series : transformer (preprocessor), dict, default `sklearn.preprocessing.StandardScaler`
      An instance of a transformer (preprocessor) compatible with the scikit-learn
      preprocessing API with methods: fit, transform, fit_transform and 
      inverse_transform. Transformation is applied to each `series` before training 
      the forecaster. ColumnTransformers are not allowed since they do not have 
      inverse_transform method.
  
      - If single transformer: it is cloned and applied to all series. 
      - If `dict` of transformers: a different transformer can be used for each series.
  transformer_exog : transformer, default `None`
      An instance of a transformer (preprocessor) compatible with the scikit-learn
      preprocessing API. The transformation is applied to `exog` before training the
      forecaster. `inverse_transform` is not available when using ColumnTransformers.
  weight_func : Callable, default `None`
      Function that defines the individual weights for each sample based on the
      index. For example, a function that assigns a lower weight to certain dates.
      Ignored if `regressor` does not have the argument `sample_weight` in its `fit`
      method. The resulting `sample_weight` cannot have negative values.
  fit_kwargs : dict, default `None`
      Additional arguments to be passed to the `fit` method of the regressor.
  n_jobs : int, 'auto', default `'auto'`
      The number of jobs to run in parallel. If `-1`, then the number of jobs is 
      set to the number of cores. If 'auto', `n_jobs` is set using the function
      skforecast.utils.select_n_jobs_fit_forecaster.
      **New in version 0.9.0**
  forecaster_id : str, int, default `None`
      Name used as an identifier of the forecaster.
  
  Attributes
  ----------
  regressor : regressor or pipeline compatible with the scikit-learn API
      An instance of a regressor or pipeline compatible with the scikit-learn API.
      An instance of this regressor is trained for each step. All of them 
      are stored in `self.regressors_`.
  regressors_ : dict
      Dictionary with regressors trained for each step. They are initialized 
      as a copy of `regressor`.
  steps : int
      Number of future steps the forecaster will predict when using method
      `predict()`. Since a different model is created for each step, this value
      should be defined before training.
  lags : numpy ndarray, dict
      Lags used as predictors.
  lags_ : dict
      Dictionary containing the lags of each series. Created from `lags` and 
      used internally.
  transformer_series : transformer (preprocessor), dict, default `None`
      An instance of a transformer (preprocessor) compatible with the scikit-learn
      preprocessing API with methods: fit, transform, fit_transform and 
      inverse_transform. Transformation is applied to each `series` before training 
      the forecaster. ColumnTransformers are not allowed since they do not have 
      inverse_transform method.
  
      - If single transformer: it is cloned and applied to all series. 
      - If `dict` of transformers: a different transformer can be used for each series.
  transformer_series_ : dict
      Dictionary with the transformer for each series. It is created cloning the 
      objects in `transformer_series` and is used internally to avoid overwriting.
  transformer_exog : transformer
      An instance of a transformer (preprocessor) compatible with the scikit-learn
      preprocessing API. The transformation is applied to `exog` before training the
      forecaster. `inverse_transform` is not available when using ColumnTransformers.
  weight_func : Callable
      Function that defines the individual weights for each sample based on the
      index. For example, a function that assigns a lower weight to certain dates.
      Ignored if `regressor` does not have the argument `sample_weight` in its
      `fit` method. The resulting `sample_weight` cannot have negative values.
  source_code_weight_func : str
      Source code of the custom function used to create weights.
  max_lag : int
      Maximum value of lag included in `lags`.
  window_size : int
      Size of the window needed to create the predictors. It is equal to
      `max_lag`.
  last_window : pandas Series
      Last window seen by the forecaster during training. It stores the values 
      needed to predict the next `step` immediately after the training data.   
  index_type : type
      Type of index of the input used in training.
  index_freq : str
      Frequency of Index of the input used in training.
  training_range: pandas Index
      First and last values of index of the data used during training.
  included_exog : bool
      If the forecaster has been trained using exogenous variable/s.
  exog_type : type
      Type of exogenous variable/s used in training.
  exog_dtypes : dict
      Type of each exogenous variable/s used in training. If `transformer_exog` 
      is used, the dtypes are calculated after the transformation.
  exog_col_names : list
      Names of columns of `exog` if `exog` used in training was a pandas
      DataFrame.
  series_col_names : list
      Names of the series used during training.
  X_train_col_names : list
      Names of columns of the matrix created internally for training.
  fit_kwargs : dict
      Additional arguments to be passed to the `fit` method of the regressor.
  in_sample_residuals : dict
      Residuals of the models when predicting training data. Only stored up to
      1000 values per model in the form `{step: residuals}`. If `transformer_series` 
      is not `None`, residuals are stored in the transformed scale.
  out_sample_residuals : dict
      Residuals of the models when predicting non training data. Only stored
      up to 1000 values per model in the form `{step: residuals}`. If `transformer_series` 
      is not `None`, residuals are assumed to be in the transformed scale. Use 
      `set_out_sample_residuals()` method to set values.
  fitted : bool
      Tag to identify if the regressor has been fitted (trained).
  creation_date : str
      Date of creation.
  fit_date : str
      Date of last fit.
  skforecast_version : str
      Version of skforecast library used to create the forecaster.
  python_version : str
      Version of python used to create the forecaster.
  n_jobs : int, 'auto', default `'auto'`
      The number of jobs to run in parallel. If `-1`, then the number of jobs is 
      set to the number of cores. If 'auto', `n_jobs` is set using the fuction
      skforecast.utils.select_n_jobs_fit_forecaster.
      **New in version 0.9.0**
  forecaster_id : str, int
      Name used as an identifier of the forecaster.
  
  Notes
  -----
  A separate model is created for each forecasting time step. It is important to
  note that all models share the same parameter and hyperparameter configuration.
class_docstr_tok: ["This","class","turns","any","regressor","compatible","with","the","scikit-learn","API","into","a","autoregressive","multivariate","direct","multi-step","forecaster",".","A","separate","model","is","created","for","each","forecast","time","step",".","See","documentation","for","more","details",".","Parameters","--","--","--","--","--","regressor",":","regressor","or","pipeline","compatible","with","the","scikit-learn","API","An","instance","of","a","regressor","or","pipeline","compatible","with","the","scikit-learn","API",".","level",":","str","Name","of","the","time","series","to","be","predicted",".","steps",":","int","Maximum","number","of","future","steps","the","forecaster","will","predict","when","using","method","`","predict","(",")","`",".","Since","a","different","model","is","created","for","each","step",",","this","value","must","be","defined","before","training",".","lags",":","int",",","list",",","numpy","ndarray",",","range",",","dict","Lags","used","as","predictors",".","Index","starts","at","1",",","so","lag","1","is","equal","to","t-1",".","-","`","int","`",":","include","lags","from","1","to","`","lags","`","(","included",")",".","-","`","list","`",",","`","1d","numpy","ndarray","`","or","`","range","`",":","include","only","lags","present","in","`","lags","`",",","all","elements","must","be","int",".","-","`","dict","`",":","create","different","lags","for","each","series",".","{","'series_column_name","'",":","lags","}",".","transformer_series",":","transformer","(","preprocessor",")",",","dict",",","default","`","sklearn.preprocessing.StandardScaler","`","An","instance","of","a","transformer","(","preprocessor",")","compatible","with","the","scikit-learn","preprocessing","API","with","methods",":","fit",",","transform",",","fit_transform","and","inverse_transform",".","Transformation","is","applied","to","each","`","series","`","before","training","the","forecaster",".","ColumnTransformers","are","not","allowed","since","they","do","not","have","inverse_transform","method",".","-","If","single","transformer",":","it","is","cloned","and","applied","to","all","series",".","-","If","`","dict","`","of","transformers",":","a","different","transformer","can","be","used","for","each","series",".","transformer_exog",":","transformer",",","default","`","None","`","An","instance","of","a","transformer","(","preprocessor",")","compatible","with","the","scikit-learn","preprocessing","API",".","The","transformation","is","applied","to","`","exog","`","before","training","the","forecaster",".","`","inverse_transform","`","is","not","available","when","using","ColumnTransformers",".","weight_func",":","Callable",",","default","`","None","`","Function","that","defines","the","individual","weights","for","each","sample","based","on","the","index",".","For","example",",","a","function","that","assigns","a","lower","weight","to","certain","dates",".","Ignored","if","`","regressor","`","does","not","have","the","argument","`","sample_weight","`","in","its","`","fit","`","method",".","The","resulting","`","sample_weight","`","can","not","have","negative","values",".","fit_kwargs",":","dict",",","default","`","None","`","Additional","arguments","to","be","passed","to","the","`","fit","`","method","of","the","regressor",".","n_jobs",":","int",",","'auto","'",",","default","`","'auto","'","`","The","number","of","jobs","to","run","in","parallel",".","If","`","-1","`",",","then","the","number","of","jobs","is","set","to","the","number","of","cores",".","If","'auto","'",",","`","n_jobs","`","is","set","using","the","function","skforecast.utils.select_n_jobs_fit_forecaster",".","*","*","New","in","version","0.9.0","*","*","forecaster_id",":","str",",","int",",","default","`","None","`","Name","used","as","an","identifier","of","the","forecaster",".","Attributes","--","--","--","--","--","regressor",":","regressor","or","pipeline","compatible","with","the","scikit-learn","API","An","instance","of","a","regressor","or","pipeline","compatible","with","the","scikit-learn","API",".","An","instance","of","this","regressor","is","trained","for","each","step",".","All","of","them","are","stored","in","`","self.regressors_","`",".","regressors_",":","dict","Dictionary","with","regressors","trained","for","each","step",".","They","are","initialized","as","a","copy","of","`","regressor","`",".","steps",":","int","Number","of","future","steps","the","forecaster","will","predict","when","using","method","`","predict","(",")","`",".","Since","a","different","model","is","created","for","each","step",",","this","value","should","be","defined","before","training",".","lags",":","numpy","ndarray",",","dict","Lags","used","as","predictors",".","lags_",":","dict","Dictionary","containing","the","lags","of","each","series",".","Created","from","`","lags","`","and","used","internally",".","transformer_series",":","transformer","(","preprocessor",")",",","dict",",","default","`","None","`","An","instance","of","a","transformer","(","preprocessor",")","compatible","with","the","scikit-learn","preprocessing","API","with","methods",":","fit",",","transform",",","fit_transform","and","inverse_transform",".","Transformation","is","applied","to","each","`","series","`","before","training","the","forecaster",".","ColumnTransformers","are","not","allowed","since","they","do","not","have","inverse_transform","method",".","-","If","single","transformer",":","it","is","cloned","and","applied","to","all","series",".","-","If","`","dict","`","of","transformers",":","a","different","transformer","can","be","used","for","each","series",".","transformer_series_",":","dict","Dictionary","with","the","transformer","for","each","series",".","It","is","created","cloning","the","objects","in","`","transformer_series","`","and","is","used","internally","to","avoid","overwriting",".","transformer_exog",":","transformer","An","instance","of","a","transformer","(","preprocessor",")","compatible","with","the","scikit-learn","preprocessing","API",".","The","transformation","is","applied","to","`","exog","`","before","training","the","forecaster",".","`","inverse_transform","`","is","not","available","when","using","ColumnTransformers",".","weight_func",":","Callable","Function","that","defines","the","individual","weights","for","each","sample","based","on","the","index",".","For","example",",","a","function","that","assigns","a","lower","weight","to","certain","dates",".","Ignored","if","`","regressor","`","does","not","have","the","argument","`","sample_weight","`","in","its","`","fit","`","method",".","The","resulting","`","sample_weight","`","can","not","have","negative","values",".","source_code_weight_func",":","str","Source","code","of","the","custom","function","used","to","create","weights",".","max_lag",":","int","Maximum","value","of","lag","included","in","`","lags","`",".","window_size",":","int","Size","of","the","window","needed","to","create","the","predictors",".","It","is","equal","to","`","max_lag","`",".","last_window",":","pandas","Series","Last","window","seen","by","the","forecaster","during","training",".","It","stores","the","values","needed","to","predict","the","next","`","step","`","immediately","after","the","training","data",".","index_type",":","type","Type","of","index","of","the","input","used","in","training",".","index_freq",":","str","Frequency","of","Index","of","the","input","used","in","training",".","training_range",":","pandas","Index","First","and","last","values","of","index","of","the","data","used","during","training",".","included_exog",":","bool","If","the","forecaster","has","been","trained","using","exogenous","variable\/s",".","exog_type",":","type","Type","of","exogenous","variable\/s","used","in","training",".","exog_dtypes",":","dict","Type","of","each","exogenous","variable\/s","used","in","training",".","If","`","transformer_exog","`","is","used",",","the","dtypes","are","calculated","after","the","transformation",".","exog_col_names",":","list","Names","of","columns","of","`","exog","`","if","`","exog","`","used","in","training","was","a","pandas","DataFrame",".","series_col_names",":","list","Names","of","the","series","used","during","training",".","X_train_col_names",":","list","Names","of","columns","of","the","matrix","created","internally","for","training",".","fit_kwargs",":","dict","Additional","arguments","to","be","passed","to","the","`","fit","`","method","of","the","regressor",".","in_sample_residuals",":","dict","Residuals","of","the","models","when","predicting","training","data",".","Only","stored","up","to","1000","values","per","model","in","the","form","`","{","step",":","residuals","}","`",".","If","`","transformer_series","`","is","not","`","None","`",",","residuals","are","stored","in","the","transformed","scale",".","out_sample_residuals",":","dict","Residuals","of","the","models","when","predicting","non","training","data",".","Only","stored","up","to","1000","values","per","model","in","the","form","`","{","step",":","residuals","}","`",".","If","`","transformer_series","`","is","not","`","None","`",",","residuals","are","assumed","to","be","in","the","transformed","scale",".","Use","`","set_out_sample_residuals","(",")","`","method","to","set","values",".","fitted",":","bool","Tag","to","identify","if","the","regressor","has","been","fitted","(","trained",")",".","creation_date",":","str","Date","of","creation",".","fit_date",":","str","Date","of","last","fit",".","skforecast_version",":","str","Version","of","skforecast","library","used","to","create","the","forecaster",".","python_version",":","str","Version","of","python","used","to","create","the","forecaster",".","n_jobs",":","int",",","'auto","'",",","default","`","'auto","'","`","The","number","of","jobs","to","run","in","parallel",".","If","`","-1","`",",","then","the","number","of","jobs","is","set","to","the","number","of","cores",".","If","'auto","'",",","`","n_jobs","`","is","set","using","the","fuction","skforecast.utils.select_n_jobs_fit_forecaster",".","*","*","New","in","version","0.9.0","*","*","forecaster_id",":","str",",","int","Name","used","as","an","identifier","of","the","forecaster",".","Notes","--","--","-","A","separate","model","is","created","for","each","forecasting","time","step",".","It","is","important","to","note","that","all","models","share","the","same","parameter","and","hyperparameter","configuration","."]
docstr: Training Forecaster.
  
  Additional arguments to be passed to the `fit` method of the regressor 
  can be added with the `fit_kwargs` argument when initializing the forecaster.
  
  Parameters
  ----------
  series : pandas DataFrame
      Training time series.
  exog : pandas Series, pandas DataFrame, default `None`
      Exogenous variable/s included as predictor/s. Must have the same
      number of observations as `series` and their indexes must be aligned so
      that series[i] is regressed on exog[i].
  store_in_sample_residuals : bool, default `True`
      If `True`, in-sample residuals will be stored in the forecaster object
      after fitting.
  
  Returns
  -------
  None
code: def fit(
      self,
      series: pd.DataFrame,
      exog: Optional[Union[pd.Series, pd.DataFrame]] = None,
      store_in_sample_residuals: bool = True,
  ) -> None:
      """
      Training Forecaster.
  
      Additional arguments to be passed to the `fit` method of the regressor
      can be added with the `fit_kwargs` argument when initializing the forecaster.
  
      Parameters
      ----------
      series : pandas DataFrame
          Training time series.
      exog : pandas Series, pandas DataFrame, default `None`
          Exogenous variable/s included as predictor/s. Must have the same
          number of observations as `series` and their indexes must be aligned so
          that series[i] is regressed on exog[i].
      store_in_sample_residuals : bool, default `True`
          If `True`, in-sample residuals will be stored in the forecaster object
          after fitting.
  
      Returns
      -------
      None
  
      """
  
      # Reset values in case the forecaster has already been fitted.
      self.index_type = None
      self.index_freq = None
      self.last_window = None
      self.included_exog = False
      self.exog_type = None
      self.exog_dtypes = None
      self.exog_col_names = None
      self.series_col_names = None
      self.X_train_col_names = None
      self.in_sample_residuals = {
          step: None for step in range(1, self.steps + 1)
      }
      self.fitted = False
      self.training_range = None
  
      self.series_col_names = list(series.columns)
  
      if exog is not None:
          self.included_exog = True
          self.exog_type = type(exog)
          self.exog_col_names = (
              exog.columns.to_list()
              if isinstance(exog, pd.DataFrame)
              else [exog.name]
          )
  
          if len(
              set(self.exog_col_names) - set(self.series_col_names)
          ) != len(self.exog_col_names):
              raise ValueError(
                  (
                      f"`exog` cannot contain a column named the same as one of the "
                      f"series (column names of series).\n"
                      f"    `series` columns : {self.series_col_names}.\n"
                      f"    `exog`   columns : {self.exog_col_names}."
                  )
              )
  
      X_train, y_train = self.create_train_X_y(series=series, exog=exog)
  
      def fit_forecaster(
          regressor, X_train, y_train, step, store_in_sample_residuals
      ):
          """
          Auxiliary function to fit each of the forecaster's regressors in parallel.
  
          Parameters
          ----------
          regressor : object
              Regressor to be fitted.
          X_train : pandas DataFrame
              Dataframe created with the `create_train_X_y` method, first return.
          y_train : dict
              Dict created with the `create_train_X_y` method, second return.
          step : int
              Step of the forecaster to be fitted.
          store_in_sample_residuals : bool
              If `True`, in-sample residuals will be stored in the forecaster object
              after fitting.
  
          Returns
          -------
          Tuple with the step, fitted regressor and in-sample residuals.
  
          """
  
          X_train_step, y_train_step = self.filter_train_X_y_for_step(
              step=step,
              X_train=X_train,
              y_train=y_train,
              remove_suffix=True,
          )
          sample_weight = self.create_sample_weights(
              X_train=X_train_step
          )
          if sample_weight is not None:
              regressor.fit(
                  X=X_train_step,
                  y=y_train_step,
                  sample_weight=sample_weight,
                  **self.fit_kwargs,
              )
          else:
              regressor.fit(
                  X=X_train_step, y=y_train_step, **self.fit_kwargs
              )
  
          # This is done to save time during fit in functions such as backtesting()
          if store_in_sample_residuals:
              residuals = (
                  (y_train_step - regressor.predict(X_train_step))
              ).to_numpy()
  
              if len(residuals) > 1000:
                  # Only up to 1000 residuals are stored
                  rng = np.random.default_rng(seed=123)
                  residuals = rng.choice(
                      a=residuals, size=1000, replace=False
                  )
          else:
              residuals = None
  
          return step, regressor, residuals
  
      results_fit = Parallel(n_jobs=self.n_jobs)(
          delayed(fit_forecaster)(
              regressor=copy(self.regressor),
              X_train=X_train,
              y_train=y_train,
              step=step,
              store_in_sample_residuals=store_in_sample_residuals,
          )
          for step in range(1, self.steps + 1)
      )
  
      self.regressors_ = {
          step: regressor for step, regressor, _ in results_fit
      }
  
      if store_in_sample_residuals:
          self.in_sample_residuals = {
              step: residuals for step, _, residuals in results_fit
          }
  
      self.fitted = True
      self.fit_date = pd.Timestamp.today().strftime("%Y-%m-%d %H:%M:%S")
      self.training_range = preprocess_y(
          y=series[self.level], return_values=False
      )[1][[0, -1]]
      self.index_type = type(X_train.index)
      if isinstance(X_train.index, pd.DatetimeIndex):
          self.index_freq = X_train.index.freqstr
      else:
          self.index_freq = X_train.index.step
  
      self.last_window = series.iloc[-self.max_lag :].copy()
  
imports: from typing import Union, Dict, List, Tuple, Any, Optional, Callable
  import warnings
  import logging
  import sys
  import numpy
  import pandas
  import sklearn
  import sklearn.pipeline
  from sklearn.base import clone
  from sklearn.preprocessing import StandardScaler
  import inspect
  from copy import copy, deepcopy
  from itertools import chain
  from joblib import Parallel, delayed, cpu_count
  import skforecast
  from ..ForecasterBase import ForecasterBase
  from ..exceptions import IgnoredArgumentWarning
  from ..utils import initialize_lags
  from ..utils import initialize_weights
  from ..utils import check_select_fit_kwargs
  from ..utils import check_y
  from ..utils import check_exog
  from ..utils import get_exog_dtypes
  from ..utils import check_exog_dtypes
  from ..utils import check_predict_input
  from ..utils import check_interval
  from ..utils import preprocess_y
  from ..utils import preprocess_last_window
  from ..utils import preprocess_exog
  from ..utils import exog_to_direct
  from ..utils import exog_to_direct_numpy
  from ..utils import expand_index
  from ..utils import transform_series
  from ..utils import transform_dataframe
  from ..utils import select_n_jobs_fit_forecaster
label_desc: Use image node_id 7 for calling the ForecasterAutoregMultiVariate obj's underlying member method code with example usage: obj.fit(series, exog, store_in_sample_residuals) and returns: step,  regressor,  residuals