异常检测

在本笔记中,我们将实现时间序列数据中的异常检测

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

引言

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

在本教程结束时,您将对如何使用 StatsForecast 的概率模型在时间序列数据中检测异常有一个很好的理解。

大纲:

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

一旦识别出异常,我们必须决定如何处理它。例如,我们可以将其移除或替换为其他值。正确的处理方法依赖于具体背景,并超出了本笔记本的范围。移除异常可能会提高预测的准确性,但也可能低估数据中的随机性。

Tip

您可以使用 Colab 来交互式运行此 Notebook 在 Colab 中打开

安装库

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

使用 pip install statsforecast 安装必要的包。

pip install statsforecast -U

加载和探索数据

在这个例子中,我们将使用M4竞赛的每小时数据集。我们首先从datasetsforecast导入数据,您可以使用pip install datasetsforecast进行安装。

pip install datasetsforecast -U 
from datasetsforecast.m4 import M4 

加载数据的函数是 M4.load。它需要以下两个参数:

  • directory: (字符串)数据将被下载的目录。
  • group: (字符串)组名,可以是 YearlyQuarterlyMonthlyWeeklyDailyHourly

此函数返回多个输出,但只需要第一个带有目标序列的输出。

df_total, *_ = M4.load('./data', 'Hourly')
df_total.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

Stats

df_total['ds'] = df_total['ds'].astype(int)

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

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

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

  • df: 一个包含 [unique_id, ds, y] 列的 pandas 数据框。
  • forecasts_df: 一个包含 [unique_id, ds] 和模型的 pandas 数据框。
  • unique_ids: (list[str]) 一个包含我们想要绘制的时间序列的ID列表。
  • plot_random: (bool = True) 随机绘制时间序列。
  • plot_anomalies: (bool = False) 绘制每个预测区间的异常值。
  • engine: (str = plotly) 用于生成图表的库。静态图表也可以使用matplotlib。
from statsforecast import StatsForecast 
StatsForecast.plot(df, plot_random = False) 

训练模型

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

from statsforecast.models import MSTL

# 创建一个模型及其实例化参数的列表 
models = [MSTL(season_length = [24, 24*7])]

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

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

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

  • h: (int) 预测的时间范围。
  • level: (list[float]) 预测区间的置信水平。
  • fitted: (bool = False) 返回样本内预测值。

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

horizon = 48
levels = [99] 

fcst = sf.forecast(h = 48, level = levels, fitted = True)
fcst = fcst.reset_index()
fcst.head()
unique_id ds MSTL MSTL-lo-99 MSTL-hi-99
0 H1 749 615.943970 597.662170 634.225708
1 H1 750 559.297791 531.316650 587.278931
2 H1 751 515.693542 479.151337 552.235718
3 H1 752 480.719269 436.241547 525.197021
4 H1 753 467.146484 415.199738 519.093262

我们可以使用之前的 plot 方法绘制预测图。

StatsForecast.plot(df, fcst, plot_random = False) 

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

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

insample_forecasts = sf.forecast_fitted_values().reset_index()
insample_forecasts.head()
unique_id ds y MSTL MSTL-lo-99 MSTL-hi-99
0 H1 1 605.0 604.924500 588.010376 621.838623
1 H1 2 586.0 585.221802 568.307678 602.135925
2 H1 3 586.0 589.740723 572.826599 606.654846
3 H1 4 559.0 557.778076 540.863953 574.692200
4 H1 5 511.0 506.747009 489.832886 523.661133

我们现在可以找到所有在样本预测中超过或低于99%预测区间的观察值。

anomalies = insample_forecasts.loc[(insample_forecasts['y'] >= insample_forecasts['MSTL-hi-99']) | (insample_forecasts['y'] <= insample_forecasts['MSTL-lo-99'])]
anomalies.head()
unique_id ds y MSTL MSTL-lo-99 MSTL-hi-99
168 H1 169 813.0 779.849792 762.935669 796.763916
279 H1 280 692.0 672.638123 655.723999 689.552246
289 H1 290 770.0 792.015442 775.101318 808.929565
308 H1 309 844.0 867.809387 850.895203 884.723511
336 H1 337 853.0 822.427002 805.512878 839.341187

我们可以通过在plot方法中添加plot_anomalies = True参数来绘制异常值。

StatsForecast.plot(insample_forecasts, plot_random = False, plot_anomalies = True)

如果我们想更仔细地观察,可以使用 unique_ids 参数来选择一个特定的时间序列,例如 H10

StatsForecast.plot(insample_forecasts, unique_ids = ['H10'], plot_anomalies = True) 

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

Give us a ⭐ on Github