用邻近数值替换异常值

3

我有一个带有一些异常值(错误的测量数据)的图表:

enter image description here

基础数据不错,但我想删除所有与“当前平均值”相差太远的内容。我尝试使用pd.rolling().mean(),但没有令人满意的结果:
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

data = np.genfromtxt('shard_height_plot.csv', delimiter = ',')
df = pd.DataFrame(data)
df.set_index(0, inplace = True)
df2 = df.rolling(20).mean()

plt.plot(df)
plt.plot(df2)
plt.show()

enter image description here

我试图在网上寻找一个好的解决方案,但是没有找到。删除掉突然暴增的数据点不应该很难吧?

编辑:数据文件可以在这里下载:https://ufile.io/pviuc

编辑2:

我通过改进我的数据集创建来解决了过多离群值的问题。

核心内容:

if abs(D - D_List[-2]) > 30:
            D = D_List[-2]
            D_List.pop()
            D_List.append(D)

基本上,这个程序会检查一个值的变化是否大于30,如果是,它将删除最后一个值并用倒数第二个值替换它。虽然不是非常惊人,但正是我所需要的。我使用了其中一个答案,因为它更加美观。非常感谢你们。

enter image description here


1
什么定义了一个点被视为“偏离当前平均值太远”? - vielkind
1
你有可供下载的数据吗? - Scott Boston
1
滚动均值不是你想要的。平均值会被你的异常值所偏移,而且从你的图表中可以看出,窗口大小会改变峰值等特征的位置。你需要对数据进行更多的过滤。中位数滤波器似乎是一个不错的选择。 - ALollz
我添加了一个含有数据集的下载链接。如果差距太大,可能意味着与数据集中最后一个值相差大于10。 - Artur Müller Romanov
1
我同意使用中值滤波器会更好。您也可以尝试使用低通滤波器。 - busybear
2个回答

2

让我们尝试使用scipy.signal(请参见文档)

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from scipy import signal

data = np.genfromtxt('shard_height_plot.csv', delimiter = ',')
df = pd.DataFrame(data)
df.set_index(0, inplace = True)
df2 = df.rolling(20).mean()

b, a = signal.butter(3, 0.05)
y = signal.filtfilt(b,a, df[1].values)

df3 = pd.DataFrame(y, index=df2.index)


plt.plot(df, alpha=.3)
plt.plot(df2, alpha=.3)
plt.plot(df3)
plt.show()

输出:

这里输入图像描述

使用medfilt:

y = signal.medfilt(df[1].values)

输出:

在此输入图像描述


(注:该段内容为HTML代码,无需翻译)

谢谢您的回答。完全忽略异常值不是可能的吗?这样会扭曲正确的数据。 - Artur Müller Romanov
@ArturMüllerRomanov,你需要稍微玩一下scipy信号库或者了解一下滤波器理论才能得到你想要的精确结果。例如,在大约-40左右的那个较低读数,是实际值还是噪声?你是否希望将所有介于-45和-30之间的值都去除?抱歉,这已经超出了我的能力范围。 - Scott Boston
1
有点好奇,downvoter 能否解释一下? - Scott Boston
1
谢谢分享这个解决方案,我会更仔细地研究这些scipy.signal函数! - user10348617

2
有许多方法可以使曲线平滑(滚动均值、GAM、平滑样条等),我最喜欢的是Savitzky-Golay方法。 其工作原理如下:在将数据点y周围的一个小窗口回归到一个多项式(最小二乘法)之后,它使用该多项式来获取您的数据点^y的估计值。然后,窗口向前移动一个数据点。
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import savgol_filter

x = np.linspace(0,5,150)
y = np.cos(x) + np.random.random(150) * 0.15
yhat = savgol_filter(y, 49, 3)

plt.plot(x,y)
plt.plot(x,yhat, color='red')
plt.show()

enter image description here

请注意,在边长仅为20的情况下,滚动平均无法起作用,因为异常点将具有非常重要的权重(5%),并且总会导致很大的偏差...

谢谢分享...我会更仔细地看一下savgol。+1 - Scott Boston

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接