在许多情况下,只有层级结构最低层的时间序列(底部时间序列)可用。HierarchicalForecast 提供了工具来创建所有层级结构的时间序列,并允许您计算所有层级结构的预测区间。在本 Notebook 中,我们将看到如何实现这一点。

!pip install hierarchicalforecast statsforecast
import pandas as pd

# compute base forecast no coherent
from statsforecast.models import AutoARIMA
from statsforecast.core import StatsForecast

#obtain hierarchical reconciliation methods and evaluation
from hierarchicalforecast.methods import BottomUp, MinTrace
from hierarchicalforecast.utils import aggregate, HierarchicalPlot
from hierarchicalforecast.core import HierarchicalReconciliation

聚合底部时间序列

在此示例中,我们将使用来自《预测:原理与实践》一书中的 Tourism 数据集。该数据集仅包含最低层级的时间序列,因此我们需要为所有层级创建时间序列。

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 函数,我们可以生成: 1. Y_df: 层级结构的时间序列 y[a,b]τ\mathbf{y}_{[a,b]\tau} 2. S_df: 包含聚合约束 S[a,b]S_{[a,b]} 的 DataFrame 3. tags: 一个列表,包含构成每个聚合层级的 ‘unique_ids’。

Y_df, S_df, tags = aggregate(df=Y_df, spec=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_id澳大利亚/ACT/堪培拉/商务澳大利亚/ACT/堪培拉/假日澳大利亚/ACT/堪培拉/其他澳大利亚/ACT/堪培拉/探访
0澳大利亚1.01.01.01.0
1澳大利亚/ACT1.01.01.01.0
2澳大利亚/新南威尔士0.00.00.00.0
3澳大利亚/北领地0.00.00.00.0
4澳大利亚/昆士兰0.00.00.00.0
tags['Country/Purpose']
array(['Australia/Business', 'Australia/Holiday', 'Australia/Other',
       'Australia/Visiting'], dtype=object)

我们可以使用 HierarchicalPlot 类按如下方式可视化 S 矩阵和数据。

hplot = HierarchicalPlot(S=S_df, tags=tags)
hplot.plot_summing_matrix()

hplot.plot_hierarchically_linked_series(
    bottom_series='Australia/ACT/Canberra/Holiday',
    Y_df=Y_df
)

划分训练/测试集

我们使用最后两年(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

计算基础预测

以下单元格使用 AutoARIMA 模型计算 Y_df 中每个时间序列的基础预测。请注意,Y_hat_df 包含预测结果,但它们不一致。为了协调预测区间,我们需要使用 StatsForecastlevel 参数计算不一致的区间。

fcst = StatsForecast(models=[AutoARIMA(season_length=4)], 
                     freq='QS', n_jobs=-1)
Y_hat_df = fcst.forecast(df=Y_train_df, h=8, fitted=True, level=[80, 90])
Y_fitted_df = fcst.forecast_fitted_values()

协调预测

以下单元格使用 HierarchicalReconciliation 类使先前的预测结果一致。由于层级结构不严格,我们无法使用诸如 TopDownMiddleOut 等方法。在此示例中,我们使用 BottomUpMinTrace。如果要计算预测区间,必须如下使用 level 参数。

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, level=[80, 90])

DataFrame Y_rec_df 包含协调后的预测结果。

Y_rec_df.head()
unique_iddsAutoARIMAAutoARIMA-lo-90AutoARIMA-lo-80AutoARIMA-hi-80AutoARIMA-hi-90AutoARIMA/BottomUpAutoARIMA/BottomUp-lo-90AutoARIMA/BottomUp-lo-80AutoARIMA/MinTrace_method-mint_shrinkAutoARIMA/MinTrace_method-mint_shrink-lo-90AutoARIMA/MinTrace_method-mint_shrink-lo-80AutoARIMA/MinTrace_method-mint_shrink-hi-80AutoARIMA/MinTrace_method-mint_shrink-hi-90AutoARIMA/MinTrace_method-olsAutoARIMA/MinTrace_method-ols-lo-90AutoARIMA/MinTrace_method-ols-lo-80AutoARIMA/MinTrace_method-ols-hi-80AutoARIMA/MinTrace_method-ols-hi-90
0澳大利亚2016-01-0126212.55355324705.94818025038.71507727386.39202927719.15892724646.51708423983.65684324130.06409125267.79733824491.63061824663.06409125872.53058626043.96405826082.75348825010.87614125247.62380326917.88317427154.630835
1澳大利亚2016-04-0125033.66712523337.26758823711.95469626355.37955426730.06666222942.95770322229.91683822387.40757923836.80444423002.62021423186.86812824486.74076024670.98867424822.10209423616.73439323882.96633225761.23785726027.469796
2澳大利亚2016-07-0124507.02719822640.02879823052.39641325961.65798326374.02559922568.28648821805.89219921974.28372823294.24090822410.71983322605.86487323982.61694224177.76198324269.57872422944.38004323237.07928725302.07816225594.777406
3澳大利亚2016-10-0125598.92861323575.66524324022.54741027175.30981627622.19198323113.07572622308.67186022486.34212724154.48448723221.70618523427.73076624881.23820825087.26279025340.54992323905.43407024222.41093626458.68891126775.665777
4澳大利亚2017-01-0126982.57679624669.53523825180.42128528784.73230829295.61835423779.26492122874.19422723074.09897525155.00137224125.26891524352.70795225957.29479326184.73383026690.20092725051.35269825413.32833527967.07351828329.049155

绘制预测结果

然后我们可以使用以下函数绘制概率预测结果。

plot_df = Y_df.merge(Y_rec_df, on=['unique_id', 'ds'], how="outer")

绘制单个时间序列

hplot.plot_series(
    series='Australia',
    Y_df=plot_df, 
    models=['y', 'AutoARIMA', 'AutoARIMA/MinTrace_method-ols'],
    level=[80]
)

# Since we are plotting a bottom time series
# the probabilistic and mean forecasts
# are the same
hplot.plot_series(
    series='Australia/Western Australia/Experience Perth/Visiting',
    Y_df=plot_df, 
    models=['y', 'AutoARIMA', 'AutoARIMA/BottomUp'],
    level=[80]
)

绘制层级关联的时间序列

hplot.plot_hierarchically_linked_series(
    bottom_series='Australia/Western Australia/Experience Perth/Visiting',
    Y_df=plot_df, 
    models=['y', 'AutoARIMA', 'AutoARIMA/MinTrace_method-ols', 'AutoARIMA/BottomUp'],
    level=[80]
)

# ACT only has Canberra
hplot.plot_hierarchically_linked_series(
    bottom_series='Australia/ACT/Canberra/Other',
    Y_df=plot_df, 
    models=['y', 'AutoARIMA/MinTrace_method-mint_shrink'],
    level=[80, 90]
)

参考文献