| name | forecasting-multiple-series |
| description | Forecasts multiple time series simultaneously using a global model with ForecasterRecursiveMultiSeries or ForecasterDirectMultiVariate. Covers data formats, encoding, per-series transformers, and multi-series backtesting. Use when the user has two or more related time series.
|
Forecasting Multiple Time Series
When to Use
- ForecasterRecursiveMultiSeries: A single global model learns patterns across many series. Each series is predicted independently but the model shares parameters. Best default for multi-series.
- ForecasterDirectMultiVariate: Uses values from multiple series as input features to predict one target series. Use when series are strongly correlated and influence each other.
Related skills
- Before:
choosing-a-forecaster (decide between MultiSeries, MultiVariate, Rnn, or Foundation for multi-series problems)
- Before:
autocorrelation-and-lag-selection (analyse representative series to inform the shared lags argument)
- Before:
feature-engineering (build per-series exogenous and rolling features)
- After:
hyperparameter-optimization (tune the global model across series)
- After:
prediction-intervals (add intervals to the multi-series forecasts)
Stop Conditions
Scan before writing code. Each row lists a rule, the symptom when it is broken, and the recovery. Full pitfall catalog: the troubleshooting-common-errors skill.
| Rule | Symptom | Recovery |
|---|
Use backtesting_forecaster_multiseries and the *_multiseries search functions | backtesting_forecaster / grid_search_forecaster raises on a multi-series forecaster | Call the _multiseries variant with series= instead of y= |
ForecasterDirectMultiVariate defaults to transformer_series=StandardScaler() | Series are scaled unexpectedly (other forecasters default to None) | Pass transformer_series=None explicitly if you do not want scaling |
exog format must match the series format (both wide, or both dict) | Index / format mismatch during fit or predict | Convert exog to the same layout as series before fitting |
Regressors with native categorical support need encoding='ordinal_category' | Categoricals silently encoded as plain ordinals, degrading the model | Set encoding='ordinal_category' for LightGBM / CatBoost / XGBoost / HistGBR |
Data Formats
ForecasterRecursiveMultiSeries accepts three input formats:
-
Wide DataFrame — columns are series, index is datetime:
-
Dictionary — {series_id: pd.Series}:
{'series_1': pd.Series([1.0, 1.2, ...]), 'series_2': pd.Series([2.5, 2.3, ...])}
Note: Long-format DataFrames are not directly accepted. Use reshape_series_long_to_dict() to convert long format to a dictionary first (see Data Reshaping Utilities below).
Complete Workflow
import pandas as pd
from lightgbm import LGBMRegressor
from skforecast.recursive import ForecasterRecursiveMultiSeries
from skforecast.model_selection import backtesting_forecaster_multiseries, TimeSeriesFold
series = pd.read_csv('data.csv', index_col='date', parse_dates=True)
series = series.asfreq('D')
forecaster = ForecasterRecursiveMultiSeries(
estimator=LGBMRegressor(n_estimators=200, random_state=123),
lags=24,
encoding='ordinal',
transformer_series=None,
categorical_features='auto',
differentiation=None,
dropna_from_series=False,
)
forecaster.fit(series=series)
predictions = forecaster.predict(steps=10)
predictions = forecaster.predict(steps=10, levels=['series_1', 'series_2'])
cv = TimeSeriesFold(
steps=10,
initial_train_size=len(series) - 100,
refit=False,
)
metric, predictions_bt = backtesting_forecaster_multiseries(
forecaster=forecaster,
series=series,
cv=cv,
metric='mean_absolute_error',
levels=None,
)
print(metric)
With Exogenous Variables
forecaster.fit(series=series, exog=exog_df)
predictions = forecaster.predict(steps=10, exog=exog_test)
ForecasterDirectMultiVariate
from skforecast.direct import ForecasterDirectMultiVariate
forecaster = ForecasterDirectMultiVariate(
level='target_series',
steps=10,
estimator=LGBMRegressor(n_estimators=100, random_state=123),
lags=24,
transformer_series=StandardScaler(),
categorical_features='auto',
dropna_from_series=False,
)
forecaster.fit(series=series_df)
predictions = forecaster.predict()
Data Reshaping Utilities
from skforecast.preprocessing import (
reshape_series_wide_to_long,
reshape_series_long_to_dict,
reshape_exog_long_to_dict,
reshape_series_exog_dict_to_long,
)
series_long = reshape_series_wide_to_long(series_wide)
series_dict = reshape_series_long_to_dict(series_long, freq='D')
exog_dict = reshape_exog_long_to_dict(exog_long, freq='D')
Common Mistakes
- Mismatched series lengths: ForecasterRecursiveMultiSeries handles different-length series if
dropna_from_series=True.
- Wrong encoding for categorical regressor: Use
encoding='ordinal_category' with regressors that natively handle categoricals (LightGBM, CatBoost).
- Exog format mismatch: Exog format (wide/dict) must match the series format.
- Forgetting
levels parameter: By default predict() forecasts all series. Use levels to limit predictions.
- Unexpected scaling in ForecasterDirectMultiVariate:
transformer_series defaults to StandardScaler(), unlike other forecasters that default to None. Set transformer_series=None explicitly if you don't want automatic scaling.