先决条件

本教程假定您对 StatsForecast 有基本了解。有关最小示例,请访问快速入门

引言

异常检测是时间序列预测中的一项重要任务。它涉及识别不遵循预期数据集模式的异常观测值。异常,也称为离群值,可能由多种因素引起,例如数据收集过程中的错误、数据底层模式的突然变化或意外事件。它们可能对许多预测模型造成问题,因为它们会扭曲趋势、季节性模式或自相关估计。因此,异常可能对预测的准确性产生重大影响,正因为如此,识别它们至关重要。此外,异常检测在不同行业中有许多应用,例如检测金融数据中的欺诈、监控在线服务的性能或识别能源使用中的异常模式。

学完本教程后,您将很好地理解如何使用StatsForecast 的概率模型检测时间序列数据中的异常。

大纲

  1. 安装库
  2. 加载和探索数据
  3. 训练模型
  4. 恢复样本内预测并识别异常

重要提示

一旦识别出异常,我们必须决定如何处理它。例如,我们可以将其删除或替换为另一个值。正确的处理方式取决于上下文,并且超出了本 notebook 的范围。删除异常可能会提高预测的准确性,但也可能低估数据中的随机性。

提示

您可以使用 Colab 交互式地运行此 Notebook

安装库

我们假定您已安装 StatsForecast。如果尚未安装,请参阅此指南了解如何安装 StatsForecast 的说明。

使用 pip install statsforecast 安装必要的软件包

pip install statsforecast -U

加载和探索数据

在本示例中,我们将使用M4 竞赛的每小时数据集。

import pandas as pd
df_total = pd.read_parquet('https://datasets-nixtla.s3.amazonaws.com/m4-hourly.parquet')
df_total.head()
unique_iddsy
0H11605.0
1H12586.0
2H13586.0
3H14559.0
4H15511.0

StatsForecast 的输入始终是采用长格式(long format)的数据框,包含三列:unique_iddfy

  • unique_id:(字符串、整数或类别)序列的唯一标识符。
  • ds:(时间戳或整数)格式为 YYYY-MM-DD 或 YYYY-MM-DD HH:MM:SS 的时间戳,或表示时间的整数索引。
  • y:(数值)我们希望预测的度量值。

从此数据集中,我们将选择前 8 个时间序列以减少总执行时间。您可以通过更改 n_series 的值来选择任意数量。

n_series = 8
uids = df_total['unique_id'].unique()[:n_series]
df = df_total.query('unique_id in @uids')

我们可以使用来自 utilsforecast 包的 plot_series 函数绘制这些序列。此函数有多个参数,下面将解释在此 notebook 中生成图表所需的参数。

  • df:一个 pandas 数据框,包含列 [unique_id, ds, y]。
  • forecasts_df:一个 pandas 数据框,包含列 [unique_id, ds] 和 models。
  • ids:包含我们要绘制的时间序列 ID 的列表。
  • level:要绘制的预测区间级别。
  • plot_anomalies:是否包含每个预测区间的异常值。
from statsforecast import StatsForecast
from utilsforecast.plotting import plot_series
plot_series(df)

训练模型

为了生成预测,我们将使用MSTL 模型,它非常适合此处使用的低频数据。我们首先需要从 statsforecast.models 导入它,然后需要实例化它。由于我们使用每小时数据,我们有两个季节周期:每 24 小时一次(每小时)和每 24*7 小时一次(每天)。因此,我们需要设置 season_length = [24, 24*7]

from statsforecast.models import MSTL
# Create a list of models and instantiation parameters 
models = [MSTL(season_length = [24, 24*7])]

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

  • models:上一步中定义的模型列表。
  • freq:一个字符串或整数,指示数据的频率。请参阅pandas 的可用频率
  • n_jobs:一个整数,指示并行处理中使用的任务数。使用 -1 选择所有核心。
sf = StatsForecast(
    models=models, 
    freq=1,
    n_jobs=-1,
)

现在我们将预测接下来的 48 小时。为此,我们将使用 forecast 方法,它需要以下参数

  • df:包含训练数据的数据框。
  • h:预测范围。
  • level:预测区间的置信水平。
  • fitted:返回样本内预测。

重要的是我们要选择一个 level 并设置 fitted=True,因为我们需要样本内预测及其预测区间来检测异常。

horizon = 48
levels = [99] 

fcst = sf.forecast(df=df, h=48, level=levels, fitted=True)
fcst.head()
unique_iddsMSTLMSTL-lo-99MSTL-hi-99
0H1749607.607223587.173250628.041196
1H1750552.364253521.069710583.658796
2H1751506.785334465.894977547.675691
3H1752472.906141423.114088522.698195
4H1753452.240231394.064394510.416067

我们可以使用之前提到的 plot_series 函数绘制预测结果。

plot_series(df, fcst)

恢复样本内预测并识别异常

在本例中,**异常**是指给定置信水平下(此处我们选择了 99%)样本内预测的预测区间之外的任何观测值。因此,我们首先需要使用 forecast_fitted_values 方法恢复样本内预测。

insample_forecasts = sf.forecast_fitted_values()
insample_forecasts.head()
unique_iddsyMSTLMSTL-lo-99MSTL-hi-99
0H11605.0605.098607584.678408625.518805
1H12586.0588.496673568.076474608.916872
2H13586.0585.586856565.166657606.007054
3H14559.0554.012377533.592178574.432576
4H15511.0510.153508489.733309530.573707

现在我们可以找到样本内预测的 99% 预测区间之上或之下的所有观测值。

anomalies = insample_forecasts[~insample_forecasts['y'].between(insample_forecasts['MSTL-lo-99'], insample_forecasts['MSTL-hi-99'])]
anomalies.head()
unique_iddsyMSTLMSTL-lo-99MSTL-hi-99
42H143613.0649.404871628.984672669.825069
47H148683.0662.245526641.825328682.665725
48H149687.0655.382320634.962122675.802519
100H1101507.0484.934230464.514031505.354428
110H1111451.0474.899006454.478808495.319205

我们可以通过设置 plot_series 函数的 levelplot_anomalies 参数来绘制异常值。

plot_series(forecasts_df=insample_forecasts, level=levels, plot_anomalies=True)

如果想仔细查看,我们可以使用 ids 参数选择特定的时间序列,例如 H10

plot_series(forecasts_df=insample_forecasts, level=[99], plot_anomalies=True, ids=['H10'])

在此,我们使用 MSTL 模型识别了数据中的异常,但 StatsForecast 中的任何概率模型都可以使用。我们还选择了样本内预测的 99% 预测区间,但也可以使用其他置信水平。