Pandasデータフレームの外れ値を検出して除外する


197

列が少ないpandasデータフレームがあります。

これで、特定の行が特定の列値に基づく外れ値であることがわかりました。

例えば

列「Vol」にはすべての値が12xxあり、1つの値は4000(外れ値)です。

次にVol、このような列を持つ行を除外します。

したがって、基本的には、特定の列の値が平均から3標準偏差以内にあるすべての行を選択するように、データフレームにフィルターをかける必要があります。

これを達成するためのエレガントな方法は何ですか?

回答:


213

データフレームに複数の列があり、少なくとも1つの列に外れ値があるすべての行を削除する場合、次の式はそれを一度に実行します。

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 2015

17
各列について、最初に、列の平均と標準偏差を基準にして、列の各値のZスコアを計算します。次に、Zスコアの絶対値を取得します。これは、方向が問題ではないため、しきい値を下回っている場合に限られます。.all(axis = 1)は、各行について、すべての列が制約を満たすことを保証します。最後に、この条件の結果を使用してデータフレームにインデックスを付けます。
rafaelvalle

4
列にNull / Nansがある場合の状況をどのように処理しますか?どうすればそれらを無視させることができますか?
asimo

6
このソリューションでstr列をどのように処理しますか?一部の列が非数値であり、すべての数値列に基づいて外れ値を削除したい場合。
ssp

6
Got error: "TypeError:unsupported operand type(s)for /: 'str' and 'int'"
sak

142

のように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()も参考までにDataFrame.clip()
ジェフ

7
clip()ジェフの場合、アウトラインは削除されません。df.SOME_DATA.clip(-3std,+3std)アウトライナーを+ 3stdまたは-3stdのいずれかに割り当てます
CT Zhu

1
それはほとんど同じです、@ AMM
CT Zhu

1
パンダのデータフレームに100列ある場合、どうすれば同じことができますか?
DreamerP 2018年

1
すばらしい、その回答@CTZhuに感謝します。@DreamerPあなただけで全体のデータフレームに適用することができますdf_new = df[np.abs(df - df.mean()) <= (3 * df.std())]ただし、シリーズまたは単一の列に適用するのとは対照的に、これは外れ値をnp.nanDataFrameの形状に置き換えて形状を維持するため、欠損値を埋めるために補間が必要になる場合があります。
Scotty1-

93

データフレーム列ごとに、次の方法で変位値を取得できます。

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

列が1つだけ(たとえば、「B」)が3つの標準偏差内にある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

「df_out = df_in.loc [(df_in [col_name]> fence_low)&(df_in [col_name] <fence_high)]」という行でエラー「ValueError:多次元キーでインデックスを作成できません」が発生します
Imran Ahmad Ghazali

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を選択するのはかなり安全だと思います。betweenとこのように分位数を使用することは、かなり構文です。
PascalVKooten

18

私は答えを見ていないのでとの契約という数値非数値属性は、ここで補完答えです。

数値属性の外れ値のみを削除したい場合があります(カテゴリー変数は外れ値になることはほとんどありません)。

関数定義

非数値属性も存在する場合にデータを処理するように@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エリアと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エリアとSalePrice


2
素晴らしい解決策!ヘッドがアップしたようreduce=False以来、廃止されているpandasバージョン0.23.0
RK1

の代わりresult_type='reduce'reduce=False
Ekaba Bisong


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、平均からの標準偏差が3未満かどうかに基づいて変換されます。


これは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
""")

テキストデータをpandasデータフレームに読み込みます。

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
OPが何かをしたい理由を推測できません。
RajeshM
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.