在许多应用中,时间序列集合是分层组织的。示例包括定义不同类型聚合的地理级别、产品或类别。在这种情况下,通常要求预测者为所有分解和聚合序列提供预测。一个自然而然的需求是这些预测必须是“一致的”,即底层序列的预测值精确地加总到聚合序列的预测值。

在本笔记本中,我们演示了如何使用 HierarchicalForecast 在地理级别之间生成一致的预测。我们将使用经典的澳大利亚国内旅游 (Tourism) 数据集,该数据集包含澳大利亚每个州游客数量的月度时间序列。

我们将首先加载 Tourism 数据,并使用 StatsForecast 中的 AutoETS 模型生成基础预测,然后使用 HierarchicalForecast 中的几种协调算法对预测进行协调。最后,我们将展示其性能与 预测:原理与实践 中报告的结果相当,该书使用了 R 包 fable

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

!pip install hierarchicalforecast statsforecast

1. 加载和处理数据

在本例中,我们将使用 Tourism 数据集,该数据集来自 预测:原理与实践 一书。

该数据集仅包含最低级别的时间序列,因此我们需要为所有层级创建时间序列。

import numpy as np
import pandas as pd
Y_df = pd.read_csv('https://raw.githubusercontent.com/Nixtla/transfer-learning-time-series/main/datasets/tourism.csv')
Y_df = Y_df.rename({'Trips': 'y', 'Quarter': 'ds'}, axis=1)
Y_df.insert(0, 'Country', 'Australia')
Y_df = Y_df[['Country', 'Region', 'State', 'Purpose', 'ds', 'y']]
Y_df['ds'] = Y_df['ds'].str.replace(r'(\d+) (Q\d)', r'\1-\2', regex=True)
Y_df['ds'] = pd.PeriodIndex(Y_df["ds"], freq='Q').to_timestamp()
Y_df.head()
国家地区目的dsy
0澳大利亚阿德莱德南澳大利亚商务1998-01-01135.077690
1澳大利亚阿德莱德南澳大利亚商务1998-04-01109.987316
2澳大利亚阿德莱德南澳大利亚商务1998-07-01166.034687
3澳大利亚阿德莱德南澳大利亚商务1998-10-01127.160464
4澳大利亚阿德莱德南澳大利亚商务1999-01-01137.448533

该数据集可以按以下非严格层级结构进行分组。

spec = [
    ['Country'],
    ['Country', 'State'], 
    ['Country', 'Purpose'], 
    ['Country', 'State', 'Region'], 
    ['Country', 'State', 'Purpose'], 
    ['Country', 'State', 'Region', 'Purpose']
]

使用 HierarchicalForecast 中的 aggregate 函数,我们可以获取完整的时间序列集合。

from hierarchicalforecast.utils import aggregate
Y_df, S_df, tags = aggregate(Y_df, spec)
Y_df.head()
unique_iddsy
0澳大利亚1998-01-0123182.197269
1澳大利亚1998-04-0120323.380067
2澳大利亚1998-07-0119826.640511
3澳大利亚1998-10-0120830.129891
4澳大利亚1999-01-0122087.353380
S_df.iloc[:5, :5]
unique_idAustralia/ACT/Canberra/BusinessAustralia/ACT/Canberra/HolidayAustralia/ACT/Canberra/OtherAustralia/ACT/Canberra/Visiting
0澳大利亚1.01.01.01.0
1Australia/ACT1.01.01.01.0
2Australia/New South Wales0.00.00.00.0
3Australia/Northern Territory0.00.00.00.0
4Australia/Queensland0.00.00.00.0
tags['Country/Purpose']
array(['Australia/Business', 'Australia/Holiday', 'Australia/Other',
       'Australia/Visiting'], dtype=object)

划分训练/测试集

我们将最后两年(8个季度)用作测试集。

Y_test_df = Y_df.groupby('unique_id', as_index=False).tail(8)
Y_train_df = Y_df.drop(Y_test_df.index)
Y_train_df.groupby('unique_id').size()
unique_id
Australia                                                72
Australia/ACT                                            72
Australia/ACT/Business                                   72
Australia/ACT/Canberra                                   72
Australia/ACT/Canberra/Business                          72
                                                         ..
Australia/Western Australia/Experience Perth/Other       72
Australia/Western Australia/Experience Perth/Visiting    72
Australia/Western Australia/Holiday                      72
Australia/Western Australia/Other                        72
Australia/Western Australia/Visiting                     72
Length: 425, dtype: int64

2. 计算基础预测

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

from statsforecast.models import AutoETS
from statsforecast.core import StatsForecast
fcst = StatsForecast(models=[AutoETS(season_length=4, model='ZZA')], 
                     freq='QS', n_jobs=-1)
Y_hat_df = fcst.forecast(df=Y_train_df, h=8, fitted=True)
Y_fitted_df = fcst.forecast_fitted_values()

3. 协调预测

以下单元格使用 HierarchicalReconciliation 类使之前的预测保持一致。由于层级结构不是严格的,我们无法使用 TopDownMiddleOut 等方法。在本例中,我们使用 BottomUpMinTrace

from hierarchicalforecast.methods import BottomUp, MinTrace
from hierarchicalforecast.core import HierarchicalReconciliation
reconcilers = [
    BottomUp(),
    MinTrace(method='mint_shrink'),
    MinTrace(method='ols')
]
hrec = HierarchicalReconciliation(reconcilers=reconcilers)
Y_rec_df = hrec.reconcile(Y_hat_df=Y_hat_df, Y_df=Y_fitted_df, S=S_df, tags=tags)

数据框 Y_rec_df 包含协调后的预测结果。

Y_rec_df.head()
unique_iddsAutoETSAutoETS/自下而上AutoETS/MinTrace_method-mint_shrinkAutoETS/MinTrace_method-ols
0澳大利亚2016-01-0125990.06800424381.91173725428.08978325894.399067
1澳大利亚2016-04-0124458.49028222903.89596423914.27140024357.301898
2澳大利亚2016-07-0123974.05598422412.26573923428.46239423865.910647
3澳大利亚2016-10-0124563.45449523127.34957824089.84595524470.782393
4澳大利亚2017-01-0125990.06800424518.11800625545.35867825901.362283

4. 评估

HierarchicalForecast 包包含一个 evaluate 函数,用于评估不同的层级,并且能够计算相对于基准模型的缩放指标。

from hierarchicalforecast.evaluation import evaluate
from utilsforecast.losses import rmse, mase
from functools import partial
eval_tags = {}
eval_tags['Total'] = tags['Country']
eval_tags['Purpose'] = tags['Country/Purpose']
eval_tags['State'] = tags['Country/State']
eval_tags['Regions'] = tags['Country/State/Region']
eval_tags['Bottom'] = tags['Country/State/Region/Purpose']

df = Y_rec_df.merge(Y_test_df, on=['unique_id', 'ds'])
evaluation = evaluate(df = df,
                      tags = eval_tags,
                      train_df = Y_train_df,
                      metrics = [rmse,
                                 partial(mase, seasonality=4)])

evaluation.columns = ['level', 'metric', 'Base', 'BottomUp', 'MinTrace(mint_shrink)', 'MinTrace(ols)']
numeric_cols = evaluation.select_dtypes(include="number").columns
evaluation[numeric_cols] = evaluation[numeric_cols].map('{:.2f}'.format).astype(np.float64)

RMSE

下表显示了使用 RMSE 衡量的不同层级下每种协调方法的性能。

evaluation.query('metric == "rmse"')
层级指标基础自下而上MinTrace(mint_shrink)MinTrace(ols)
0总计rmse1743.293028.622112.731818.94
2目的rmse534.75791.19577.14515.53
4rmse308.15413.39316.82287.32
6地区rmse51.6655.1346.5546.28
8底层rmse19.3719.3717.8018.19
10整体rmse41.1249.8240.4738.75

MASE

下表显示了使用 MASE 衡量的不同层级下每种协调方法的性能。

evaluation.query('metric == "mase"')
层级指标基础自下而上MinTrace(mint_shrink)MinTrace(ols)
1总计mase1.593.162.061.67
3目的mase1.322.281.481.25
5mase1.391.901.401.25
7地区mase1.121.191.010.99
9底层mase0.980.980.941.01
11整体mase1.021.060.971.02

与 fable 对比

请注意,我们可以重现 预测:原理与实践 中报告的结果。原始结果是使用 R 包 fable 计算的。

参考文献