ตรวจจับและแยกค่าผิดปกติในกรอบข้อมูล Pandas


199

ฉันมีกรอบข้อมูลแพนด้ากับคอลัมน์ไม่กี่

ตอนนี้ฉันรู้แล้วว่าแถวบางเส้นเป็นค่าผิดปกติซึ่งขึ้นอยู่กับค่าคอลัมน์ที่แน่นอน

ตัวอย่างเช่น

คอลัมน์ 'Vol' มีค่าทั้งหมดโดยรอบ12xxและหนึ่งค่าคือ4000(ค่าที่มากกว่า)

ตอนนี้ฉันต้องการยกเว้นแถวเหล่านั้นที่มีVolคอลัมน์แบบนี้

โดยพื้นฐานแล้วฉันต้องใส่ตัวกรองลงในกรอบข้อมูลเพื่อให้เราเลือกแถวทั้งหมดที่ค่าของคอลัมน์หนึ่งอยู่ภายในกล่าวคือค่าเบี่ยงเบนมาตรฐาน 3 ค่าจากค่าเฉลี่ย

อะไรคือวิธีที่สวยงามในการบรรลุเป้าหมายนี้?

คำตอบ:


215

หากคุณมีหลายคอลัมน์ใน dataframe ของคุณและต้องการลบแถวทั้งหมดที่มีค่าผิดปกติอย่างน้อยหนึ่งคอลัมน์นิพจน์ต่อไปนี้จะทำเช่นนั้นในนัดเดียว

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

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

คำอธิบาย:

  • สำหรับแต่ละคอลัมน์อันดับแรกให้คำนวณคะแนน Z ของแต่ละค่าในคอลัมน์เทียบกับค่าเฉลี่ยของคอลัมน์และส่วนเบี่ยงเบนมาตรฐาน
  • จากนั้นจะใช้ค่าสัมบูรณ์ของคะแนน Z เนื่องจากทิศทางไม่สำคัญเฉพาะถ้าอยู่ต่ำกว่าขีด จำกัด
  • all (axis = 1) รับรองว่าสำหรับแต่ละแถวคอลัมน์ทั้งหมดเป็นไปตามข้อ จำกัด
  • ในที่สุดผลลัพธ์ของเงื่อนไขนี้ใช้เพื่อจัดทำดัชนีชื่อไฟล์

6
คุณช่วยอธิบายว่ารหัสนี้กำลังทำอะไรอยู่? และอาจให้แนวคิดว่าฉันจะลบแถวทั้งหมดที่มีค่าผิดปกติในคอลัมน์ที่ระบุได้อย่างไร จะเป็นประโยชน์ ขอบคุณ
samthebrand

17
สำหรับแต่ละคอลัมน์อันดับแรกให้คำนวณคะแนน Z ของแต่ละค่าในคอลัมน์เทียบกับค่าเฉลี่ยของคอลัมน์และส่วนเบี่ยงเบนมาตรฐาน จากนั้นจะใช้ค่าสัมบูรณ์ของคะแนน Z เนื่องจากทิศทางไม่สำคัญเฉพาะถ้าอยู่ต่ำกว่าขีด จำกัด . all (axis = 1) รับรองว่าสำหรับแต่ละแถวคอลัมน์ทั้งหมดเป็นไปตามข้อ จำกัด ในที่สุดผลลัพธ์ของเงื่อนไขนี้จะใช้เพื่อจัดทำดัชนีชื่อไฟล์
rafaelvalle

4
คุณจะรับมือกับสถานการณ์อย่างไรเมื่อมี Nulls / Nans ในคอลัมน์ เราจะให้พวกเขาเพิกเฉยได้อย่างไร?
asimo

6
เราจะจัดการกับคอลัมน์ str สำหรับวิธีนี้ได้อย่างไร หากคอลัมน์บางคอลัมน์ไม่ใช่ตัวเลขและเราต้องการลบค่าผิดปกติตามคอลัมน์ตัวเลขทั้งหมด
ssp

6
มีข้อผิดพลาด: "TypeError: ประเภทของตัวถูกดำเนินการที่ไม่รองรับสำหรับ /: 'str' และ 'int'"
sak

144

ใช้การ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
นั่นเกือบจะเหมือนกัน @AMM
CT Zhu

1
เราจะทำสิ่งเดียวกันได้อย่างไรถ้ากรอบข้อมูลแพนด้ามี 100 คอลัมน์?
DreamerP

1
เยี่ยมมากขอบคุณสำหรับคำตอบนั้น @CTZhu @DreamerP คุณก็สามารถใช้มันไป DataFrame df_new = df[np.abs(df - df.mean()) <= (3 * df.std())]ทั้งหมดด้วย: แต่ตรงกันข้ามกับการนำไปใช้กับซีรี่ส์หรือคอลัมน์เดียวสิ่งนี้จะแทนที่ค่าผิดปกติด้วยnp.nanและคงรูปร่างของ DataFrame ดังนั้นการแก้ไขอาจจำเป็นต้องเติมค่าที่หายไป
Scotty1-

95

สำหรับคอลัมน์ dataframe แต่ละคอลัมน์คุณสามารถรับ quantile ด้วย:

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 และคำสั่งหรือ "หรือ"?
AB

38

คำตอบนี้จะคล้ายกับที่จัดไว้ให้โดย @tanemaki แต่ใช้การแสดงออกแทนlambdascipy 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)]

หากต้องการกรอง DataFrame โดยที่มีเพียงหนึ่งคอลัมน์ (เช่น 'B') ที่อยู่ในส่วนเบี่ยงเบนมาตรฐานสามส่วน:

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

ดูที่นี่สำหรับวิธีการใช้คะแนน z นี้ตามเกณฑ์การหมุน: คะแนนการกลิ้งZ นำไปใช้กับดาต้าดาต้าแพนด้า


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]> fence_low) & (df_in [col_name] <fence_high)]" คุณจะช่วยได้ไหม
Imran Ahmad Ghazali

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มีค่าบางอย่างเกี่ยวกับบ้าน: ซอยรูปร่างที่ดินราคาขาย ... ตัวอย่าง: เอกสารข้อมูล

ก่อนอื่นคุณต้องการเห็นภาพข้อมูลในกราฟกระจาย (ด้วยคะแนน z 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)

ก่อน - พื้นที่ Gr Liv เทียบกับการขายราคา

# 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 กับ SalePrice


2
สุดยอดทางออก! ในฐานะที่เป็นหัวหน้าขึ้นreduce=Falseเลิกใช้แล้วตั้งแต่pandasเวอร์ชั่น 0.23.0
RK1

แทนสำหรับresult_type='reduce' reduce=False
Ekaba

18

สำหรับแต่ละซีรี่ส์ใน dataframe คุณสามารถใช้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
ที่นี่คุณกำลังเลือกเฉพาะข้อมูลภายในช่วง interquartile (IQR) แต่โปรดทราบว่าอาจมีค่าอยู่นอกช่วงนี้ที่ไม่ใช่ค่าผิดปกติ
BCArg

2
การเลือกเช่น 0.1 และ 0.9 นั้นค่อนข้างปลอดภัย การใช้ระหว่างและปริมาณเช่นนี้เป็นไวยากรณ์ที่ค่อนข้าง
PascalVKooten

8

scipy.statsมีวิธีการtrim1()และtrimboth()ตัดค่าผิดปกติออกเป็นแถวเดียวตามการจัดอันดับและเปอร์เซ็นต์ที่แนะนำของค่าลบออก


1
trimbothง่ายที่สุดสำหรับฉัน
Wordsforthewise

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 และคำตอบนี้ควรจะสูงขึ้น
Erfan

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

ฉันชอบที่จะตัดมากกว่าปล่อย รายการต่อไปนี้จะเข้าที่ที่ pecentiles ที่ 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
ไม่สามารถตั้งสมมติฐานเกี่ยวกับสาเหตุที่ OP ต้องการทำอะไรสักอย่าง
RajeshM
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.