पंडों के डेटा फ्रेम में आउटलेर का पता लगाएं और बाहर करें


198

मेरे पास कुछ कॉलम के साथ एक पांडा डेटा फ्रेम है।

अब मुझे पता है कि कुछ पंक्तियाँ एक निश्चित स्तंभ मान के आधार पर आउटलेर हैं।

उदाहरण के लिए

स्तंभ 'Vol' के चारों ओर सभी मान हैं 12xxऔर एक मान 4000(बाह्य) है।

अब मैं उन पंक्तियों को बाहर करना चाहूंगा जिनमें Volइस तरह कॉलम हैं।

इसलिए, अनिवार्य रूप से मुझे डेटा फ्रेम पर एक फिल्टर लगाने की आवश्यकता है जैसे कि हम सभी पंक्तियों का चयन करते हैं जहां एक निश्चित कॉलम के मान भीतर हैं, कहते हैं, मतलब से 3 मानक विचलन।

इसे प्राप्त करने का एक सुंदर तरीका क्या है?

जवाबों:


214

यदि आपके डेटाफ़्रेम में कई कॉलम हैं और कम से कम एक कॉलम में आउटलेयर वाली सभी पंक्तियों को निकालना चाहते हैं, तो निम्न अभिव्यक्ति एक शॉट में होगी।

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

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

विवरण:

  • प्रत्येक स्तंभ के लिए, पहले स्तंभ के माध्य और मानक विचलन के सापेक्ष, कॉलम के प्रत्येक मान का Z-स्कोर गणना करता है।
  • फिर जेड-स्कोर का निरपेक्ष लेता है क्योंकि दिशा कोई फर्क नहीं पड़ता, केवल अगर यह सीमा से नीचे है।
  • सभी (अक्ष = 1) सुनिश्चित करता है कि प्रत्येक पंक्ति के लिए, सभी स्तंभ बाधा को संतुष्ट करते हैं।
  • अंत में, इस शर्त के परिणाम का उपयोग डेटाफ्रेम को अनुक्रमित करने के लिए किया जाता है।

6
क्या आप बता सकते हैं कि यह कोड क्या कर रहा है? और शायद एक विचार प्रदान करें कि मैं उन सभी पंक्तियों को कैसे हटा सकता हूं जिनमें एक एकल निर्दिष्ट कॉलम में एक रूपरेखा है? मददगार होगा। धन्यवाद।
18

17
प्रत्येक स्तंभ के लिए, पहले यह कॉलम माध्य और मानक विचलन के सापेक्ष कॉलम में प्रत्येक मान के जेड-स्कोर की गणना करता है। फिर जेड-स्कोर का निरपेक्ष लेता है क्योंकि दिशा मायने नहीं रखती है, केवल अगर यह सीमा से नीचे है। .all (अक्ष = 1) यह सुनिश्चित करता है कि प्रत्येक पंक्ति के लिए, सभी स्तंभ बाधा को संतुष्ट करते हैं। अंत में, इस शर्त के परिणाम का उपयोग डेटाफ्रेम को अनुक्रमित करने के लिए किया जाता है।
राफेलवालाल

4
कॉलम में Nulls / Nans होने पर आप स्थिति को कैसे संभालेंगे। हम उन्हें कैसे नजरअंदाज कर सकते हैं?
असिमो

6
हम इस समाधान के लिए तार से कैसे निपटते हैं? यदि कुछ कॉलम नॉन-न्यूमेरिक हैं और हम सभी न्यूमेरिक कॉलम के आधार पर आउटलेर को हटाना चाहते हैं।
एसपी

6
त्रुटि मिली: "TypeError: असमर्थित ऑपरेंड प्रकार (ओं) के लिए /: 'str' और 'int'"
sak

143

booleanअनुक्रमणिका का उपयोग करें जैसा कि आप करते हैंnumpy.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

एक श्रृंखला के लिए यह समान है:

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

6
उनका एक DataFrame.abs()FYI है, वह भीDataFrame.clip()
Jeff

7
clip()जेफ के मामले में , रूपरेखा को हटाया नहीं जाता है: df.SOME_DATA.clip(-3std,+3std)आउटलाइनरों को या तो + 3std या -3std पर असाइन करें
CT Zhu

1
यह लगभग समान है, @ खेल
सीटी झू

1
यदि हमारे पंडों के डेटा फ्रेम में 100 कॉलम हैं, तो हम कैसे काम कर सकते हैं?
ड्रीमरपी

1
बहुत बढ़िया, उस उत्तर के लिए धन्यवाद @CTZhu। @DreamerP तुम सिर्फ साथ पूरे DataFrame में इसे लागू कर सकते हैं: df_new = df[np.abs(df - df.mean()) <= (3 * df.std())]लेकिन एक श्रृंखला या एकल कॉलम पर इसे लागू करने के विपरीत, यह np.nanआउटलेर्स को बदल देगा और डेटाफ़्रेम का आकार बनाए रखेगा, इसलिए लापता मानों को भरने के लिए प्रक्षेप की आवश्यकता हो सकती है।
स्कॉटी 1-

95

आपके प्रत्येक डेटाफ़्रेम कॉलम के लिए, आपको निम्न मात्राएँ मिल सकती हैं:

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

और उसके बाद फ़िल्टर करें:

df[df["col"] < q]

यदि किसी को निचले और ऊपरी हिस्से को हटाने की आवश्यकता हो, तो एक 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
यह लेख बाहरी हटाने तकनीक का एक बहुत अच्छा सिंहावलोकन देता है machinelearningmastery.com/...
user6903745

2
यह केवल ऊपरी सीमा से आउटलेर को हटा सकता है .. कम नहीं?
indolentdeveloper

1
@indolentdeveloper आप सही हैं, केवल निचले आउटलेर्स को निकालने के लिए असमानता को उल्टा करते हैं, या उन्हें एक OR ऑपरेटर के साथ जोड़ते हैं।
user6903745

4
टिप्पणी का विचार उत्तरों को अद्यतन करने के लिए था;)। चूंकि कोई इस बिंदु को याद कर सकता है।
indolentdeveloper

@ user6903745 और बयान या "या"?
एबी

38

यह उत्तर @tanemaki द्वारा प्रदान किए गए के समान है, लेकिन lambdaइसके बजाय एक अभिव्यक्ति का उपयोग करता है 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)]

डेटाफ़्रेम को फ़िल्टर करने के लिए जहां केवल एक कॉलम (जैसे 'बी') तीन मानक विचलन के भीतर है:

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

इस ज़ेड-स्कोर को रोलिंग के आधार पर कैसे लागू किया जाए, इसके लिए यहाँ देखें: पैंडास डेटाफ़्रेम पर रोलिंग ज़ेड-स्कोर


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

मुझे त्रुटि मिल रही है "ValueError: लाइन में बहुआयामी कुंजी के साथ इंडेक्स नहीं कर सकता" df_out = df_in.loc [(df_in [col_name]> बाड़_low) और (df_in (col_name) <बाड़_high]] "क्या आप मदद करेंगे
इमरान अहमद

20

चूंकि मैंने एक उत्तर नहीं देखा है जो संख्यात्मक और गैर-संख्यात्मक विशेषताओं से निपटता है , यहां एक पूरक उत्तर है।

आप केवल संख्यात्मक विशेषताओं पर आउटलेर को छोड़ना चाह सकते हैं (श्रेणीबद्ध चर शायद ही आउटलेर हो सकते हैं)।

कार्य की परिभाषा

जब गैर-संख्यात्मक विशेषताएँ भी मौजूद हों तो मैंने डेटा को संभालने के लिए @ tanemaki के सुझाव को बढ़ाया है:

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)

प्रयोग

drop_numerical_outliers(df)

उदाहरण

dfघरों के बारे में कुछ मूल्यों के साथ एक डेटासेट की कल्पना करें : गली, भूमि समोच्च, बिक्री मूल्य, ... उदाहरण: डेटा प्रलेखन

सबसे पहले, आप डेटा को स्कैटर ग्राफ पर देखना चाहते हैं (जेड-स्कोर थ्रेश = 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)

इससे पहले - जीआर लिव एरिया वर्सेस सेलप्राइस

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

के बाद - जीआर लिव क्षेत्र बनाम बिक्री


2
महान समाधान! एक सिर के रूप में 0.23.0reduce=Falsepandas
RK1

स्थानापन्न result_type='reduce'के लिए reduce=False
एकबा बिसॉन्ग

18

डेटाफ़्रेम में प्रत्येक श्रृंखला के लिए, आप उपयोग कर सकते हैं betweenऔर quantileआउटलेर को हटा सकते हैं ।

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

3
यहां आप इंटरक्वेर्टाइल रेंज (IQR) के भीतर केवल डेटा का चयन कर रहे हैं, लेकिन ध्यान रखें कि इस रेंज के बाहर मान हो सकते हैं जो आउटलेर नहीं हैं।
BCArg

2
मुझे लगता है कि 0.1 और 0.9 का चयन बहुत सुरक्षित होगा। के बीच और इस तरह का उपयोग करना एक बहुत अच्छा वाक्यविन्यास है।
पास्कलवूटन

8

scipy.statsरैंकिंग और हटाए गए मूल्यों का एक प्रतिशत के अनुसार विधियों trim1()और trimboth()एक पंक्ति में आउटलेर्स को काटने के लिए है।


1
trimbothमेरे लिए सबसे आसान था।
शब्दफोर्ट वाइज

6

एक अन्य विकल्प आपके डेटा को बदलना है ताकि आउटलेयर का प्रभाव कम हो। आप अपने डेटा को जीतकर ऐसा कर सकते हैं।

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

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

मूल डेटा

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

डेटा को वर्गीकृत किया


6

यदि आपको विधि जंजीर पसंद है, तो आप इस तरह के सभी संख्यात्मक कॉलम के लिए अपनी बूलियन स्थिति प्राप्त कर सकते हैं:

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

प्रत्येक स्तंभ के प्रत्येक मान को इस True/Falseआधार पर रूपांतरित किया जाएगा कि उसके तीन मानक विचलन औसत से कम हैं या नहीं।


यह le(3)इसके निष्कासन को हटाने के बाद से होना चाहिए । इस तरह आप Trueआउटलेयर के लिए मिलते हैं । इसके अलावा +1 और यह उत्तर अधिक होना चाहिए
इरफान

2

आप बूलियन मास्क का उपयोग कर सकते हैं:

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

उत्पादन:

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

1

चूंकि मैं अपनी डेटा विज्ञान यात्रा के बहुत शुरुआती चरण में हूं, इसलिए मैं नीचे दिए गए कोड के साथ आउटलेर्स का इलाज कर रहा हूं।

#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

हमारे आउटलेर्स की सीमा के रूप में 98 वें और 2 प्रतिशत प्रतिशत प्राप्त करें

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

डेटा और 2 समूहों के साथ एक पूर्ण उदाहरण इस प्रकार है:

आयात:

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

2 समूहों के साथ डेटा उदाहरण: G1: समूह 1. G2: समूह 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
""")

पंडों के डेटाफ्रेम का पाठ डेटा पढ़ें:

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

मानक विचलन का उपयोग कर आउटलेर्स को परिभाषित करें

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

फ़िल्टर्ड डेटा मान और आउटलेर परिभाषित करें:

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

परिणाम प्रिंट करें:

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

आउटलेर्स छोड़ने का मेरा कार्य

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

मैं ड्रॉप के बजाय क्लिप करना पसंद करता हूं। निम्नलिखित 2 और 98 वें पेसेन्टाइल में इनिप को क्लिप करेगा।

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

निष्कासन और निष्कासन मुझे विश्वास है कि सांख्यिकीय रूप से गलत है। यह डेटा को मूल डेटा से अलग बनाता है। साथ ही डेटा को असमान रूप से आकार देता है और इसलिए सबसे अच्छा तरीका है कि डेटा को लॉग ट्रांसफ़ॉर्म करके आउटलेर्स के प्रभाव को कम करें या उससे बचें। यह मेरे लिए काम किया:

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

3
क्यों ओपी कुछ करना चाहता है, इसके बारे में धारणा नहीं बना सकते।
राजेश
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.