Подтвердить что ты не робот

Существует ли встроенная функция 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, которые я использовал здесь).

4b9b3361

Ответ 1

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

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

Ответ 2

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

Опираясь на ответ eumiro:

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 находился в разумном относительном масштабе.

Обратите внимание, что для работы синтаксиса data[s<m], data должен быть пустым массивом.

Ответ 3

Ответ бенджамина Банье дает возможность прохода, когда медиана расстояний от медианы равна 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 distance, 7, is greater than m)

Ответ 4

На основе Benjamin's, используя pandas.Series, и заменив MAD с 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, поэтому будет добавлено больше выбросов.

Ответ 5

Альтернативой является создание надежной оценки стандартного отклонения (предполагая гауссову статистику). Если посмотреть онлайн-калькуляторы, я вижу, что 90% процентиль соответствует 1.2815 & сигма; и 95% составляет 1,645 & sigma; (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

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

Ответ 6

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

Для этого я использовал функции маскировки пустышки:

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

Ответ 7

В этом ответе я хотел бы предоставить два метода: решение, основанное на "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.

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

Ответ 8

Учтите, что все вышеперечисленные методы не работают, когда ваше стандартное отклонение становится очень большим из-за огромных выбросов.

(Simalar, так как среднее вычисление терпит неудачу и должно скорее вычислять среднее значение. Хотя среднее значение "более подвержено такой ошибке, как stdDv".)

Вы можете попытаться итеративно применить свой алгоритм или выполнить фильтрацию, используя межквартильный диапазон: (здесь "фактор" относится к диапазону n * sigma, но только тогда, когда ваши данные соответствуют распределению Гаусса)

import numpy as np

def sortoutOutliers(dataIn,factor):
    quant3, quant1 = np.percentile(dataIn, [75 ,25])
    iqr = quant3 - quant1
    iqrSigma = iqr/1.34896
    medData = np.median(dataIn)
    dataOut = [ x for x in dataIn if ( (x > medData - factor* iqrSigma) and (x < medData + factor* iqrSigma) ) ] 
    return(dataOut)