Wykrywanie i wykluczanie wartości odstających w ramce danych Pandas


198

Mam ramkę danych pand z kilkoma kolumnami.

Teraz wiem, że niektóre wiersze są wartościami odstającymi na podstawie określonej wartości kolumny.

Na przykład

kolumna „Vol” ma wszystkie wartości wokół, 12xxa jedna wartość to 4000(odstająca).

Teraz chciałbym wykluczyć te wiersze, które mają Vol taką kolumnę.

Zasadniczo więc muszę umieścić filtr w ramce danych, abyśmy wybrali wszystkie wiersze, w których wartości określonej kolumny mieszczą się w, powiedzmy, 3 standardowych odchyleniach od średniej.

Jaki jest elegancki sposób na osiągnięcie tego?

Odpowiedzi:


214

Jeśli masz wiele kolumn w ramce danych i chcesz usunąć wszystkie wiersze, które mają wartości odstające w co najmniej jednej kolumnie, poniższe wyrażenie zrobi to w jednym ujęciu.

df = pd.DataFrame(np.random.randn(100, 3))

from scipy import stats
df[(np.abs(stats.zscore(df)) < 3).all(axis=1)]

opis:

  • Dla każdej kolumny najpierw oblicza wynik Z każdej wartości w kolumnie, w stosunku do średniej kolumny i odchylenia standardowego.
  • Następnie przyjmuje się absolut Z-score, ponieważ kierunek nie ma znaczenia, tylko jeśli jest poniżej progu.
  • all (oś = 1) zapewnia, że ​​dla każdego wiersza wszystkie kolumny spełniają ograniczenie.
  • Wreszcie wynik tego warunku służy do indeksowania ramki danych.

6
Czy możesz wyjaśnić, co robi ten kod? A może podsunę pomysł, w jaki sposób mogę usunąć wszystkie wiersze, które mają wartości odstające w jednej określonej kolumnie? Byłoby pomocne. Dzięki.
samthebrand

17
Dla każdej kolumny najpierw oblicza wynik Z każdej wartości w kolumnie, w stosunku do średniej kolumny i odchylenia standardowego. Następnie przyjmuje się absolut Z-score, ponieważ kierunek nie ma znaczenia, tylko jeśli jest poniżej progu. .all (oś = 1) zapewnia, że ​​dla każdego wiersza wszystkie kolumny spełniają ograniczenie. Wreszcie wynik tego warunku służy do indeksowania ramki danych.
rafaelvalle

4
Jak poradziłbyś sobie z sytuacją, gdy w kolumnach są wartości Null / Nans. Jak możemy je ignorować?
asimo

6
jak radzimy sobie z kolumnami str dla tego rozwiązania? Jeśli niektóre kolumny są nienumeryczne i chcemy usunąć wartości odstające na podstawie wszystkich kolumn numerycznych.
ssp

6
Wystąpił błąd: „TypeError: nieobsługiwane typy operandów dla /: 'str” i „int” ”
sak

143

Używaj booleanindeksowania tak, jak robisz to wnumpy.array

df = pd.DataFrame({'Data':np.random.normal(size=200)})
# example dataset of normally distributed data. 

df[np.abs(df.Data-df.Data.mean()) <= (3*df.Data.std())]
# keep only the ones that are within +3 to -3 standard deviations in the column 'Data'.

df[~(np.abs(df.Data-df.Data.mean()) > (3*df.Data.std()))]
# or if you prefer the other way around

W przypadku serii jest to podobne:

S = pd.Series(np.random.normal(size=200))
S[~((S-S.mean()).abs() > 3*S.std())]

6
ich DataFrame.abs()FYI równieżDataFrame.clip()
Jeff

7
W przypadku clip()Jeffa kontury nie są usuwane: df.SOME_DATA.clip(-3std,+3std)przypisz kontury do + 3std lub -3std
CT Zhu

1
To prawie tak samo, @AMM
CT Zhu

1
Jak możemy zrobić to samo, jeśli nasza ramka danych pand ma 100 kolumn?
DreamerP

1
Wspaniale, dzięki za odpowiedź @CTZhu. @DreamerP można po prostu zastosować ją do całej DataFrame z: df_new = df[np.abs(df - df.mean()) <= (3 * df.std())]. Ale w przeciwieństwie do zastosowania go do Serii lub pojedynczej kolumny, spowoduje to zastąpienie wartości odstających np.nani zachowanie kształtu DataFrame, więc może być konieczna interpolacja w celu uzupełnienia brakujących wartości.
Scotty1-

95

Dla każdej kolumny ramki danych można uzyskać kwantyl za pomocą:

q = df["col"].quantile(0.99)

a następnie filtruj za pomocą:

df[df["col"] < q]

Jeśli trzeba usunąć dolne i górne wartości odstające, połącz warunek z instrukcją AND:

q_low = df["col"].quantile(0.01)
q_hi  = df["col"].quantile(0.99)

df_filtered = df[(df["col"] < q_hi) & (df["col"] > q_low)]

3
Ten artykuł daje bardzo dobry przegląd poboczna technik usuwania machinelearningmastery.com/...
user6903745

2
może to usunąć wartości odstające tylko z górnej granicy. Nie dolnej?
indolentdeveloper

1
@indolentdeveloper masz rację, po prostu odwróć nierówność, aby usunąć dolne wartości odstające lub połącz je z operatorem OR.
user6903745,

4
Ideą komentarza była aktualizacja odpowiedzi;). Ponieważ ktoś może przegapić ten punkt.
indolentdeveloper

@ user6903745 OŚWIADCZENIE lub „LUB”?
AB

38

Ta odpowiedź jest podobna do tej dostarczonej przez @tanemaki, ale używa lambdawyrażenia zamiast scipy stats.

df = pd.DataFrame(np.random.randn(100, 3), columns=list('ABC'))

df[df.apply(lambda x: np.abs(x - x.mean()) / x.std() < 3).all(axis=1)]

Aby przefiltrować ramkę danych, gdy tylko JEDNA kolumna (np. „B”) mieści się w trzech standardowych odchyleniach:

df[((df.B - df.B.mean()) / df.B.std()).abs() < 3]

Zobacz tutaj, jak stosować ten wynik Z na bieżąco: Toczący wynik Z zastosowany do ramki danych pand


22
#------------------------------------------------------------------------------
# accept a dataframe, remove outliers, return cleaned data in a new dataframe
# see http://www.itl.nist.gov/div898/handbook/prc/section1/prc16.htm
#------------------------------------------------------------------------------
def remove_outlier(df_in, col_name):
    q1 = df_in[col_name].quantile(0.25)
    q3 = df_in[col_name].quantile(0.75)
    iqr = q3-q1 #Interquartile range
    fence_low  = q1-1.5*iqr
    fence_high = q3+1.5*iqr
    df_out = df_in.loc[(df_in[col_name] > fence_low) & (df_in[col_name] < fence_high)]
    return df_out

Pojawia się błąd „ValueError: Nie można zaindeksować kluczem wielowymiarowym” w wierszu „df_out = df_in.loc [(df_in [nazwa_kolumny]> płot) i (df_in [nazwa_kolumny] <wysokość_ ogrodzenia)]„ Pomożesz
Imran Ahmad Ghazali

20

Ponieważ nie widziałem odpowiedzi dotyczącej liczbowych i nienumerycznych atrybutów, oto odpowiedź uzupełniająca.

Możesz zrzucić wartości odstające tylko na atrybuty liczbowe (zmienne kategorialne nie mogą być wartościami odstającymi).

Definicja funkcji

Rozszerzyłem sugestię @ tanemaki do obsługi danych, gdy obecne są również atrybuty nienumeryczne:

from scipy import stats

def drop_numerical_outliers(df, z_thresh=3):
    # Constrains will contain `True` or `False` depending on if it is a value below the threshold.
    constrains = df.select_dtypes(include=[np.number]) \
        .apply(lambda x: np.abs(stats.zscore(x)) < z_thresh, reduce=False) \
        .all(axis=1)
    # Drop (inplace) values set to be rejected
    df.drop(df.index[~constrains], inplace=True)

Stosowanie

drop_numerical_outliers(df)

Przykład

Wyobraź sobie zbiór danych dfz pewnymi wartościami dotyczącymi domów: aleja, kontur terenu, cena sprzedaży, ... Np .: Dokumentacja danych

Najpierw chcesz wizualizować dane na wykresie punktowym (z Z-score Thresh = 3):

# Plot data before dropping those greater than z-score 3. 
# The scatterAreaVsPrice function's definition has been removed for readability's sake.
scatterAreaVsPrice(df)

Przed - cena Gr Liv a sprzedaż Cena

# Drop the outliers on every attributes
drop_numerical_outliers(train_df)

# Plot the result. All outliers were dropped. Note that the red points are not
# the same outliers from the first plot, but the new computed outliers based on the new data-frame.
scatterAreaVsPrice(train_df)

After - Gr Liv Area versus SaleCena


2
Świetne rozwiązanie! Ponieważ heads-up reduce=Falsezostał uznany za przestarzały od pandaswersji 0.23.0
RK1

Zastępstwo result_type='reduce'dla reduce=False.
Ekaba Bisong,

18

Dla każdej serii w ramce danych można użyć betweeni quantileusunąć wartości odstające.

x = pd.Series(np.random.normal(size=200)) # with outliers
x = x[x.between(x.quantile(.25), x.quantile(.75))] # without outliers

3
Tutaj wybierasz tylko dane z zakresu międzykwartylowego (IQR), ale pamiętaj, że mogą istnieć wartości poza tym zakresem, które nie są wartościami odstającymi.
BCArg

2
Myślę, że wybranie np. 0,1 i 0,9 byłoby całkiem bezpieczne. Używanie między kwantylami i takimi jak to jest ładną składnią.
PascalVKooten

8

scipy.statsma metody trim1()i trimboth()wycinać wartości odstające w jednym rzędzie, zgodnie z rankingiem i wprowadzonym procentem usuniętych wartości.


1
trimbothbyło dla mnie najłatwiejsze.
wordsforthewise

6

Inną opcją jest przekształcenie danych, aby złagodzić wpływ wartości odstających. Możesz to zrobić, wygrywając swoje dane.

import pandas as pd
from scipy.stats import mstats
%matplotlib inline

test_data = pd.Series(range(30))
test_data.plot()

Oryginalne dane

# Truncate values to the 5th and 95th percentiles
transformed_test_data = pd.Series(mstats.winsorize(test_data, limits=[0.05, 0.05])) 
transformed_test_data.plot()

Dane winsorized


6

Jeśli podoba Ci się łączenie metod, możesz uzyskać warunek logiczny dla wszystkich kolumn numerycznych, takich jak:

df.sub(df.mean()).div(df.std()).abs().lt(3)

Każda wartość każdej kolumny zostanie przekonwertowana na True/Falsepodstawie tego, czy jest ona mniejsza niż trzy standardowe odchylenia od średniej, czy nie.


Powinno tak być le(3)od momentu usunięcia wartości odstających. W ten sposób dostajesz Truewartości odstające. Poza tym +1 i ta odpowiedź powinna być wyższa
Erfan

2

Możesz użyć maski logicznej:

import pandas as pd

def remove_outliers(df, q=0.05):
    upper = df.quantile(1-q)
    lower = df.quantile(q)
    mask = (df < upper) & (df > lower)
    return mask

t = pd.DataFrame({'train': [1,1,2,3,4,5,6,7,8,9,9],
                  'y': [1,0,0,1,1,0,0,1,1,1,0]})

mask = remove_outliers(t['train'], 0.1)

print(t[mask])

wynik:

   train  y
2      2  0
3      3  1
4      4  1
5      5  0
6      6  0
7      7  1
8      8  1

1

Ponieważ jestem na bardzo wczesnym etapie mojej podróży do nauki danych, odbiegam od poniższego kodu.

#Outlier Treatment

def outlier_detect(df):
    for i in df.describe().columns:
        Q1=df.describe().at['25%',i]
        Q3=df.describe().at['75%',i]
        IQR=Q3 - Q1
        LTV=Q1 - 1.5 * IQR
        UTV=Q3 + 1.5 * IQR
        x=np.array(df[i])
        p=[]
        for j in x:
            if j < LTV or j>UTV:
                p.append(df[i].median())
            else:
                p.append(j)
        df[i]=p
    return df

1

Zdobądź 98. i 2. percentyl jako granice naszych wartości odstających

upper_limit = np.percentile(X_train.logerror.values, 98) 
lower_limit = np.percentile(X_train.logerror.values, 2) # Filter the outliers from the dataframe
data[‘target’].loc[X_train[‘target’]>upper_limit] = upper_limit data[‘target’].loc[X_train[‘target’]<lower_limit] = lower_limit

0

pełny przykład z danymi i 2 grupami:

Import:

from StringIO import StringIO
import pandas as pd
#pandas config
pd.set_option('display.max_rows', 20)

Przykład danych z 2 grupami: G1: Grupa 1. G2: Grupa 2:

TESTDATA = StringIO("""G1;G2;Value
1;A;1.6
1;A;5.1
1;A;7.1
1;A;8.1

1;B;21.1
1;B;22.1
1;B;24.1
1;B;30.6

2;A;40.6
2;A;51.1
2;A;52.1
2;A;60.6

2;B;80.1
2;B;70.6
2;B;90.6
2;B;85.1
""")

Odczytaj dane tekstowe do ramki danych pand:

df = pd.read_csv(TESTDATA, sep=";")

Zdefiniuj wartości odstające za pomocą standardowych odchyleń

stds = 1.0
outliers = df[['G1', 'G2', 'Value']].groupby(['G1','G2']).transform(
           lambda group: (group - group.mean()).abs().div(group.std())) > stds

Zdefiniuj wartości filtrowanych danych i wartości odstające:

dfv = df[outliers.Value == False]
dfo = df[outliers.Value == True]

Wydrukuj wynik:

print '\n'*5, 'All values with decimal 1 are non-outliers. In the other hand, all values with 6 in the decimal are.'
print '\nDef DATA:\n%s\n\nFiltred Values with %s stds:\n%s\n\nOutliers:\n%s' %(df, stds, dfv, dfo)

0

Moja funkcja usuwania wartości odstających

def drop_outliers(df, field_name):
    distance = 1.5 * (np.percentile(df[field_name], 75) - np.percentile(df[field_name], 25))
    df.drop(df[df[field_name] > distance + np.percentile(df[field_name], 75)].index, inplace=True)
    df.drop(df[df[field_name] < np.percentile(df[field_name], 25) - distance].index, inplace=True)

0

Wolę przycinać niż upuszczać. poniższe zostaną przypięte na drugim i 98 pecentylu.

df_list = list(df)
minPercentile = 0.02
maxPercentile = 0.98

for _ in range(numCols):
    df[df_list[_]] = df[df_list[_]].clip((df[df_list[_]].quantile(minPercentile)),(df[df_list[_]].quantile(maxPercentile)))

-2

Usuwanie i pomijanie wartości odstających uważam za statystycznie złe. To odróżnia dane od danych oryginalnych. Sprawia również, że dane są nierównomiernie ukształtowane, a zatem najlepszym sposobem jest zmniejszenie lub uniknięcie efektu wartości odstających przez logiczną transformację danych. To działało dla mnie:

np.log(data.iloc[:, :])

3
Nie można zakładać, dlaczego PO chce coś zrobić.
RajeshM,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.