import os
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生成特征
利用StatsForecast模型创建特征
一些模型会创建序列的内部表示,这些表示可以为其他模型提供有用的输入。一个例子是 MSTL 模型,它将序列分解为趋势和季节性分量。本指南将向您展示如何使用 mstl_decomposition 函数来提取这些特征以进行训练,然后使用它们的未来值进行推断。
# 这样,预测方法的输出结果中就会包含一个id列。
# 而不是作为索引
os.environ['NIXTLA_ID_AS_COL'] = '1'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_id | ds | y | |
|---|---|---|---|
| 0 | H1 | 1 | 605.0 |
| 1 | H1 | 2 | 586.0 |
| 2 | H1 | 3 | 586.0 |
| 3 | H1 | 4 | 559.0 |
| 4 | H1 | 5 | 511.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)这生成了我们应该用于训练的DataFrame(添加了趋势和季节性列),以及我们应该用于预测的DataFrame。
transformed_df.head()| unique_id | ds | y | trend | seasonal | |
|---|---|---|---|---|---|
| 0 | H1 | 1 | 605.0 | 501.350550 | 124.683643 |
| 1 | H1 | 2 | 586.0 | 506.424549 | 87.115039 |
| 2 | H1 | 3 | 586.0 | 511.453736 | 79.479564 |
| 3 | H1 | 4 | 559.0 | 516.434474 | 45.616992 |
| 4 | H1 | 5 | 511.0 | 521.362991 | -10.940498 |
X_df.head()| unique_id | ds | trend | seasonal | |
|---|---|---|---|---|
| 0 | H1 | 701 | 626.519191 | -22.738998 |
| 1 | H1 | 702 | 627.423191 | -108.992314 |
| 2 | H1 | 703 | 628.146391 | -148.675478 |
| 3 | H1 | 704 | 628.724951 | -183.715284 |
| 4 | H1 | 705 | 629.187799 | -208.038694 |
我们现在可以训练我们的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_id | ds | ARIMA | |
|---|---|---|---|
| 0 | H1 | 701 | 597.716125 |
| 1 | H1 | 702 | 513.244507 |
| 2 | H1 | 703 | 475.042450 |
| 3 | H1 | 704 | 441.217743 |
| 4 | H1 | 705 | 417.895813 |
我们现在可以评估性能。
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.7%
dtype: object
并将其与仅使用序列值进行比较。
preds_noexog = sf.forecast(h=horizon, df=train)
compute_evaluation(preds_noexog)mase 2.3
smape 7.7%
dtype: object
Give us a ⭐ on Github