Skip to content

Adapters

Adapters provide integration with forecasting frameworks.

SkforecastAdapter

Integration with skforecast's ForecasterRecursiveMultiSeries (0.21+).

SkforecastAdapter(forecaster, series=None, exog=None)

Bases: BaseAdapter

Adapter for skforecast :class:~skforecast.recursive.ForecasterRecursiveMultiSeries.

In skforecast 0.21+, the multi-series global forecaster was renamed from ForecasterAutoregMultiSeries / ForecasterMultiSeries to ForecasterRecursiveMultiSeries, and create_train_X_y requires the same series object you passed to :meth:fit.

Provides integration with skforecast's multi-series forecasting models, extracting the training matrix and providing prediction capabilities for use with xeries explainers.

Example

from skforecast.recursive import ForecasterRecursiveMultiSeries from sklearn.ensemble import RandomForestRegressor

forecaster = ForecasterRecursiveMultiSeries( ... estimator=RandomForestRegressor(random_state=0), ... lags=24, ... ) forecaster.fit(series=series_wide_df)

adapter = SkforecastAdapter(forecaster, series=series_wide_df) X, y = adapter.get_training_data()

or: adapter = from_skforecast(forecaster, series=series_wide_df)

Initialize the skforecast adapter.

Parameters:

Name Type Description Default
forecaster Any

A fitted ForecasterRecursiveMultiSeries instance.

required
series DataFrame | dict[str, Series | DataFrame] | None

Training series in the same form as passed to fit(series=...). If provided, :meth:get_training_data can be called with no arguments.

None
exog Series | DataFrame | dict[str, Series | DataFrame] | None

Optional exogenous variables, same as passed to fit if any.

None

Raises:

Type Description
ValueError

If forecaster is not fitted or is not a supported type.

Source code in src/xeries/adapters/skforecast.py
def __init__(
    self,
    forecaster: Any,
    series: pd.DataFrame | dict[str, pd.Series | pd.DataFrame] | None = None,
    exog: pd.Series | pd.DataFrame | dict[str, pd.Series | pd.DataFrame] | None = None,
) -> None:
    """Initialize the skforecast adapter.

    Args:
        forecaster: A fitted ``ForecasterRecursiveMultiSeries`` instance.
        series: Training series in the same form as passed to ``fit(series=...)``.
            If provided, :meth:`get_training_data` can be called with no arguments.
        exog: Optional exogenous variables, same as passed to ``fit`` if any.

    Raises:
        ValueError: If forecaster is not fitted or is not a supported type.
    """
    self._validate_forecaster(forecaster)
    self.forecaster = forecaster
    self._series: pd.DataFrame | dict[str, pd.Series | pd.DataFrame] | None = series
    self._exog: pd.Series | pd.DataFrame | dict[str, pd.Series | pd.DataFrame] | None = exog
    self._X: pd.DataFrame | None = None
    self._y: pd.Series | None = None
    self._cache_key: tuple[int | None, int | None] | None = None

n_lags property

Return the number of lag features in the training matrix.

get_feature_names()

Get predictor column names (lags, window features, etc.).

Source code in src/xeries/adapters/skforecast.py
def get_feature_names(self) -> list[str]:
    """Get predictor column names (lags, window features, etc.)."""
    X, _ = self.get_training_data()
    return [c for c in X.columns if c != self.SERIES_COL_ENCODED]

get_lag_features()

Get lag feature column names (e.g. lag_1, lag_2, ...).

Source code in src/xeries/adapters/skforecast.py
def get_lag_features(self) -> list[str]:
    """Get lag feature column names (e.g. ``lag_1``, ``lag_2``, ...)."""
    feature_names = self.get_feature_names()
    return [f for f in feature_names if f.startswith("lag_")]

get_series_column()

Column or index level name used to identify the series in X.

skforecast 0.21+ typically adds _level_skforecast (ordinal codes). Older stacked layouts use a MultiIndex level named level.

Source code in src/xeries/adapters/skforecast.py
def get_series_column(self) -> str:
    """Column or index level name used to identify the series in ``X``.

    skforecast 0.21+ typically adds ``_level_skforecast`` (ordinal codes).
    Older stacked layouts use a MultiIndex level named ``level``.
    """
    X, _ = self.get_training_data()
    if isinstance(X.index, pd.MultiIndex) and self.SERIES_LEVEL_LEGACY in X.index.names:
        return self.SERIES_LEVEL_LEGACY
    if self.SERIES_COL_ENCODED in X.columns:
        return self.SERIES_COL_ENCODED
    if self.SERIES_LEVEL_LEGACY in X.columns:
        return self.SERIES_LEVEL_LEGACY
    raise ValueError(
        "Cannot infer series column: expected MultiIndex level 'level' or "
        f"column '{self.SERIES_COL_ENCODED}' in the training matrix."
    )

get_series_ids()

Unique series identifiers (decoded names when using ordinal encoding).

Source code in src/xeries/adapters/skforecast.py
def get_series_ids(self) -> list[Any]:
    """Unique series identifiers (decoded names when using ordinal encoding)."""
    X, _ = self.get_training_data()
    col = self.get_series_column()
    if col == self.SERIES_COL_ENCODED:
        codes = X[col]
        names = getattr(self.forecaster, "series_names_in_", None)
        if names is not None:
            return [names[int(i)] for i in sorted(codes.unique())]
        return sorted(codes.unique().tolist())
    if isinstance(X.index, pd.MultiIndex):
        return list(X.index.get_level_values(col).unique())
    return list(X[col].unique())

get_training_data(series=None, exog=None, *, suppress_warnings=False)

Extract training features (X) and target (y) from the forecaster.

skforecast's create_train_X_y requires the series argument (and optional exog) matching what was used in fit. Pass series here or when constructing :class:SkforecastAdapter.

The training matrix X has a MultiIndex with level (series id) and date.

Parameters:

Name Type Description Default
series DataFrame | dict[str, Series | DataFrame] | None

Same series as forecaster.fit(series=...). Uses the constructor series / exog if omitted.

None
exog Series | DataFrame | dict[str, Series | DataFrame] | None

Same optional exog as in fit, if used.

None
suppress_warnings bool

Forwarded to skforecast.

False

Returns:

Type Description
tuple[DataFrame, Series]

Tuple of (X, y).

Raises:

Type Description
ValueError

If series cannot be resolved.

Source code in src/xeries/adapters/skforecast.py
def get_training_data(
    self,
    series: pd.DataFrame | dict[str, pd.Series | pd.DataFrame] | None = None,
    exog: pd.Series | pd.DataFrame | dict[str, pd.Series | pd.DataFrame] | None = None,
    *,
    suppress_warnings: bool = False,
) -> tuple[pd.DataFrame, pd.Series]:
    """Extract training features (X) and target (y) from the forecaster.

    skforecast's ``create_train_X_y`` requires the ``series`` argument (and optional
    ``exog``) matching what was used in ``fit``. Pass ``series`` here or when
    constructing :class:`SkforecastAdapter`.

    The training matrix X has a MultiIndex with ``level`` (series id) and ``date``.

    Args:
        series: Same ``series`` as ``forecaster.fit(series=...)``. Uses the
            constructor ``series`` / ``exog`` if omitted.
        exog: Same optional exog as in ``fit``, if used.
        suppress_warnings: Forwarded to skforecast.

    Returns:
        Tuple of ``(X, y)``.

    Raises:
        ValueError: If ``series`` cannot be resolved.
    """
    s = series if series is not None else self._series
    e = exog if exog is not None else self._exog
    if s is None:
        raise ValueError(
            "Pass `series` (the same object passed to forecaster.fit(series=...)) "
            "to get_training_data(), or provide series=... when constructing "
            "SkforecastAdapter(forecaster, series=...)."
        )

    key = (id(s), id(e) if e is not None else None)
    if self._cache_key != key or self._X is None or self._y is None:
        self._X, self._y = self.forecaster.create_train_X_y(
            s,
            exog=e,
            suppress_warnings=suppress_warnings,
        )
        self._cache_key = key

    self._series = s
    self._exog = e
    return self._X, self._y

predict(X)

Make predictions using the underlying estimator.

The input X is forwarded to the underlying estimator unchanged when it is a :class:pandas.DataFrame so that estimators which were fitted with feature names (e.g. LGBMRegressor, modern sklearn check_feature_names machinery) can match their training schema and avoid the "X does not have valid feature names, but ... was fitted with feature names" warning. Non-DataFrame inputs are passed through untouched.

Parameters:

Name Type Description Default
X DataFrame

Input features DataFrame (same structure as training X).

required

Returns:

Type Description
NDArray[Any]

Array of predictions.

Source code in src/xeries/adapters/skforecast.py
def predict(self, X: pd.DataFrame) -> NDArray[Any]:
    """Make predictions using the underlying estimator.

    The input ``X`` is forwarded to the underlying estimator unchanged when it
    is a :class:`pandas.DataFrame` so that estimators which were fitted with
    feature names (e.g. ``LGBMRegressor``, modern sklearn ``check_feature_names``
    machinery) can match their training schema and avoid the
    ``"X does not have valid feature names, but ... was fitted with feature names"``
    warning. Non-DataFrame inputs are passed through untouched.

    Args:
        X: Input features DataFrame (same structure as training X).

    Returns:
        Array of predictions.
    """
    model = self._fitted_estimator
    if model is None:
        raise ValueError("No fitted estimator found on forecaster")
    return np.asarray(model.predict(X))

Helper Function

from_skforecast(forecaster, series=None, exog=None)

Create a :class:SkforecastAdapter from a fitted forecaster.

Parameters:

Name Type Description Default
forecaster Any

Fitted ForecasterRecursiveMultiSeries.

required
series DataFrame | dict[str, Series | DataFrame] | None

Training series (same as fit(series=...)) if not passed later.

None
exog Series | DataFrame | dict[str, Series | DataFrame] | None

Optional exog matching fit.

None

Returns:

Type Description
SkforecastAdapter

SkforecastAdapter instance.

Source code in src/xeries/adapters/skforecast.py
def from_skforecast(
    forecaster: Any,
    series: pd.DataFrame | dict[str, pd.Series | pd.DataFrame] | None = None,
    exog: pd.Series | pd.DataFrame | dict[str, pd.Series | pd.DataFrame] | None = None,
) -> SkforecastAdapter:
    """Create a :class:`SkforecastAdapter` from a fitted forecaster.

    Args:
        forecaster: Fitted ``ForecasterRecursiveMultiSeries``.
        series: Training series (same as ``fit(series=...)``) if not passed later.
        exog: Optional exog matching ``fit``.

    Returns:
        SkforecastAdapter instance.
    """
    return SkforecastAdapter(forecaster, series=series, exog=exog)

Base Adapter

BaseAdapter

Bases: ABC

Abstract base class for forecasting framework adapters.

Adapters provide a consistent interface for extracting training data and making predictions from various forecasting frameworks.

get_feature_names() abstractmethod

Get the names of features used by the model.

Returns:

Type Description
list[str]

List of feature names.

Source code in src/xeries/adapters/base.py
@abstractmethod
def get_feature_names(self) -> list[str]:
    """Get the names of features used by the model.

    Returns:
        List of feature names.
    """
    ...

get_series_column() abstractmethod

Get the name of the column/index level containing series identifiers.

Returns:

Type Description
str

Name of the series identifier column.

Source code in src/xeries/adapters/base.py
@abstractmethod
def get_series_column(self) -> str:
    """Get the name of the column/index level containing series identifiers.

    Returns:
        Name of the series identifier column.
    """
    ...

get_training_data(*args, **kwargs) abstractmethod

Extract training features (X) and target (y) from the forecaster.

Framework-specific adapters may require extra arguments (e.g. skforecast needs the same series passed to fit).

Returns:

Type Description
tuple[DataFrame, Series]

Tuple of (X, y) where X is a DataFrame with features and y is the target.

Source code in src/xeries/adapters/base.py
@abstractmethod
def get_training_data(self, *args: Any, **kwargs: Any) -> tuple[pd.DataFrame, pd.Series]:
    """Extract training features (X) and target (y) from the forecaster.

    Framework-specific adapters may require extra arguments (e.g. skforecast
    needs the same ``series`` passed to ``fit``).

    Returns:
        Tuple of (X, y) where X is a DataFrame with features and y is the target.
    """
    ...

predict(X) abstractmethod

Make predictions using the underlying model.

Parameters:

Name Type Description Default
X DataFrame

Input features DataFrame.

required

Returns:

Type Description
NDArray[Any]

Array of predictions.

Source code in src/xeries/adapters/base.py
@abstractmethod
def predict(self, X: pd.DataFrame) -> NDArray[Any]:
    """Make predictions using the underlying model.

    Args:
        X: Input features DataFrame.

    Returns:
        Array of predictions.
    """
    ...