Pandas 데이터 프레임에서 특이 값 탐지 및 제외


199

열이 적은 팬더 데이터 프레임이 있습니다.

이제 특정 행이 특정 열 값을 기반으로하는 특이 치라는 것을 알고 있습니다.

예를 들어

열 'Vol'에는 모든 값이 12xx있으며 하나의 값은 4000(이상 값)입니다.

이제 이와 같은 Vol열이 있는 행을 제외하고 싶습니다.

따라서 본질적으로 특정 열의 값이 평균과 3 표준 편차 내에있는 모든 행을 선택하도록 데이터 프레임에 필터를 배치해야합니다.

이것을 달성하는 우아한 방법은 무엇입니까?

답변:


215

데이터 프레임에 여러 열이 있고 하나 이상의 열에 특이 치가있는 모든 행을 제거하려면 다음식이 한 번에 수행됩니다.

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
열에 Null / 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()Jeff 의 경우 윤곽선이 제거되지 않습니다. 윤곽선 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())]. 그러나 Series 또는 단일 열에 적용하는 것과 달리 이는 특이 치를 대체 np.nan하여 DataFrame의 모양을 유지하므로 누락 된 값을 채우기 위해 보간이 필요할 수 있습니다.
Scotty1-

95

각 데이터 프레임 열에 대해 다음을 통해 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 AND 문 또는 "OR"?
AB

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

하나의 열 (예 : 'B') 만 세 표준 편차 내에있는 DataFrame을 필터링하려면 다음을 수행하십시오.

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]> fence_low) (df_in [COL_NAME] <fence_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골목, 토지 등고선, 판매 가격 등 주택에 대한 일부 가치가 있는 데이터 세트 를 상상해보십시오 . 예 : 데이터 문서

먼저, 산점도 (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)

Before-Gr Liv Area 대 SalePrice

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

판매 후 가격-Gr Liv 영역


2
훌륭한 솔루션! 버전 0.23.0 reduce=False부터 pandas
RK1

을 대신 result_type='reduce'합니다 reduce=False.
Ekaba Bisong

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를 선택하면 꽤 안전합니다. 이와 같은 사이와 Quantile을 사용하는 것은 꽤 구문입니다.
PascalVKooten

8

scipy.stats방법을 보유 trim1()하고 trimboth()순위 제거 값의 도입 비율에 따라, 하나의 행에 아웃 라이어를 잘라.


1
trimboth나를 위해 가장 쉬웠다.
wordsforthewise

6

다른 옵션은 특이 치의 영향을 완화하도록 데이터를 변환하는 것입니다. 데이터를 winsorizing하여이 작업을 수행 할 수 있습니다.

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

Winsorized 데이터


6

메소드 체인을 좋아하는 경우 다음과 같이 모든 숫자 열에 대한 부울 조건을 얻을 수 있습니다.

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

각 열의 각 값은 True/False세 표준 편차가 평균에서 벗어난 지 여부 에 따라 변환됩니다 .


특이 치가 제거 된le(3) 이후 여야합니다 . 이 방법으로 당신 은 특이 치를 얻 습니다. 그 +1 True
외에이

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

데이터와 두 그룹으로 구성된 전체 예는 다음과 같습니다.

수입품 :

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

두 그룹이있는 데이터 예 : 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

떨어 뜨리기보다는 클립하는 것을 선호합니다. 다음은 두 번째와 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.