Czy istnieje numpy do odrzucania odstających z listy

Czy istnieje numpy builtin, aby zrobić coś takiego jak poniżej? Oznacza to, że weź listę d i zwróć listę {[2] } z usuniętymi elementami zewnętrznymi na podstawie pewnego przyjętego rozkładu punktów w 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ówię "coś podobnego", ponieważ funkcja może pozwalać na różne rozkłady (Poissona, Gaussa itp.) i różne progi odstające w obrębie tych rozkładów (jak m użyłem tutaj).

Author: aaren, 2012-07-27

7 answers

Ta metoda jest prawie identyczna jak twoja, tylko bardziej numpyst (działa również tylko na tablicach numpy):

def reject_outliers(data, m=2):
    return data[abs(data - np.mean(data)) < m * np.std(data)]
 72
Author: eumiro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-27 11:27:57

Coś ważnego przy radzeniu sobie z odstającymi jest to, że należy starać się używać estymatorów tak solidne, jak to możliwe. Średnia rozkładu będzie tendencyjna przez wartości odstające, ale np. mediana będzie znacznie mniejsza.

Bazując na odpowiedzi 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]

Tutaj zastąpiłem średnią solidniejszą medianą, a odchylenie standardowe absolutną odległością od mediany. Następnie przeskalowałem odległości o ich (ponownie) wartość mediany tak, że m jest na rozsądnym względnym skala.

 140
Author: Benjamin Bannier,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-15 05:49:03

Budowanie na Benjaminie, użycie pandas.Series i zastąpienie MAD przez 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]

Na przykład, jeśli ustawisz iq_range=0.6, percentyle przedziału międzykwartylowego staną się: 0.20 <--> 0.80, więc zostanie uwzględnionych więcej odstających.

 8
Author: ankostis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-10 10:47:10

Odpowiedź Benjamina Bannera daje przejście, gdy mediana odległości od mediany wynosi 0, więc ta zmodyfikowana wersja okazała się nieco bardziej pomocna w przypadkach, jak podano w poniższym przykładzie.

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]

Przykład:

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

Daje:

[[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)
 5
Author: Yigal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-30 11:10:24

Alternatywą jest solidne oszacowanie odchylenia standardowego (zakładając statystykę Gaussa). Patrząc na kalkulatory online, widzę, że percentyl 90% odpowiada 1.2815 σ, a 95% to 1.645 σ (http://vassarstats.net/tabs.html?#z )

Jako prosty przykład:

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)

Wyjście, które otrzymuję to:

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

, która jest bliska wartości oczekiwanej 2.

Jeśli chcemy usunąć punkty powyżej/poniżej 5 odchyleń standardowych (przy 1000 punktach oczekiwana wartość 1 > 3 odchylenia standardowe):

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))

Co daje:

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

Nie mam pojęcia, które podejście jest bardziej skuteczne / solidne

 3
Author: Chris,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-20 12:43:15

Chciałbym podać dwie metody w tej odpowiedzi, rozwiązanie oparte na "Z score" i rozwiązanie oparte na "IQR".

Kod podany w odpowiedzi działa zarówno na pojedynczej tablicy dim numpy, jak i na wielu tablicach numpy.

Najpierw zaimportujmy niektóre moduły.

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

Z score based method

Metoda ta sprawdzi, czy liczba wypada poza trzy odchylenia standardowe. Na podstawie tej reguły, jeśli wartość jest odstająca, metoda zwróci true, jeśli nie, zwróci fałsz.

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

Metoda oparta na IQR

Ta metoda sprawdzi, czy wartość jest mniejsza niż q1 - 1.5 * iqr lub większa niż q3 + 1.5 * iqr, co jest podobne do metody wykresu 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)

Na koniec, jeśli chcesz odfiltrować wartości odstające, użyj selektora numpy.

Miłego dnia.
 1
Author: Losses Don,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-21 23:29:10

Chciałem zrobić coś podobnego, z wyjątkiem ustawienia numeru na NaN zamiast usuwania go z danych, ponieważ jeśli usuniesz go, zmienisz długość, która może zepsuć kreślenie (tzn. jeśli usuwasz tylko odstające wartości z jednej kolumny w tabeli, ale potrzebujesz, aby pozostała taka sama jak inne kolumny, abyś mógł narysować je przeciwko sobie).

W tym celu użyłem funkcji maskujących 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
 0
Author: Alex S,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-20 19:53:51