概率预测

import warnings
warnings.simplefilter('ignore')

import logging
logging.getLogger('statsforecast').setLevel(logging.ERROR)

在这个例子中,我们将实现预测区间

本教程假设对StatsForecast有基本的了解。有关最小示例,请访问快速开始

引言

当我们生成预测时,通常会产生一个称为点预测的单一值。然而,这个值并不能告诉我们与预测相关的不确定性。为了衡量这种不确定性,我们需要预测区间

预测区间是一组值,它表示预测在给定概率下可能取的范围。因此,95%的预测区间应该包含一个范围,这个范围以95%的概率包含实际未来值。概率预测旨在生成完整的预测分布。相反,点预测通常返回该分布的平均值或中位数。然而,在现实场景中,预测不仅最可能的未来结果,还应包括许多替代结果。

StatsForecast有很多模型可以生成点预测。它还提供可以生成相同点预测及其预测区间的概率模型。这些模型是随机数据生成过程,可以产生完整的预测分布。在本教程结束时,您将对StatsForecast中可用的概率模型有一个良好的理解,并能够使用它们生成点预测和预测区间。此外,您还将学习如何生成包含历史数据、点预测和预测区间的图表。

重要

尽管这两个术语经常被混淆,但预测区间与置信区间并不相同。

警告

在实际中,大多数预测区间过于狭窄,因为模型未考虑所有不确定性来源。关于这一点的讨论可以在 这里 找到。

大纲:

  1. 安装库
  2. 加载和探索数据
  3. 训练模型
  4. 绘制预测区间
Tip

您可以使用 Colab 交互式运行此笔记本 在 Colab 中打开

安装库

我们假设您已经安装了StatsForecast。如果没有,请查看本指南了解如何安装StatsForecast的说明。

使用pip install statsforecast安装必要的包

%%capture
pip install statsforecast -U

加载和探索数据

在这个例子中,我们将使用来自M4竞赛的每小时数据集。我们首先需要从一个URL下载数据,然后将其加载为pandas数据框。请注意,我们将分别加载训练数据和测试数据。我们还将把测试数据的y列重命名为y_test

import pandas as pd 

train = pd.read_csv('https://auto-arima-results.s3.amazonaws.com/M4-Hourly.csv')
test = pd.read_csv('https://auto-arima-results.s3.amazonaws.com/M4-Hourly-test.csv').rename(columns={'y': 'y_test'})
train.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
test.head()
unique_id ds y_test
0 H1 701 619.0
1 H1 702 565.0
2 H1 703 532.0
3 H1 704 495.0
4 H1 705 481.0

由于本笔记的目的是生成预测区间,我们将仅使用数据集中的前8个系列以减少总计算时间。

n_series = 8 
uids = train['unique_id'].unique()[:n_series] # 选择数据集中的前n_series个序列
train = train.query('unique_id in @uids')
test = test.query('unique_id in @uids')

我们可以使用来自StatsForecast类的statsforecast.plot方法绘制这些系列。该方法有多个参数,生成本笔记本中图形所需的参数如下所述。

  • df:一个包含[unique_id, ds, y]列的pandas数据框。
  • forecasts_df:一个包含[unique_id, ds]和模型的pandas数据框。
  • plot_random:bool = True。随机绘制时间序列。
  • models:List[str]。一个我们想要绘制的模型列表。
  • level:List[float]。一个我们想要绘制的预测区间列表。
  • engine:str = plotly。也可以是matplotlibplotly生成交互式图形,而matplotlib生成静态图形。
from statsforecast import StatsForecast

StatsForecast.plot(train, test, plot_random = False)
/Users/fedex/projects/statsforecast/statsforecast/core.py:21: TqdmExperimentalWarning: Using `tqdm.autonotebook.tqdm` in notebook mode. Use `tqdm.tqdm` instead to force console mode (e.g. in jupyter console)
  from tqdm.autonotebook import tqdm

训练模型

StatsForecast 可以高效地在不同的时间序列上训练多个 模型。大多数模型可以生成概率预测,这意味着它们可以同时产生点预测和预测区间。

在这个例子中,我们将使用 AutoETS 以及以下基准模型:

要使用这些模型,我们首先需要从 statsforecast.models 中导入它们,然后实例化这些模型。由于我们正在处理每小时的数据,因此在需要该参数的模型中,我们需要设置 seasonal_length=24

from statsforecast.models import (
    AutoETS, 
    HistoricAverage, 
    Naive, 
    RandomWalkWithDrift, 
    SeasonalNaive
)

# 创建一个模型及其实例化参数的列表 
models = [
    AutoETS(season_length=24),
    HistoricAverage(), 
    Naive(), 
    RandomWalkWithDrift(), 
    SeasonalNaive(season_length=24)
]

要实例化一个新的 StatsForecast 对象,我们需要以下参数:

  • df:包含训练数据的数据框。
  • models:在前一步中定义的模型列表。
  • freq:一个字符串,指示数据的频率。请参见 pandas 可用的频率
  • n_jobs:一个整数,用于指示并行处理时使用的作业数。使用 -1 选择所有核心。
sf = StatsForecast(
    df=train, 
    models=models, 
    freq='H', 
    n_jobs=-1
) 

现在我们准备生成点预测和预测区间。为此,我们将使用 forecast 方法,该方法需要两个参数:

  • h:一个整数,表示预测的时间范围。在这种情况下,我们将预测接下来的48小时。
  • level:一个浮点数列表,表示预测区间的置信水平。例如,level=[95] 表示值的范围应该以95%的概率包含实际的未来值。
levels = [80, 90, 95, 99] # 预测区间的置信水平 

forecasts = sf.forecast(h=48, level=levels)
forecasts = forecasts.reset_index()
forecasts.head()
unique_id ds AutoETS AutoETS-lo-99 AutoETS-lo-95 AutoETS-lo-90 AutoETS-lo-80 AutoETS-hi-80 AutoETS-hi-90 AutoETS-hi-95 ... RWD-hi-99 SeasonalNaive SeasonalNaive-lo-80 SeasonalNaive-lo-90 SeasonalNaive-lo-95 SeasonalNaive-lo-99 SeasonalNaive-hi-80 SeasonalNaive-hi-90 SeasonalNaive-hi-95 SeasonalNaive-hi-99
0 H1 701 631.889587 533.371826 556.926819 568.978882 582.874084 680.905090 694.800354 706.852356 ... 789.416626 691.0 582.823792 552.157349 525.558777 473.573395 799.176208 829.842651 856.441223 908.426575
1 H1 702 559.750854 460.738586 484.411835 496.524353 510.489288 609.012329 622.977295 635.089844 ... 833.254150 618.0 509.823822 479.157379 452.558807 400.573395 726.176208 756.842651 783.441223 835.426575
2 H1 703 519.235474 419.731232 443.522095 455.694794 469.729156 568.741821 582.776123 594.948853 ... 866.990601 563.0 454.823822 424.157379 397.558807 345.573395 671.176208 701.842651 728.441223 780.426575
3 H1 704 486.973358 386.979523 410.887451 423.120056 437.223480 536.723267 550.826660 563.059265 ... 895.510132 529.0 420.823822 390.157379 363.558807 311.573395 637.176208 667.842651 694.441223 746.426575
4 H1 705 464.697357 364.216339 388.240753 400.532959 414.705078 514.689636 528.861755 541.153992 ... 920.702881 504.0 395.823822 365.157379 338.558807 286.573395 612.176208 642.842651 669.441223 721.426575

5 rows × 47 columns

我们现在将预测结果及其预测区间与测试集进行合并。这将使我们能够生成每个概率模型的图表。

test = test.merge(forecasts, how='left', on=['unique_id', 'ds'])

绘制预测区间

为了绘制点和预测区间,我们将再次使用 statsforecast.plot 方法。请注意,现在我们还需要指定我们想要绘制的模型和水平。

自动ETS

sf.plot(train, test, plot_random = False, models=['AutoETS'], level=levels)

历史平均值

sf.plot(train, test, plot_random = False, models=['HistoricAverage'], level=levels)

天真的

sf.plot(train, test, plot_random = False, models=['Naive'], level=levels)

带漂移的随机游走

sf.plot(train, test, plot_random = False, models=['RWD'], level=levels)

季节性简单预测

sf.plot(train, test, plot_random = False, models=['SeasonalNaive'], level=levels)

从这些图中,我们可以得出结论,每个预测周围的不确定性取决于所使用的模型。对于相同的时间序列,一个模型可能预测出比其他模型更广泛的可能未来值范围。

参考文献

Rob J. Hyndman 和 George Athanasopoulos (2018). “预测原则与实践,统计预测的视角”

Give us a ⭐ on Github