在R中使用异常化检测异常

本文概述

当我们谈论异常时, 我们考虑的是异常值或异常事件的数据点。在小数据集中, 识别这些事件很容易, 并且可以通过一些简单的分析图(例如箱线图)来完成。但是, 当切换到大数据集时, 情况将同时变得复杂, 尤其是在时间序列的情况下。时间序列是当分析显示趋势或季节性时, 在一个时间段内的固定时间间隔内捕获的数据。在这些情况下识别异常是一个棘手的方面。

然后是用于时间序列分析中异常检测的异常处理软件包, 它是一种整齐的异常检测算法, 它基于时间, 并且可以在一个到多个时间序列之间进行扩展。

有一些可用的软件包和方法有助于其开发, 或者可以说它是可用资源与可伸缩方法的结合。

帮助的开源工作如下:

  • Twitter的AnomalyDetection软件包:在Github上可用(cran anaomalyDetection是另一项工作)。
  • Rob Hyndman的Forecast :: tsoutliers()函数可通过预测包获得。
  • Javier Lopez-de-lacalle在CRAN上的包裹tsoutliers。

所有这些软件包和功能都用于集成到可伸缩的工作流中。

谈到匿名化的工作流程, 它分为三个部分:

  • 使用time_decompose()分解时间序列。
  • 用anomalize()异常检测余数。
  • time_recompose()异常上下限转换。

时间序列分解

第一步是使用time_decompose()进行时间序列分解。需要对特定组进行检测的测量值或数值分解为观察到的四列, 分别是季节, 趋势和余数。用于分解的默认方法是stl, 这是利用Loess平滑器的季节性分解。

黄土回归是用于平滑波动时间序列的最常用方法, 它适合于局部邻域中的多元回归, 你也可以说对数据进行了分割并将回归应用于每个部分, 这在时间序列中很有用, 因为我们知道时间界限, 在这种情况下是X变量。在趋势支配时间序列的季节性的情况下, 此方法效果很好。

这里的趋势是发生在许多观察结果上的长期增长, 而季节性则是在一分钟或一小时或每周的每日周期中发生的周期性变化。

还有第二种技术可用于基于中位数的时间序列中的季节性分解, 这是Twitter方法, 也使用了AnomalyDetection程序包。与删除季节性成分的STL相同。区别在于消除趋势是, 它使用数据的分段中位数(按指定间隔分割一个或几个中位数), 而不是拟合更平滑。在季节性主导时间序列趋势的情况下, 此方法效果很好。

让我们谈谈time_decompose()函数的输出, 如上所述, 它产生4列:

  • 观察到的:实际值。
  • 季节:季节性或周期性趋势。默认值为每周季节性。
  • 趋势:长期趋势。默认值为3个月。
  • 余数:用于分析异常值。这只是观察到的减去季节和趋势。

time_decompose()函数包含一个参数合并, 通过将其设置为TRUE, 我们可以将原始数据与产生的列一起保留。

# Using data package provided in the anomalize package and taking single time series of package purrr

purrr_package = tidyverse_cran_downloads%>%
  filter(package == "purrr")%>%
  ungroup()

purrr_anomaly  = purrr_package %>%
  time_decompose(count)

purrr_anomaly%>% glimpse()
## Observations: 425
## Variables: 5
## $ date      <date> 2017-01-01, 2017-01-02, 2017-01-03, 2017-01-04, 201...
## $ observed  <dbl> 550, 1012, 1515, 1702, 1696, 1613, 860, 796, 2008, 2...
## $ season    <dbl> -2158.8893, 692.6571, 1087.5708, 1052.3294, 939.9377...
## $ trend     <dbl> 1496.712, 1511.009, 1525.307, 1539.604, 1553.901, 15...
## $ remainder <dbl> 1212.1777, -1191.6661, -1097.8773, -889.9334, -797.8...

检测剩余部分的异常

时间序列分析完成后, 其余部分具有执行异常检测所需的特性, 从而再次创建了三个新列。

  • restder_l1:余数的下限。
  • restder_l2:余数的上限。
  • anaomaly:列告诉观察结果是否为异常。

异常是扭曲分布的高杠杆点。异常处理实现了两种抵制高杠杆点的方法:

  • IQR:内部四分位数范围
  • GESD:广义极端学生偏差测试

IQR

这是在预测包的tsoutliers()函数中使用的类似方法。在IQR中, 采用分布, 并采用25%和75%的内部四分位数范围来确定其余部分的分布。默认情况下, 将限制设置为高于和低于内部四分位数范围的3倍, 超出限制的任何余数都被视为异常。

可持续发展教育

在GESD中, 将逐步评估异常情况, 以消除最严重的违规者, 然后重新计算测试统计信息和关键值, 或者更简单地说, 你可以说在以迭代方式识别异常之后重新计算了范围。

IQR和GESD都有其优点和缺点, IQR相对较快, 因为IQR中没有回路, 但它不如GESD准确, 因为异常使GESD中去除的中位数偏斜。

purrr_anomaly = purrr_anomaly%>%
  anomalize(remainder)

purrr_anomaly%>% glimpse()
## Observations: 425
## Variables: 8
## $ date         <date> 2017-01-01, 2017-01-02, 2017-01-03, 2017-01-04, ...
## $ observed     <dbl> 550, 1012, 1515, 1702, 1696, 1613, 860, 796, 2008...
## $ season       <dbl> -2158.8893, 692.6571, 1087.5708, 1052.3294, 939.9...
## $ trend        <dbl> 1496.712, 1511.009, 1525.307, 1539.604, 1553.901, ...
## $ remainder    <dbl> 1212.1777, -1191.6661, -1097.8773, -889.9334, -79...
## $ remainder_l1 <dbl> -4330.511, -4330.511, -4330.511, -4330.511, -4330...
## $ remainder_l2 <dbl> 4400.459, 4400.459, 4400.459, 4400.459, 4400.459, ...
## $ anomaly      <chr> "No", "No", "No", "No", "No", "No", "No", "No", "...

上下限异常转换

工作流的最后一步是使用time_recompose在观测值周围创建上下限。它将季节, 趋势, remainder_l1和remainder_l2重组为新的限制, 这些限制是:

  • recomposed_l1:观测值周围的离群值的下限。
  • recomposed_l2:观察值周围的离群值的上限。
purrr_anomaly = purrr_anomaly%>%
  time_recompose()

purrr_anomaly%>% glimpse()
## Observations: 425
## Variables: 10
## $ date          <date> 2017-01-01, 2017-01-02, 2017-01-03, 2017-01-04, ...
## $ observed      <dbl> 550, 1012, 1515, 1702, 1696, 1613, 860, 796, 200...
## $ season        <dbl> -2158.8893, 692.6571, 1087.5708, 1052.3294, 939....
## $ trend         <dbl> 1496.712, 1511.009, 1525.307, 1539.604, 1553.901...
## $ remainder     <dbl> 1212.1777, -1191.6661, -1097.8773, -889.9334, -7...
## $ remainder_l1  <dbl> -4330.511, -4330.511, -4330.511, -4330.511, -433...
## $ remainder_l2  <dbl> 4400.459, 4400.459, 4400.459, 4400.459, 4400.459...
## $ anomaly       <chr> "No", "No", "No", "No", "No", "No", "No", "No", ...
## $ recomposed_l1 <dbl> -4992.689, -2126.845, -1717.634, -1738.578, -183...
## $ recomposed_l2 <dbl> 3738.281, 6604.125, 7013.336, 6992.392, 6894.298...

绘制异常

purrr_anomaly%>%
  plot_anomaly_decomposition()+
  ggtitle("Plotting Anomalies")
在R中使用异常化检测异常1

如果不调整完全依赖于数据的参数, 则对异常检测器进行建模是不完整的。

让我们开始调整参数, 以便工作流的每个级别的参数都不同, 因为工作流的每个级别都在执行自己的任务。

调整分解参数

如前所述, 趋势和季节性是分解时间序列的基础, 因此还应该对函数time_decompose的频率和趋势进行调整。默认情况下, 值是自动分配的, 这两种方法(STL, Twitter)的频率均为7天, 趋势图STL为91天, Twitter为85天。

你可以根据自己的喜好调整两个参数或单个参数, 但是在进行调整之前请仔细看一下, 因为无视更改可能会过度拟合或不适当地分解过程。

purrr_package %>%
  time_decompose(count, frequency = "auto", trend = "2 weeks")%>%
  anomalize(remainder)%>%
  plot_anomaly_decomposition()+
  ggtitle("Trend = 2 Weeks / Frequency = auto ")
在R中使用异常化检测异常2

调整参数以进行异常检测

如上所述, 这里是根据余数计算极限值来确定异常, 以对异常值进行分类。 alpha和max_anoms是控制anomalize()函数的两个参数。用简单的话来说, 默认情况下, 将alpha限制的范围设置为0.05, 减小其值将增加范围的大小, 因此很难使一个点成为异常。

purrr_package%>%
  time_decompose(count)%>%
  anomalize(remainder, alpha = 0.05)%>%
  time_recompose()%>%
  plot_anomalies(time_recompose = T)+
  ggtitle("alpha = 0.05")
在R中使用异常化检测异常3
purrr_package%>%
  time_decompose(count)%>%
  anomalize(remainder, alpha = 0.025)%>%
  time_recompose()%>%
  plot_anomalies(time_recompose = T)+
  ggtitle("alpha = 0.025")
在R中使用异常化检测异常4

max_anom参数控制可能为异常的数据的百分比。如果alpha太难调整, 并且你想关注最激进的异常, 则此参数很有用。

purrr_package%>%
  time_decompose(count)%>%
  anomalize(remainder, alpha = 0.2, max_anoms = 0.2)%>%
  time_recompose()%>%
  plot_anomalies(time_recompose = T)+
  ggtitle("20% anomaly Allowed")
在R中使用异常化检测异常5
purrr_package%>%
  time_decompose(count)%>%
  anomalize(remainder, alpha = 0.2, max_anoms = 0.05)%>%
  time_recompose()%>%
  plot_anomalies(time_recompose = T)+
  ggtitle("5% anomaly Allowed")
在R中使用异常化检测异常6
  • 详细标称所有功能参考
  • 有趣的博客以及所有详细信息

如果你想了解有关R的更多信息, 请参加srcmini的时间序列分析入门课程。

微信公众号
手机浏览(小程序)
0
分享到:
没有账号? 忘记密码?