时间序列数据的异常检测模型
在机器学习中,时间序列是在一定时间内连续排列的一系列数据点。这个时间可以是几分钟、几小时、几天、几周或几年。时间序列模型分析数据集并提取有意义的数据特征。
这使得模型从数据中理解模式并获得有用的见解。该模型使用所获得的知识来进行未来预测。在时间序列建模中,我们使用图表和图示来显示预测值。
时间序列的应用如下。天气预报,股票价格预测,外汇交易,科学和工程领域。
在建立时间序列模型时,数据集可能有异常值或离群值。异常值是指偏离正常行为的观测值或数据点。当数据集中的异常点未被发现时,它们会损害模型的性能。在本教程中,我们将使用Facebook Prophet来建立一个异常检测模型。
前提条件
要跟上本教程,读者应该。
- 有一些关于[时间序列]的知识。
- 理解[python中的时间序列分解]。
- 知道如何建立一个[时间序列模型]。
- 使用[Google Colab笔记本]。
时间序列数据集
在本教程中,我们将使用纽约出租车数据集。该数据集以半小时为单位,记录了6个月。该数据集显示了纽约市每半小时的活跃出租车司机的数量。
我们将使用这个数据集来建立一个使用Facebook先知的时间序列模型。然后我们将使用该模型来预测纽约活跃的出租车司机的数量。
在模型做出预测后,我们将分析预测的结果。这将使我们能够发现异常/异常值。通过使用散点图,我们将能够直观地看到这些异常值。异常值是指偏离预期预测值的数值(可能是极高或极低)。
导入软件包
让我们来导入本教程中要使用的包。
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import os
from datetime import datetime
import plotly.express as px
这些包对异常检测很重要。每个包的功能如下。
matplotlib:Matplotlib是一个可视化包。我们使用Matplotlib来绘制线图、数字和图表。
numpy:Numpy将把时间序列数据集转换成数组。它还使我们能够对数组进行数学运算。
pandas:Pandas执行数据分析和操作。
os:它使我们能够在Google Colab中与操作系统互动。
datetime:它将我们数据集中的时间戳转换为DateTime数据类型。时间序列模型只适用于时间列的DateTime数据类型。
plotly.expression:它能绘制出更多的交互式图表。Plotly允许我们放大我们的图表,这使我们能够获得更多的洞察力。
加载数据集
要加载数据集,请使用此代码。
df = pd.read_csv('https://raw.githubusercontent.com/numenta/NAB/master/data/realKnownCause/nyc_taxi.csv')
要查看数据集的结构,使用这段代码。
df
数据集的输出如下图所示。

从图片上看,我们的数据集有两列。timestamp 和value 列。timestamp 列有半小时的间隔。value 列显示了每半小时的活跃出租车司机的数量。该数据集还有10320 数据样本。
我们需要使用以下代码将timestamp 列转换为datetime 数据类型。
df['timestamp'] = pd.to_datetime(df['timestamp'])
将数据集改变为以小时为单位的数据集
该数据集有10320 数据样本。这个数值是巨大的,它可能很难被可视化。我们需要对数据集进行重新取样,并将其改为每小时的间隔。这将把观测值减少到一半,使其更容易被视觉化。
df = df.set_index('timestamp').resample("H").mean()
要查看重新取样的数据集,请使用这段代码。
df
输出显示如下。

从上面的图片来看,我们对数据集进行了重新采样,新的数值是5160。
绘制折线图
我们将使用折线图来可视化这个数据集。我们将使用plotly.express 库来绘制这个图。
fig = px.line(df.reset_index(), x='timestamp', y='value', title='NYC Taxi Demand')
fig.update_xaxes(
rangeslider_visible=True,
rangeselector=dict(
buttons=list([
dict(count=1, label='1y', step='year', stepmode='backward'),
dict(count=2, label='3y', step='year', stepmode='backward'),
dict(count=3, label='5y', step='year', stepmode='backward'),
dict(step='all')
])
)
)
fig.show()
从上面的代码中,我们将X轴初始化为timestamp 。y轴是value 。而折线图的标题是NYC Taxi Demand 。然后我们添加标签,可以显示1年、3年或5年内的数据点。
该图如下所示。

上图显示了从2014年7月到2015年1月的活跃出租车数量。
让我们用这个数据集来建立时间序列模型。
开始使用Facebook Prophet
首先,我们用以下命令安装Facebook Prophet。
!pip install fbprophet
安装完Facebook Prophet后,我们用下面的代码导入它。
from fbprophet import Prophet
让我们重新命名我们的数据集列。
taxi_df = df.reset_index()[['timestamp', 'value']].rename({'timestamp':'ds',
'value':'y'},
axis='columns')
在上面的代码中,我们将timestamp 列重命名为ds ,value 列重命名为y 。要检查重命名后的数据集的结构,请使用此代码。
taxi_df.head()
输出结果如下所示。

数据集的拆分
我们需要将我们的数据集分成两组。一套用于训练时间序列模型,另一套用于测试模型。
train_dataset = taxi_df[(taxi_df['ds']>='2014-07-01')&(taxi_df['ds']<='2015-01-27')]
test_dataset = taxi_df[(taxi_df['ds']>'2015-01-27')]
从上面的代码来看,2014-07-01 和2015-01-27 之间的数据集是训练集。测试集包含超过2015-01-27 的时间戳值。该模型将使用测试数据集来进行预测。
让我们来初始化我们的模型。
初始化模型
我们将使用以下代码初始化我们的模型。
model = Prophet(changepoint_range=0.95)
我们使用changepoint_range=0.95 来增加我们输出的置信区间。置信区间决定了一个预测是对还是错。置信区间增加得越高,模型做出准确预测的机会就越大。
让我们把模型拟合到我们的训练数据集上。
模型拟合
我们将模型拟合到训练数据集上,以便模型能够从中学习。
model.fit(train_dataset)
这段代码将训练模型。训练结束后,模型就可以进行未来的预测了。
使用测试数据集进行预测
测试数据集包含过去的时间戳值2015-01-27 。测试集有120个小时。我们要对测试数据集中的120个小时进行预测。
future = model.make_future_dataframe(periods=120, freq='H')
future.tail()
从上面的代码来看,periods=120 显示了测试数据集中记录的120个小时。freq='H' 将给出以小时为单位的预测值。future.tail() 将显示ds 列的最后五行。它显示了模型将预测的最后五个小时。
最后五行显示如下。

要进行这些预测,请使用这段代码。
forecast = model.predict(future)
forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].tail()
从上面的代码中,我们已经初始化了各种方法和列。
model.predict:它是进行实际预测的方法。ds:它是包含未来120小时的时间戳值的列。yhat:包含模型预测后的预测值的列。yhat_lower:包含置信区间的底值的列。yhat_upper:它是包含置信区间上限值的列。
如果你运行上面的代码,它将提供以下输出。

上面的图片显示了不同的预测值。我们现在可以绘制一个图来显示预测值。
绘制图表
要绘制预测值,请使用这段代码。
digram1 = model.plot(forecast)
这将输出以下图表。

从上面的图片中,黑色的点是预测值。我们现在需要分析这些值,以检测这些值中的异常情况。
分析预测值
为了进一步了解这些预测结果,我们在预测数据框中增加两列。到目前为止,预测数据框有四列。我们需要添加一个error 列和uncertainty 列。这两列将有助于检测异常值/离群值。
误差列
我们用这一列来寻找实际的出租车数量和预测的出租车数量之间的差异。实际值保存在y 列,预测值保存在yhat 列。
我们使用以下代码添加误差列。
outcome['error'] = outcome['y'] - outcome['yhat']
不确定性列
我们用这一列来找出置信区间(yhat_upper 和yhat_lower)之间的差异。
我们使用下面的代码添加不确定性列。
outcome['uncertainity'] = outcome['yhat_upper'] - outcome['yhat_lower']
要查看新的数据框架,请使用此代码。
outcome.head()
新的数据框架如下图所示。

我们现在使用这些列来检测异常情况。
检测异常情况
我们将使用以下逻辑来识别异常情况。
outcome['anomaly'] = outcome.apply(lambda x: 'Yes' if(np.abs(x['error'])>1.5*x['uncertainity']) else 'No',axis=1)
从上面的代码来看,如果绝对误差值(误差)大于1.5乘以不确定度值,那么预测值yhat ,就是一个异常。outcome.apply 方法将应用该逻辑并检测异常。
我们现在可以创建一个散点图来显示检测到的异常情况。
散点图
要创建一个散点图,请使用这个代码段。
fig = px.scatter(outcome.reset_index(), x='ds', y='y',
color='anomaly',title='NYC Taxi Demand')
fig.update_xaxes(
rangeslider_visible=True,
rangeselector=dict(
buttons=list([
dict(count=1, label='1y', step='year', stepmode='backward'),
dict(count=2, label='3y', step='year', stepmode='backward'),
dict(count=3, label='5y', step='year', stepmode='backward'),
dict(step='all')
])
)
)
fig.show()
从上面的代码中,我们已经将ds 作为X轴,y 作为Y轴。散点图的标题是NYC Taxi Demand 。另外,检测到的异常情况将被赋予不同的颜色。当代码被执行时,它输出以下图表。

在上图中,检测到的异常点有一个红色的颜色。正常数据点的颜色为蓝色。散点图帮助我们检测了数据集中的异常值。
总结
在本教程中,我们学习了时间序列数据的异常检测。我们使用纽约出租车数据集来训练我们的模型。使用Facebook Prophet库,我们建立了一个时间序列模型。该模型被用来预测纽约活跃的出租车司机的数量。然后,我们使用散点图来检测我们数据集中的异常情况。利用这个教程,读者应该可以检测出时间序列数据的异常情况。