欠損値の補完や取り扱いについて(python)

分析に利用するデータには多くの場合、なんらかの理由により記録されなかった値、欠損値 (missing data) が含まれる

欠損値があると統計的処理や、機械学習の処理がそのまま適用できなかったり、結果にバイアスが生じてしまうので、データ分析の際にはなんらかの処理をする必要がある。

さまざま処理の方法についてまとめた

データの準備など

import numpy as np
import pandas as pd
from sklearn import datasets

diabetes = datasets.load_diabetes()
df = pd.DataFrame(diabetes['data'],columns=diabetes['feature_names'])
display(df.iloc[:,:5].head())
agesexbmibps1
00.0380760.0506800.0616960.021872-0.044223
1-0.001882-0.044642-0.051474-0.026328-0.008449
20.0852990.0506800.044451-0.005671-0.045599
3-0.089063-0.044642-0.011595-0.0366560.012191
40.005383-0.044642-0.0363850.0218720.003935

欠損値の有無は 'isnull' メソッドで確認できる

print(df.isnull().any(axis=0))
age    False
sex    False
bmi    False
bp     False
s1     False
s2     False
s3     False
s4     False
s5     False
s6     False
dtype: bool

今回のデータは欠損値がないので意図的に欠損値を作成する

df.iloc[0:2,0] = np.nan
df.iloc[1:3,1] = np.nan
df.iloc[2:4,2] = np.nan

display(df.iloc[:,:5].head())
agesexbmibps1
0NaN0.0506800.0616960.021872-0.044223
1NaNNaN-0.051474-0.026328-0.008449
20.085299NaNNaN-0.005671-0.045599
3-0.089063-0.044642NaN-0.0366560.012191
40.005383-0.044642-0.0363850.0218720.003935

欠損値処理方法

欠損値を持つデータを除去する

(もしくは欠損値を持つ変数を除去する)

# 欠損値を持つデータを除去する
df_ = df.dropna(axis=0)
print(f'{df.shape}=>{df_.shape}')

# 欠損値を持つ変数を除去する
df_ = df.dropna(axis=1)
print(f'{df.shape} => {df_.shape}')
(442, 10)=>(438, 10)
(442, 10) => (442, 7)

なんらかの統計量で補完する

平均値や中央値、最頻値など

# 平均値で補完
df_mean = df.copy()
for col in df.columns:
    df_mean[col] = df_mean[col].fillna(df_mean[col].mean())

print('平均値で補完')
display(df_mean.iloc[:,:3].head())

# 中央値で補完
df_median = df.copy()
for col in df.columns:
    df_median[col] = df_median[col].fillna(df_median[col].median())

print('中央値で補完')
display(df_median.iloc[:,:3].head())

# 最頻値で補完
df_mode = df.copy()
for col in df.columns:
    df_mode[col] = df_mode[col].fillna(df_mode[col].mode()[0])

print('最頻値で補完')
display(df_mode.iloc[:,:3].head())

平均値で補完

agesexbmi
0-0.0000820.0506800.061696
1-0.000082-0.000014-0.051474
20.085299-0.000014-0.000075
3-0.089063-0.044642-0.000075
40.005383-0.044642-0.036385

中央値で補完

agesexbmi
00.0053830.0506800.061696
10.005383-0.044642-0.051474
20.085299-0.044642-0.007284
3-0.089063-0.044642-0.007284
40.005383-0.044642-0.036385

最頻値で補完

agesexbmi
00.0162810.0506800.061696
10.016281-0.044642-0.051474
20.085299-0.044642-0.030996
3-0.089063-0.044642-0.030996
40.005383-0.044642-0.036385

予測値で補完する

例として、ランダムフォレストでの予測値で補完してみる。

from sklearn.ensemble import RandomForestRegressor

# 欠損値のある変数を列挙
null_cols = df.columns[df.isnull().any(axis=0)]

df_rf = df.copy()
for col in null_cols:

    df_temp = df_rf.copy()

    # データの抜き出し
    X = df_temp.drop(col,axis=1)
    y = df_temp[col]

    y_train = y.dropna()
    y_test = y.loc[y.isnull()]

    X_train = X.loc[y_train.index].fillna(-999)
    X_test = X.loc[y_test.index].fillna(-999)

    # ランダムフォレストでの欠損値予測
    rf = RandomForestRegressor()
    rf.fit(X_train,y_train)
    y_pred = rf.predict(X_test)

    # 予測値で補完する
    df_rf.loc[y_test.index,col] = y_pred

display(df_rf.iloc[:,:3].head())
agesexbmi
00.0313190.0506800.061696
1-0.021062-0.037969-0.051474
20.0852990.0106450.003904
3-0.089063-0.044642-0.013406
40.005383-0.044642-0.036385

欠損値の有無の情報使って特徴量を生成する

欠損値がランダムに出現するのではなく何らか理由があって出現している場合には、欠損値の有無やその数などを特徴量とすることで、予測精度が向上する場合もある。

# 欠損値のある変数を列挙しておく
null_cols = df.columns[df.isnull().any(axis=0)]

# 欠損値の有無を特徴量にする
df_null = df.copy()
for col in null_cols:
    df_null[f'{col}_null'] = 0
    df_null.loc[df_null[col].isnull(), f'{col}_null'] = 1

print("欠損値の有無")
display(df_null.iloc[:,-3:].head())

# 欠損値の個数を特徴量にする
df_ncount = df.copy()

df_ncount['null_count'] = 0
df_ncount['null_count'] = df.isnull().sum(axis=1).values

print("欠損値の個数")
display(df_ncount.iloc[:,-1:].head())

欠損値の有無

age_nullsex_nullbmi_null
0100
1110
2011
3001
4000

欠損値の個数

null_count
01
12
22
31
40

その他(メモ)

  1. 時系列データの処理の場合には、前後の値で補完することが多い。
    • fillna(method='ffill'):前方補完
    • fillna(method='bfill'):後方補完
    • df.interpolate():線形補完
  2. XGboostなどGBDT系のライブラリでは欠損値があってもOK

その他にも色々な補完方法があるみたい。
https://qiita.com/FukuharaYohei/items/9830d5760595619352a5#iterativeimputer%E9%96%A2%E6%95%B0

参考

参考HP

関連書籍








udemy