統計学において、他の値から大きく外れた値のことを外れ値というが、あるデータセットからこの外れ値を検出する方法を今回は紹介する。

サンプルデータの用意

まずはサンプルとして、1000行・4列の正規分布をした乱数の入ったデータフレームを用意する。

import pandas as pd

df = pd.DataFrame(np.random.randn(1000, 4))

データフレームに対し、describeメソッドを実行し各種統計データを確認してみよう。

df.describe()
#                  0            1            2            3
# count  1000.000000  1000.000000  1000.000000  1000.000000
# mean      0.061963    -0.018152    -0.009255    -0.071885
# std       0.981119     1.003547     1.025437     1.036963
# min      -3.561692    -3.090631    -3.084243    -3.396022
# 25%      -0.573395    -0.710958    -0.747221    -0.788857
# 50%       0.055481    -0.027430     0.002876    -0.059368
# 75%       0.702462     0.664116     0.688545     0.624966
# max       3.102698     3.604496     3.354750     2.952821

外れ値を検出する

今回4つの列を用意したが、まずはそのうち一つから外れ値を検出してみる。

例として、外れ値を絶対値3以上と設定して考える。

data = df[2]

data[np.abs(data > 3)]
# 453    3.164858
# 572    3.360151
# 979    3.459101
# Name: 2, dtype: float64

上記の例では3つの外れ値が見つかった。

外れ値を持つ行を選択する

次は絶対値3以上の外れ値を一つでも持つ行を全て選択してみる。

data = df[(np.abs(df) > 3).any(1)]
print(data)

#             0         1         2         3
# 138  0.541298  0.018690 -0.262796 -3.301846
# 143 -2.993869  0.656929  1.150055  3.275890
# 221 -2.262992  0.022498  3.085838  0.669413
# 374 -1.625388  0.352799  3.252707 -0.709602
# 417  3.058626 -0.454211  0.418938 -0.363925
# 773 -1.074662 -0.137747 -3.461462  1.753082
# 971  3.813151 -0.544527  1.194872 -1.384824
# 981 -0.996548 -3.589028 -0.538962 -2.114824
# 993  0.087114  3.100599  0.895598  0.642445

外れ値を別の値に変換する

検出した外れ値を別の値に変換する例も紹介しておこう。

df[np.abs(df) > 3] = np.sign(df) * 3
df.describe()

#                  0            1            2            3
# count  1000.000000  1000.000000  1000.000000  1000.000000
# mean     -0.008462     0.034518    -0.032374     0.004564
# std       1.028697     1.003309     1.019777     0.990512
# min      -2.820792    -3.000000    -3.000000    -3.000000
# 25%      -0.705405    -0.676406    -0.722525    -0.675062
# 50%      -0.024764     0.058775    -0.054462    -0.031949
# 75%       0.662883     0.712612     0.625067     0.673057
# max       3.000000     2.813415     3.000000     2.814021

上記のコードで、np.sign(df)という文があるが、これは引数の値の正負に応じて「1」または「-1」を返す。

headメソッドで最初の5行を取り出してみたが、こうして見ると分かりやすいだろう。

df.head()
#           0         1         2         3
# 0 -0.622233 -0.614729  1.845212 -0.801630
# 1  0.617012 -0.554088  0.634067  0.676856
# 2  0.165701  0.235992 -1.468626  0.638006
# 3  0.933732  1.353240  0.301985 -0.045967
# 4 -0.682327 -0.790911 -1.655082 -1.581637

np.sign(df).head()
#      0    1    2    3
# 0 -1.0 -1.0  1.0 -1.0
# 1  1.0 -1.0  1.0  1.0
# 2  1.0  1.0 -1.0  1.0
# 3  1.0  1.0  1.0 -1.0
# 4 -1.0 -1.0 -1.0 -1.0