【问题标题】:Replace outliers with neighbour-Value用邻居值替换异常值
【发布时间】:2019-03-14 08:50:42
【问题描述】:

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

基础数据还是不错的。我只想删除与“当前平均值”相差太远的所有内容。我尝试使用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()

我试图在网上搜索一个好的解决方案,但找不到。删除数据点应该不难,跳过屋顶,不是吗?

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

编辑2:

我通过改进数据集创建解决了异常值过多的问题。

它的核心:

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

基本上,它的作用是检查值的变化是否大于 30,如果是,则删除最后一个值并用倒数第二个替换 is。不是很壮观,但正是我需要的。我使用了其中一个答案,因为它更漂亮。非常感谢你们。

【问题讨论】:

  • 什么将一个点定义为“离当前平均值太远”?
  • 你有数据可以下载吗?
  • 滚动平均值不是您想要的。平均值会受到异常值的影响,从图中可以看出,窗口大小可以改变特征的位置,如峰。您希望对数据进行更多过滤。中值过滤器似乎可以很好地工作。
  • 我添加了数据集的下载链接。离得太远可能意味着与数据集中最后一个值的差异大于 10。
  • 我同意中值过滤器会更好。您也可以尝试使用低通滤波器。

标签: python pandas numpy matplotlib


【解决方案1】:

让我们尝试使用scipy.signal see docs

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)

输出:

【讨论】:

  • 感谢您的回答。难道不能完全忽略异常值吗?正确的数据就是这样扭曲的。
  • @ArturMüllerRomanov 您必须稍微使用一下 scipy 信号库或了解滤波器理论才能得到您正在寻找的东西。例如,大约 -40 左右的较低读数是真实值还是噪音?您是否希望删除所有介于 -45 和 -30 之间的值?抱歉,你已经达到我的极限了。
  • 出于好奇,投票者愿意解释吗?
  • 也感谢分享这个解决方案,我会更仔细地研究这些 scipy.signal 函数!
【解决方案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()

请注意,滚动平均值在周长低至 20 的情况下无法工作,因为离群点的权重 (5%) 不可忽略,并且总是会产生很大的偏差...

【讨论】:

  • 感谢分享...我会更仔细地研究 savgol。 +1
猜你喜欢
  • 2019-10-07
  • 2018-01-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多