Есть ли встроенный numpy для отклонения выбросов из списка


есть ли numpy встроенный, чтобы сделать что-то вроде следующего? То есть возьмите список d и возвращает список filtered_d при удалении любых внешних элементов на основе некоторого предполагаемого распределения точек в d.

import numpy as np

def reject_outliers(data):
    m = 2
    u = np.mean(data)
    s = np.std(data)
    filtered = [e for e in data if (u - 2 * s < e < u + 2 * s)]
    return filtered

>>> d = [2,4,5,1,6,5,40]
>>> filtered_d = reject_outliers(d)
>>> print filtered_d
[2,4,5,1,6,5]

Я говорю "что-то вроде", потому что функция может учитывать различные распределения (Пуассон, Гаусс и т. д.) и различные пороги выбросов в этих распределениях (например,m я использовал здесь).

7 70

7 ответов:

этот метод почти идентичен вашему, просто больше numpyst (также работает только с массивами numpy):

def reject_outliers(data, m=2):
    return data[abs(data - np.mean(data)) < m * np.std(data)]

что-то важное при работе с выбросами заключается в том, что следует попытаться использовать оценки как можно более надежные. Среднее значение распределения будет смещено выбросами, но, например, медиана будет намного меньше.

основываясь на ответе эумиро:

def reject_outliers(data, m = 2.):
    d = np.abs(data - np.median(data))
    mdev = np.median(d)
    s = d/mdev if mdev else 0.
    return data[s<m]

здесь Я заменил среднее значение на более надежную медиану и стандартное отклонение с абсолютным расстоянием до медианы. Затем я масштабировал расстояния по их (снова) медианному значению так, что m на разумный относительный масштаб.

опираясь на Бенджамина, используя pandas.Series и заменить сумасшедший с IQR:

def reject_outliers(sr, iq_range=0.5):
    pcnt = (1 - iq_range) / 2
    qlow, median, qhigh = sr.dropna().quantile([pcnt, 0.50, 1-pcnt])
    iqr = qhigh - qlow
    return sr[ (sr - median).abs() <= iqr]

например, если вы установите iq_range=0.6, процентили межквартильного диапазона стали бы:0.20 <--> 0.80, Так что больше выбросы будут включены.

ответ Бенджамина баннера дает проход, когда медиана расстояний от медианы равна 0, поэтому я нашел эту модифицированную версию немного более полезной для случаев, приведенных в примере ниже.

def reject_outliers_2(data, m = 2.):
    d = np.abs(data - np.median(data))
    mdev = np.median(d)
    s = d/(mdev if mdev else 1.)
    return data[s<m]

пример:

data_points = np.array([10, 10, 10, 17, 10, 10])
print(reject_outliers(data_points))
print(reject_outliers_2(data_points))

выдает:

[[10, 10, 10, 17, 10, 10]]  # 17 is not filtered
[10, 10, 10, 10, 10]  # 17 is filtered (it's distance, 7, is greater than m)

альтернативой является робастная оценка стандартного отклонения (при условии гауссовой статистики). Глядя на онлайн калькуляторы, я вижу, что 90% процентиль соответствует 1.2815 σ, А 95% - 1.645 σ (http://vassarstats.net/tabs.html?#z)

простой пример:

import numpy as np

# Create some random numbers
x = np.random.normal(5, 2, 1000)

# Calculate the statistics
print("Mean= ", np.mean(x))
print("Median= ", np.median(x))
print("Max/Min=", x.max(), " ", x.min())
print("StdDev=", np.std(x))
print("90th Percentile", np.percentile(x, 90))

# Add a few large points
x[10] += 1000
x[20] += 2000
x[30] += 1500

# Recalculate the statistics
print()
print("Mean= ", np.mean(x))
print("Median= ", np.median(x))
print("Max/Min=", x.max(), " ", x.min())
print("StdDev=", np.std(x))
print("90th Percentile", np.percentile(x, 90))

# Measure the percentile intervals and then estimate Standard Deviation of the distribution, both from median to the 90th percentile and from the 10th to 90th percentile
p90 = np.percentile(x, 90)
p10 = np.percentile(x, 10)
p50 = np.median(x)
# p50 to p90 is 1.2815 sigma
rSig = (p90-p50)/1.2815
print("Robust Sigma=", rSig)

rSig = (p90-p10)/(2*1.2815)
print("Robust Sigma=", rSig)

выход я получаю:

Mean=  4.99760520022
Median=  4.95395274981
Max/Min= 11.1226494654   -2.15388472011
Sigma= 1.976629928
90th Percentile 7.52065379649

Mean=  9.64760520022
Median=  4.95667658782
Max/Min= 2205.43861943   -2.15388472011
Sigma= 88.6263902244
90th Percentile 7.60646688694

Robust Sigma= 2.06772555531
Robust Sigma= 1.99878292462

что близко к ожидаемому значению 2.

если мы хотим удалить точки выше / ниже 5 стандартных отклонения (с 1000 пунктов мы ожидали бы 1 значение > 3 стандартных отклонения):

y = x[abs(x - p50) < rSig*5]

# Print the statistics again
print("Mean= ", np.mean(y))
print("Median= ", np.median(y))
print("Max/Min=", y.max(), " ", y.min())
print("StdDev=", np.std(y))

что дает:

Mean=  4.99755359935
Median=  4.95213030447
Max/Min= 11.1226494654   -2.15388472011
StdDev= 1.97692712883

Я понятия не имею, какой подход более эффективный/надежный

Я хотел бы предоставить два метода в этом ответе, решение на основе "Z score" и решение на основе "IQR".

код, приведенный в этом ответе, работает как на одном dim numpy массив и несколько numpy массив.

давайте сначала импортируем некоторые модули.

import collections
import numpy as np
import scipy.stats as stat
from scipy.stats import iqr

Z оценка на основе метода

этот метод будет проверять, если число выходит за рамки трех стандартных отклонений. На основе этого правила, если значение является выбросом, метод будет верните true, если нет, верните false.

def sd_outlier(x, axis = None, bar = 3, side = 'both'):
    assert side in ['gt', 'lt', 'both'], 'Side should be `gt`, `lt` or `both`.'

    d_z = stat.zscore(x, axis = axis)

    if side == 'gt':
        return d_z > bar
    elif side == 'lt':
        return d_z < -bar
    elif side == 'both':
        return np.abs(d_z) > bar

метод на основе IQR

этот метод будет проверять, если значение меньше q1 - 1.5 * iqr или более q3 + 1.5 * iqr, что аналогично методу построения SPSS.

def q1(x, axis = None):
    return np.percentile(x, 25, axis = axis)

def q3(x, axis = None):
    return np.percentile(x, 75, axis = axis)

def iqr_outlier(x, axis = None, bar = 1.5, side = 'both'):
    assert side in ['gt', 'lt', 'both'], 'Side should be `gt`, `lt` or `both`.'

    d_iqr = iqr(x, axis = axis)
    d_q1 = q1(x, axis = axis)
    d_q3 = q3(x, axis = axis)
    iqr_distance = np.multiply(d_iqr, bar)

    stat_shape = list(x.shape)

    if isinstance(axis, collections.Iterable):
        for single_axis in axis:
            stat_shape[single_axis] = 1
    else:
        stat_shape[axis] = 1

    if side in ['gt', 'both']:
        upper_range = d_q3 + iqr_distance
        upper_outlier = np.greater(x - upper_range.reshape(stat_shape), 0)
    if side in ['lt', 'both']:
        lower_range = d_q1 - iqr_distance
        lower_outlier = np.less(x - lower_range.reshape(stat_shape), 0)

    if side == 'gt':
        return upper_outlier
    if side == 'lt':
        return lower_outlier
    if side == 'both':
        return np.logical_or(upper_outlier, lower_outlier)

наконец, если вы хотите отфильтровать выбросы, использовать numpy селектор.

хорошего дня.

Я хотел сделать что-то подобное, за исключением установки числа в NaN, а не удаления его из данных, так как если вы удалите его, вы измените длину, которая может испортить построение (т. е. если вы только удаляете выбросы из одного столбца в таблице, но вам нужно, чтобы он оставался таким же, как и другие столбцы, чтобы вы могли построить их друг против друга).

для этого я использовал маскирующие функции numpy:

def reject_outliers(data, m=2):
    stdev = np.std(data)
    mean = np.mean(data)
    maskMin = mean - stdev * m
    maskMax = mean + stdev * m
    mask = np.ma.masked_outside(data, maskMin, maskMax)
    print('Masking values outside of {} and {}'.format(maskMin, maskMax))
    return mask