从 numpy 数组中删除错误但合理的值

Posted

技术标签:

【中文标题】从 numpy 数组中删除错误但合理的值【英文标题】:Remove bad but plausible values from numpy array 【发布时间】:2018-03-24 14:56:25 【问题描述】:

我有一个基本的 numpy 值数组(称为时间序列,如下所示)。略高于 0 的值通常表示错误的值(理想情况下,它们本来是 Nan'd 但唉......),所以我想将它们从数组中删除。我的问题是,对于时间序列的其他实例,这些 0 左右的值可能是合理的。仅供参考,这些值是海面温度测量值,如果测量值是在极地附近或极地地区进行的,则接近 0 的值是合理的。

我的问题是:有没有一种聪明的方法可以删除这些数据点?我曾考虑过使用 np.diff 尝试在数据中定位“步骤变化”,但我似乎没有得到任何结果。我还考虑过使用统计信息,例如围绕时间序列平均值的窗口,但是因为这些值是双峰的,所以这是行不通的;例如,在下面的情况下,平均值将在 9 左右——这并不代表真正的平均值(即,删除了坏数据)——因此围绕这个值的窗口会删除所有好的值。

array([[ 17.7804203 ],
   [  0.08901367],
   [  0.08901367],
   [  0.08901367],
   [  0.08901367],
   [ 17.7335453 ],
   [ 17.72670937],
   [ 17.72670937],
   [ 17.75502968],
   [ 17.81459999],
   [ 17.89565468],
   [ 17.98159218],
   [  0.08901367],
   [  0.08901367],
   [  0.08901367],
   [  0.08901367],
   [  0.08901367],
   [  0.08901367],
   [  0.08901367],
   [  0.08901367],
   [  0.08901367],
   [  0.08901367],
   [  0.08901367],
   [ 17.9210453 ]], dtype=float32)

【问题讨论】:

没有先验模型信息,您无能为力。而且这个模型信息没有正式说明。所以这个例子或多或少有 2 个峰值(我的措辞不好/不正式;想象一个数轴,在某个 x 轴值上有很多点;还有一些在其他 x 轴值)。总是这样吗?零附近的一是恒定的。总是这样吗?如是;也许使用一些一维聚类来获得这两个(平均值)值;然后检查两者的方差(使用来自聚类的类信息)并丢弃具有较低方差的类的所有值。 您基本上需要一维clustering 算法来对您的双峰(多峰)数据进行分类。我是DBSCAN 的特别粉丝,因为不需要事先知道存在多少组,并且能够在集群中识别主要成员和异常值。 感谢您的建议。我的一个想法是找到小于 0.1 的重复值(因为错误值在给定数组中似乎相同)。然后根据具体情况删除这些值。但这仍然不是一个完美的解决方案 与错误的接近零值在同一个数组中的似是而非的接近零值,还是在我们不必担心的其他数组中? 【参考方案1】:

这里有一些 hacky 演示,试图向 this answer(SO 的集群专家)学习一些东西。

我改变了一些东西,不做任何保证:

标记方法 重要:我在这里偏离了他的基本理论(他的方法应该更好)! 我只是根据最近的中心进行标记(如 kmeans) argrelextrema 的比较器(对于没有唯一值的数据需要!) 使用启发式带宽选择(不设置常数!)

这段代码使用kernel-density-estimation获得一个自动选择k的一维聚类。现在听起来,你不想总是使用k=2,但这里更通用。

所以将它用于您的数据:

import numpy as np
from scipy.signal import argrelextrema
from scipy.spatial.distance import cdist
from sklearn.neighbors.kde import KernelDensity
import matplotlib.pyplot as plt
import matplotlib.cm as cm

x = np.array([[ 17.7804203 ],
   [  0.08901367],
   [  0.08901367],
   [  0.08901367],
   [  0.08901367],
   [ 17.7335453 ],
   [ 17.72670937],
   [ 17.72670937],
   [ 17.75502968],
   [ 17.81459999],
   [ 17.89565468],
   [ 17.98159218],
   [  0.08901367],
   [  0.08901367],
   [  0.08901367],
   [  0.08901367],
   [  0.08901367],
   [  0.08901367],
   [  0.08901367],
   [  0.08901367],
   [  0.08901367],
   [  0.08901367],
   [  0.08901367],
   [ 17.9210453 ],])
   # [5.4],
   # [5.41],
   # [5.3],
   # [5.35]])

np.random.shuffle(x)                                             # just for demo

kde = KernelDensity(kernel='gaussian').fit(x)               # bandwith heuristic
s = np.linspace(np.amin(x), np.amax(x))
e = kde.score_samples(s.reshape(-1,1))

ma = argrelextrema(e, np.greater_equal )[0]
s_ma = np.array([s[ma[i]] for i in range(len(ma))])           # cluster centers
n_clusters = len(s_ma)                                        # n_clusters

# class labeling
Y = cdist(x, s_ma[np.newaxis].T)
labels = np.empty(len(x), dtype=int)
for x_ind in range(len(x)):
    labels[x_ind] = np.argmin(Y[x_ind, :])

# plot classification
xs = np.arange(len(x))
colors = cm.rainbow(np.linspace(0, 1, n_clusters))
for label in range(n_clusters):
    inds = np.where(labels == label)[0]
    plt.scatter(xs[inds], x[inds], color=colors[label], s=40)
plt.show()

输出这个分类(记住:我置换了 x 值):

现在让我们在 5 左右添加 4 个新点(因为我很懒,所以将它们添加到最后,我使用了提到的排列)。只需取消注释代码中的这些行即可。

输出:

所以在第一种情况下,我们获得了两个集群,现在我们获得了三个(我发誓:n_clusters=3 尽管我的 matplotlib 代码以某种方式改变了颜色......)!

随意玩弄这个。您可能会在评论中使用我的方法,使用标签来提取类,计算方差并丢弃最低的类。但这当然取决于您的任务。

例如最后添加的这段代码:

# Calculate variance for each class
variances = np.array([np.var(x[np.where(labels == i)[0]]) for i in 
range(n_clusters)])
print(np.hstack([variances[np.newaxis].T, s_ma[np.newaxis].T]))

会输出:

[[  1.92592994e-34   8.90136700e-02]  # variance | cluster_mean
 [  1.92500000e-03   5.20117896e+00]
 [  8.05793565e-03   1.79815922e+01]]

在您的情况下可能被解释为:扔掉 0 类(最小方差或一些阈值检查:使用 var

【讨论】:

感谢@sascha 的建议。我会玩弄你的想法,看看它对我有什么影响。

以上是关于从 numpy 数组中删除错误但合理的值的主要内容,如果未能解决你的问题,请参考以下文章

如何编写查询选择合理的权衡?

numpy 保存/加载损坏数组

如何从 NumPy 数组中删除所有零元素?

从 numpy 数组中删除 None 的有效方法

利用顺序表的操作,实现以下的函数。

从 numpy 数组中删除类似方波的伪影