从2D列表中删除异常值

时间:2014-02-24 19:53:46

标签: python numpy

我有一个词典列表,如:

t = [{k: 1, 'a': 22, 'b': 59}, {k: 2, 'a': 21, 'b': 34}, {'k': 3, 'a': 991, 'b': 29}, {'k': 4, 'a': 45, 'b': 11}, {'k': 5, 'a'; 211, 'b': 77}, {'k': 6, 'a': 100, 'b': 1024}]

如何从中移除异常值以便我可以拥有以某个有意义的值为中心的所有内容,或者它没有太大或太小的值?

感谢。

2 个答案:

答案 0 :(得分:2)

作为起点,您可以将数据转换为记录数组:

import numpy as np
t = [{'k': 1, 'a': 22, 'b': 59}, {'k': 2, 'a': 21, 'b': 34}, {'k': 3, 'a': 991, 'b': 29}, {'k': 4, 'a': 45, 'b': 11}, {'k': 5, 'a': 211, 'b': 77}, {'k': 6, 'a': 100, 'b': 1024}]
foo = np.core.records.fromrecords([x.values() for x in t], names=t[0].keys())

这样可以更轻松地进行分析:

In [34]: foo.a.mean(), foo.a.std()
Out[34]: (231.66666666666666, 345.81674659018785)

In [35]: foo.b.mean(), foo.b.std()
Out[35]: (205.66666666666666, 366.58590019560518)

也许你可以用箱线图寻找异常值?

from matplotlib import pyplot
pyplot.boxplot([foo.a, foo.b])
pyplot.show()

或者,您可以在数据的第90个百分位数内找到值:

In [40]: foo.a[foo.a < np.percentile(foo.a, 90)]
Out[40]: array([ 22,  21,  45, 211, 100])

并选择非离群值k值:

outlier_mask = (foo.a < np.percentile(foo.a, 90)) & (foo.b < np.percentile(foo.b, 90))
foo.k[outlier_mask]

当然,您如何决定哪些值是异常值取决于您。

答案 1 :(得分:1)

下面的代码找到距离均值最远的点,删除它,然后再次检查均值。如果移除该点会导致均值移动小于给定容差(通过旧均值的百分比变化),则拒绝移动并返回旧列表。否则,将保留新列表并继续该过程。

t = [{'a': 22, 'b': 59, 'k': 1},
 {'a': 21, 'b': 34, 'k': 2},
 {'a': 991, 'b': 29, 'k': 3},
 {'a': 45, 'b': 11, 'k': 4},
 {'a': 211, 'b': 77, 'k': 5},
 {'a': 100, 'b': 1024, 'k': 6}]

K = [te['k'] for te in t]
A = [te['a'] for te in t]
B = [te['b'] for te in t]

data = zip(K,A,B)

def mean(A):
    return sum(A)/float(len(A))

def max_deviation(A):
    mu = mean(A)
    dev = [(a, abs(a-mu)) for a in A]
    dev.sort(key=lambda k: k[1], reverse=True)
    return dev[0][0]

def remove_outliers(A, tol=.3):
    mu = mean(A)
    A_prime = list(a for a in A if a != max_deviation(A))
    mu_prime = mean(A_prime)
    if abs(mu_prime - mu)/float(mu) > tol:
        return remove_outliers(A_prime, tol)
    else:
        return A

t_prime = [dict(k=k, a=a, b=b) for k, a, b in data 
           if a in remove_outliers(A) and b in remove_outliers(B)]

>>> print t_prime
[{'a': 22, 'b': 59, 'k': 1},
 {'a': 21, 'b': 34, 'k': 2},
 {'a': 45, 'b': 11, 'k': 4}]

编辑:这可能会更好地扩展,因为它会删除一个值而不是创建N-1值。这将修改原始的A向量。如果您不想这样,那么第一个选项将是您的最佳选择,或者以副本开头发送。

def remove_outliers(A, tol=.3):
    mu = mean(A)
    out = max_deviation(A)
    A.remove(out)
    mu_prime = mean(A)
    if abs(mu_prime - mu)/float(mu) > tol:
        return remove_outliers(A, tol)
    else:
        A.append(out)
        return A