原文:
towardsdatascience.com/how-to-deal-with-time-series-outliers-28b217c7f6c2
在本文中,我们将探讨:
不同类型的时间序列异常值
基于预测和估计的异常值检测方法
如何使用替换处理不想要的异常值
异常值的类型
异常值是显著偏离正常行为的观测值。
时间序列可能会因为某些不寻常且非重复的事件而出现异常值。这些异常值会影响时间序列分析,并误导从业者得出错误的结论或不良的预测。因此,识别和处理异常值是确保可靠的时间序列建模的关键步骤。
在时间序列中,异常值通常分为两种类型:加性异常值和创新性异常值。
加性异常值
加性异常值是相对于历史数据表现出异常高(或低)值的观测值。
加性异常值的例子是产品因促销或相关病毒性内容而销售的激增。有时这些异常值是由于数据收集错误造成的。加性效应与异常值对基础系统非持久性影响有关。异常值仅限于相应的观测值,在此之后,时间序列恢复其正常模式。
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/3ac5c55763af79b966cf7af2ab92f120.png
几个加性异常值的时间序列。图片由作者提供。
加性异常值可以跨越连续的观测值。这些也被称为子序列异常值或异常区域。
创新性异常值
创新性异常值类似于加性异常值,但具有持续性效应。异常值会对后续观测产生影响。一个常见的例子是,由于某些病毒性内容,网站访问量增加。网站可能会继续经历比平时更多的访问量,直到这种效应减弱。
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/538eefda9bc87bb405c3dea3b344dd19.png
包含创新性异常值的时间序列。图片由作者提供。
处理创新性异常值的一种方法是使用干预分析。例如,使用一个随时间衰减效应的虚拟变量。
与变化点的关系
异常值与变化的概念相关。一些观测值,称为变化点,标志着时间序列中结构变化的开始。
这些变化点与异常值相关但不同。异常值是相对于特定分布的异常观测值。变化点是具有分布变化的特征的结构性中断。
异常值的含义
你如何处理异常值取决于它们的性质和分析的目标。
来自噪声的异常值,如数据收集中的错误,是不需要的数据。这种类型的异常值在分析之前应该被移除或替换。
另一方面,一些异常值本身很有趣,并且很重要,需要预测。因此,移除它们可能会导致误导性的结论或过于乐观的预测。这种情况在各个领域都会发生,例如欺诈检测或能源。考虑一个能源需求的时间序列,其中在某个时期能源负荷激增。这种类型的异常值可能是由某些不寻常的事件(例如极端寒冷的天气)引起的。公用事业公司需要预测这种异常值,因此移除它们不是一个好主意。对这些观测值进行建模是平衡能源供需和防止停电的关键。
检测和处理异常值
在时间序列数据中检测异常值有几种方法。其中许多属于以下两种类别之一:基于预测或基于估计。
基于预测的检测
基于预测检测异常值涉及使用预测模型。目标是比较预测值与实际值。两者之间的大差异表明该观测值是异常值。
让我们通过以下时间序列来了解这在实践中是如何工作的:
fromdatasetsforecast.m4importM4 dataset,*_=M4.load('./data','Hourly')series=dataset.query(f'unique_id=="H1"').reset_index(drop=True)在前面的代码中,我们从 M4 数据集中获取了 id 为 H1 的时间序列。接下来,我们基于 statsforecast 构建了一个季节性简单预测模型:
fromstatsforecastimportStatsForecastfromstatsforecast.modelsimportSeasonalNaive# seasonal naive modelmodel=[SeasonalNaive(season_length=24)]# creating a statsforecast instancesf=StatsForecast(df=series,models=model,freq='H')# fitting the forecasting modelsf.forecast(h=1,level=[99],fitted=True)# getting insample predictionspreds=sf.forecast_fitted_values()在构建模型后,我们使用 _forecast_fittedvalues方法为训练样本获取预测区间。然后,我们将这些与实际值进行比较:
# outliers based on prediction intervalsoutliers=preds.loc[(preds['y']>=preds['SeasonalNaive-hi-99'])|(preds['y']<=preds['SeasonalNaive-lo-99'])]任何落在 99%预测区间之外的观测值都被视为异常值。
这里是异常值的图表:
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/a117b9f2b041c697b7ce5f52feda0f17.png
季节性简单模型检测到的异常值。图片由作者提供
您还可以使用预测的实际误差而不是区间。在这种情况下,当误差异常大时,会出现异常值。
基于估计的检测
基于估计的方法使用汇总统计来检测异常值。一个例子是 z 分数。其思路是通过减去平均值并除以标准差来标准化数据。然后,具有大 z 分数值的点是异常值。
这里有一个例子:
# values above/below 3 std deviationsthresh=3rolling_series=series['y'].rolling(window=24,min_periods=1,center=True)avg=rolling_series.mean()std=rolling_series.std(ddof=0)zscore=series['y'].sub(avg).div(std)m=zscore.between(-thresh,thresh)注意,平均数和标准差是使用滚动窗口计算的,以考虑时间序列中的时间依赖性。
另一种方法是使用时间序列分解方法,并在残差上检测异常值。让我们先使用 STL 获取残差:
fromstatsmodels.tsa.seasonalimportSTL stl=STL(series['y'].values,period=24,robust=True).fit()resid=pd.Series(stl.resid)注意,我们向 STL 传递了参数robust=True,因此模型可以容忍更大的误差。
然后,您可以使用标准的箱线图规则来检测异常值。例如,将低于第一四分位数 3 倍 IQR 的观测值或高于第三四分位数的观测值标记为异常。以下是操作步骤:
q1,q3=resid.quantile([.25,.75])iqr=q3-q1 is_outlier_r=~resid.apply(lambdax:q1-(3*iqr)<x<q3+(3*iqr))is_outlier_r_idx=np.where(is_outlier_r)[0]resid_df=resid.reset_index()resid_df['index']=pd.date_range(end='2021-12-01',periods=series.shape[0],freq='H')resid_df.columns=['index','Residual']这些异常值在残差序列中也很明显:
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/aa94b2aa0142c953d790087c08c0684a.png
使用箱线图规则在残差中检测异常值。图片由作者提供。
替换异常值
检测后,您可以通过用更合理的值替换它们来清理异常值。
您首先移除异常值,然后将问题转化为数据插补任务。
如您在之前的文章《如何处理时间序列缺失数据》中学习到的,处理时间序列插补有许多方法。这些包括:
前向或后向填充
移动平均数
线性插值
关键要点
时间序列异常值是显著偏离历史数据的观测值
异常值在持续性和意义上可能表现出不同的特征
存在几种异常值检测方法,包括基于预测和基于估计的方法
您可以使用数据插补技术替换不想要的异常值
相关文章
- 如何处理时间序列缺失数据
参考文献
[1] Tsay, Ruey S. “时间序列中的异常值、水平变化和方差变化。” 预测杂志 7.1 (1988): 1–20。
[2] 处理异常值和缺失值
[3] Blázquez-García, Ane, et al. “关于时间序列数据中异常值/异常检测的综述。” ACM 计算调查 (CSUR) 54.3 (2021): 1–33。
[4] Nixtla 异常检测教程