组织成不同聚合级别结构的大量时间序列通常要求其预测遵循其聚合约束,这对创建能够进行一致预测的新颖算法提出了挑战。

HierarchicalForecast 包提供了广泛的经典分层对齐算法的 Python 实现。

在本 notebook 中,我们将展示如何使用 StatsForecast 库生成基础预测,并使用 HierarchicalForecast 包执行分层对齐。

您可以使用 Google Colab 在 CPU 或 GPU 上运行这些实验。

1. 库

!pip install hierarchicalforecast statsforecast datasetsforecast

2. 加载数据

在本示例中,我们将使用 TourismSmall 数据集。以下单元格获取层次结构中不同级别的时间序列、从底部层次结构恢复完整数据集的求和矩阵 S 以及由 tags 表示的每个层次结构的索引。

import numpy as np
import polars as pl

from datasetsforecast.hierarchical import HierarchicalData, HierarchicalInfo
group_name = 'TourismSmall'
group = HierarchicalInfo.get_group(group_name)
Y_df, S_df, tags = HierarchicalData.load('./data', group_name)

Y_df = pl.from_pandas(Y_df)
S_df = pl.from_pandas(S_df.reset_index(names="unique_id"))
Y_df = Y_df.with_columns(pl.col('ds').cast(pl.Date))
S_df[:6, :6]
unique_idnsw-hol-citynsw-hol-noncityvic-hol-cityvic-hol-noncityqld-hol-city
strf64f64f64f64f64
”total”1.01.01.01.01.0
”hol”1.01.01.01.01.0
”vfr”0.00.00.00.00.0
”bus”0.00.00.00.00.0
”oth”0.00.00.00.00.0
”nsw-hol”1.01.00.00.00.0
tags
{'Country': array(['total'], dtype=object),
 'Country/Purpose': array(['hol', 'vfr', 'bus', 'oth'], dtype=object),
 'Country/Purpose/State': array(['nsw-hol', 'vic-hol', 'qld-hol', 'sa-hol', 'wa-hol', 'tas-hol',
        'nt-hol', 'nsw-vfr', 'vic-vfr', 'qld-vfr', 'sa-vfr', 'wa-vfr',
        'tas-vfr', 'nt-vfr', 'nsw-bus', 'vic-bus', 'qld-bus', 'sa-bus',
        'wa-bus', 'tas-bus', 'nt-bus', 'nsw-oth', 'vic-oth', 'qld-oth',
        'sa-oth', 'wa-oth', 'tas-oth', 'nt-oth'], dtype=object),
 'Country/Purpose/State/CityNonCity': array(['nsw-hol-city', 'nsw-hol-noncity', 'vic-hol-city',
        'vic-hol-noncity', 'qld-hol-city', 'qld-hol-noncity',
        'sa-hol-city', 'sa-hol-noncity', 'wa-hol-city', 'wa-hol-noncity',
        'tas-hol-city', 'tas-hol-noncity', 'nt-hol-city', 'nt-hol-noncity',
        'nsw-vfr-city', 'nsw-vfr-noncity', 'vic-vfr-city',
        'vic-vfr-noncity', 'qld-vfr-city', 'qld-vfr-noncity',
        'sa-vfr-city', 'sa-vfr-noncity', 'wa-vfr-city', 'wa-vfr-noncity',
        'tas-vfr-city', 'tas-vfr-noncity', 'nt-vfr-city', 'nt-vfr-noncity',
        'nsw-bus-city', 'nsw-bus-noncity', 'vic-bus-city',
        'vic-bus-noncity', 'qld-bus-city', 'qld-bus-noncity',
        'sa-bus-city', 'sa-bus-noncity', 'wa-bus-city', 'wa-bus-noncity',
        'tas-bus-city', 'tas-bus-noncity', 'nt-bus-city', 'nt-bus-noncity',
        'nsw-oth-city', 'nsw-oth-noncity', 'vic-oth-city',
        'vic-oth-noncity', 'qld-oth-city', 'qld-oth-noncity',
        'sa-oth-city', 'sa-oth-noncity', 'wa-oth-city', 'wa-oth-noncity',
        'tas-oth-city', 'tas-oth-noncity', 'nt-oth-city', 'nt-oth-noncity'],
       dtype=object)}

我们将数据帧分割成训练/测试集。

Y_test_df = Y_df.group_by('unique_id').tail(group.horizon)
Y_train_df = Y_df.filter(pl.col('ds') < Y_test_df['ds'].min())

3. 基础预测

以下单元格使用 auto_arimanaive 模型计算每个时间序列的基础预测。请注意,Y_hat_df 包含预测结果,但它们并不一致。

from statsforecast.core import StatsForecast
from statsforecast.models import AutoARIMA, Naive
fcst = StatsForecast(
    models=[AutoARIMA(season_length=group.seasonality), Naive()], 
    freq="1q", 
    n_jobs=-1
)
Y_hat_df = fcst.forecast(df=Y_train_df, h=group.horizon)

4. 分层对齐

以下单元格使用 HierarchicalReconciliation 类使之前的预测结果保持一致。用于使预测结果一致的方法有

  • BottomUp:该方法的对齐是对上层进行简单加总。
  • TopDown:第二种方法将基础级别的预测约束到最顶层聚合级别的序列,然后通过比例将其分配给分解序列。
  • MiddleOut:将基础预测固定在中间层。
from hierarchicalforecast.core import HierarchicalReconciliation
from hierarchicalforecast.methods import BottomUp, TopDown, MiddleOut
reconcilers = [
    BottomUp(),
    TopDown(method='forecast_proportions'),
    MiddleOut(middle_level='Country/Purpose/State', 
              top_down_method='forecast_proportions')
]
hrec = HierarchicalReconciliation(reconcilers=reconcilers)
Y_rec_df = hrec.reconcile(Y_hat_df=Y_hat_df, Y_df=Y_train_df, S=S_df, tags=tags)

5. 评估

HierarchicalForecast 包包含 evaluate 函数用于评估不同的层次结构,我们可以使用 utilsforecast 计算相对于基线模型的平均绝对误差。

from hierarchicalforecast.evaluation import evaluate
from utilsforecast.losses import mse
df = Y_rec_df.join(Y_test_df, on=['unique_id', 'ds'])
evaluation = evaluate(df = df,
                      tags = tags,
                      train_df = Y_train_df,
                      metrics = [mse],
                      benchmark="Naive")

evaluation[["level", "metric", "AutoARIMA", "AutoARIMA/BottomUp", "AutoARIMA/TopDown_method-forecast_proportions"]]
级别指标AutoARIMAAutoARIMA/BottomUpAutoARIMA/TopDown_method-forecast_proportions
strstrf64f64f64
”国家"”mse-scaled”0.3178970.2269990.317897
”国家/目的"”mse-scaled”0.3232070.1993590.251368
”国家/目的/州"”mse-scaled”0.2661180.3057110.308241
”国家/目的/州/城市非城…“mse-scaled”0.3051730.3051730.305913
”总体"”mse-scaled”0.3117070.2349340.289406

参考文献