有些模型会创建序列的内部表示,这些表示可作为其他模型的输入。一个例子是 MSTL 模型,它将序列分解为趋势和季节性分量。本指南向您展示如何使用 mstl_decomposition 函数提取这些特征用于训练,然后使用它们的未来值进行推理。

from functools import partial

import pandas as pd
import statsforecast
from statsforecast import StatsForecast
from statsforecast.feature_engineering import mstl_decomposition
from statsforecast.models import ARIMA, MSTL
from utilsforecast.evaluation import evaluate
from utilsforecast.losses import smape, mase
df = pd.read_parquet('https://datasets-nixtla.s3.amazonaws.com/m4-hourly.parquet')
uids = df['unique_id'].unique()[:10]
df = df[df['unique_id'].isin(uids)]
df.head()
unique_iddsy
0H11605.0
1H12586.0
2H13586.0
3H14559.0
4H15511.0

假设您想使用 ARIMA 模型预测您的序列,但希望将 MSTL 模型的趋势和季节性分量作为外部回归变量纳入。您可以定义要使用的 MSTL 模型,然后将其提供给 mstl_decomposition 函数。

freq = 1
season_length = 24
horizon = 2 * season_length
valid = df.groupby('unique_id').tail(horizon)
train = df.drop(valid.index)
model = MSTL(season_length=24)
transformed_df, X_df = mstl_decomposition(train, model=model, freq=freq, h=horizon)

这将生成我们应该用于训练的数据框(已添加趋势和季节性列),以及我们应该用于预测的数据框。

transformed_df.head()
unique_iddsy趋势季节性
0H11605.0502.872910131.419934
1H12586.0507.87345693.100015
2H13586.0512.82253382.155386
3H14559.0517.71748142.412749
4H15511.0522.555849-11.401890
X_df.head()
unique_idds趋势季节性
0H1701643.801348-29.189627
1H1702644.328207-99.680432
2H1703644.749693-141.169014
3H1704645.086883-173.325625
4H1705645.356634-195.862530

现在我们可以训练我们的 ARIMA 模型并计算预测结果。

sf = StatsForecast(
    models=[ARIMA(order=(1, 0, 1), season_length=season_length)],
    freq=freq
)
preds = sf.forecast(h=horizon, df=transformed_df, X_df=X_df)
preds.head()
unique_iddsARIMA
0H1701612.737668
1H1702542.851796
2H1703501.931839
3H1704470.248289
4H1705448.115839

现在我们可以评估性能。

def compute_evaluation(preds):
    full = preds.merge(valid, on=['unique_id', 'ds'])
    mase24 = partial(mase, seasonality=24)
    res = evaluate(full, metrics=[smape, mase24], train_df=train).groupby('metric')['ARIMA'].mean()
    res_smape = '{:.1%}'.format(res['smape'])
    res_mase = '{:.1f}'.format(res['mase'])
    return pd.Series({'mase': res_mase, 'smape': res_smape})
compute_evaluation(preds)
mase      1.0
smape    3.9%
dtype: object

并将其与仅使用序列值进行比较。

preds_noexog = sf.forecast(h=horizon, df=train)
compute_evaluation(preds_noexog)
mase      2.3
smape    7.7%
dtype: object