使用 pandas 检测具有许多缺失值的数据的离群值

Outlier detection with pandas for data with many missing values

我有几个有间隙的长期数据系列,想使用低通滤波器来检测异常值。理论上,(data-median) > 3 sigma 似乎是一个合适的测试,但这有两个问题:

  1. 数据系列太长且多变,因此整个系列仅使用一个中位数和标准差是行不通的,

  2. 使用 pandas.rolling_median 和 pandas.rolling_std 已经让我走得很远了,但现在数据差距成为一个问题,因为每个有效间隔结束时的滚动值都丢失了,因此没有可比较两者的值。

问题用以下程序说明(如果由于随机数据在第一次尝试期间捕获所有异常值,您可能需要再次 运行):

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
WINDOW = 72  # rolling window size
#generate random data series
dates = pd.date_range(start='1996-01-01 00:00', end='1996-05-31 23:00', freq='H')
values = np.random.random(size=len(dates))
# add random spikes
idx = np.random.randint(0, len(dates), size=40)
values[idx] = values[idx] * 3.
# set periods to missing
idx = np.random.randint(0, len(dates), size=20)
for i in idx:
    values[i:i+WINDOW] = np.nan
# create pandas series
s = pd.Series(values, index=dates)
s.plot(linestyle='None', marker='o')
# calculate rolling median and standard deviation
rm = pd.rolling_median(s, window=WINDOW, center=True)
rm.plot(linestyle='None', marker='x')
rs = pd.rolling_std(s, window=WINDOW, center=True)
(rm+3.*rs).plot()
# identify outliers as (series-median) > 3*stddev
n = (s-rm).apply(np.abs)
outliers = s[n > 3.*rs]
outliers.plot(linestyle='None', marker='^', color='r')
plt.show()

当你运行这个程序时你应该看到一些离群值没有用红色三角形标记,因为红线(中位数+ 3个标准差)不包含任何值。

所以,我的问题是:如何用相应的第一个和最后一个有效中值填充每个滚动间隔的开始和结束?

举个例子:假设我的滚动中位数是[nan, nan, 2, 4, 3, nan, nan],我希望得到[2, 2, 2, 4, 3, 3, 3]。到目前为止我只能想到一个麻烦的循环解决方案,但感觉不对。

您可以使用 ffill and bfill.

ffill 将通过 nans 向前传播最接近的值,而 bfill 将通过 nans 向后传播最接近的值。这些都是 fillna 的便捷方法,具有指定的方向。

s = pd.Series([np.nan, np.nan, 2, 4, 3, np.nan, np.nan])
s = s.ffill().bfill()
print(s)

产出

0    2.0
1    2.0
2    2.0
3    4.0
4    3.0
5    3.0
6    3.0
dtype: float64