データの欠損値把握と補間

Posted on 2020/02/01 in 機械学習 , Updated on: 2020/02/01

はじめに

機械学習の前処理として非常に重要な作業の一つである欠損値への対応として、データないの欠損値の把握と補間の方法を紹介する。通常、大半が欠損している特徴量は削除してもほとんど問題はないが、そうではない時は対処する必要がある。titanic dataset を使用して各種補間方法を紹介する。

In [73]:
# データとライブラリの準備
import pandas as pd
import numpy as np

train = pd.read_csv('titanic.csv')
print('titanic data shape:', train.shape)
print('Missing values of each columns')
train.isna().sum()
titanic data shape: (891, 15)
Missing values of each columns
Out[73]:
survived         0
pclass           0
sex              0
age            177
sibsp            0
parch            0
fare             0
embarked         2
class            0
who              0
adult_male       0
deck           688
embark_town      2
alive            0
alone            0
dtype: int64

training データ(891個のサンプル)内で、欠損値を持つのは、age, embarked, deck, embark_town の4つ。それぞれ補間後の値がどう変わったかを確認するために、それぞれの特徴量の値が NaN の index を取得しておく。

In [74]:
age_nan_index = train[train['age'].isna()].index
embarked_nan_index = train[train['embarked'].isna()].index
deck_nan_index = train[train['age'].isna()].index
embark_town_nan_index = train[train['embark_town'].isna()].index

欠損値を把握する

今回使用するデータはサンプル数、特徴量数ともに比較的少ないため、上記で用いた isna().sum() である程度把握できる。しかし、非常に大きなデータを扱う際は、特徴量ごとに欠損量を並べたら新たな dataframe を作成しておくと便利である。

ここでは、dataframe を渡すと、欠損値をまとめた新たな dataframe を返す関数を紹介する。

In [75]:
def missing_values_table(df):
    
    # 各特徴量の欠損値の合計
    mis_val = df.isnull().sum()
    
    # 各特徴量の欠損値の割合 (%)
    mis_val_percent = 100 * df.isnull().sum() / len(df)

    # 欠損値 dataframe を作成して、カラム名を変更
    mis_val_table = pd.concat([mis_val, mis_val_percent], axis=1)
    mis_val_table_ren_columns = mis_val_table.rename(
        columns={0: 'Missing Values', 1: '% of Total Values'})

    # 割合のカラムでソート (降順)。欠損値が 0 の特徴量は除外
    mis_val_table_ren_columns = mis_val_table_ren_columns[
        mis_val_table_ren_columns.iloc[:,1] != 0].sort_values(
        '% of Total Values', ascending=False).round(1)
    
    # まとめ情報を出力
    print('Selected dataframe has ' + str(df.shape[1]) + ' columns.')
    print('There are ' + str(mis_val_table_ren_columns.shape[0]) + \
          ' columns that have missing values.')

    return mis_val_table_ren_columns

この関数に、今回の training data を渡してみる。

In [76]:
missing_values_table(train)
Selected dataframe has 15 columns.
There are 4 columns that have missing values.
Out[76]:
Missing Values % of Total Values
deck 688 77.2
age 177 19.9
embarked 2 0.2
embark_town 2 0.2

数値データの補間 (numeric imputation)

scikit learnImputer クラスを利用して各特徴量の平均値や最頻値、中央値で、欠損値を埋める。数値データの統計量を用いて欠損値を補間するので、いったん数値データのみ(age)に適用する。

まず、Imputer クラスを作成する。引数として補間する値 missin_values, 補間方法 strategy, 補間する軸 axis (0で列、1で行)を指定する。

In [46]:
from sklearn.preprocessing import Imputer

# 平均値で補間する Imputer
imputer_mean = Imputer(missing_values='NaN', strategy='mean', axis=0)
# 中央値で補間する Imputer
imputer_medi = Imputer(missing_values='NaN', strategy='median', axis=0)
# 最頻値で補間する Imputer
imputer_freq = Imputer(missing_values='NaN', strategy='most_frequent',
                       axis=0)

# それぞれの imputer を training data へ適用して変換。dataframe を渡しても np.array が返る
mean = imputer_mean.fit_transform(train[['age']])
medi = imputer_medi.fit_transform(train[['age']])
freq = imputer_freq.fit_transform(train[['age']])

補間が実行されたので、それぞれどのように値が変化しているかを確認する。また、元のデータの統計量(平均値、中央値、最頻値)を確認する。

In [60]:
# 元のデータ
origin_mean = train['age'].mean()
origin_medi = train['age'].median()
origin_freq = train['age'].mode().values

# 変換後のデータ
imputed_mean = mean[age_nan_index[0]]
imputed_medi = medi[age_nan_index[0]]
imputed_freq = freq[age_nan_index[0]]

print(f'Original data mean {origin_mean}, imputed data value {imputed_mean}')
print(f'Original data median {origin_medi}, imputed data value {imputed_medi}')
print(f'Original data frequent {origin_freq}, imputed data value {imputed_freq}')
Original data mean 29.69911764705882, imputed data value [29.69911765]
Original data median 28.0, imputed data value [28.]
Original data frequent [24.], imputed data value [24.]

いずれも、元の特徴量の統計量と同じ値で補完されてる。

カテゴリデータの補間 (categorical imputation)

Imputer を用いて、カテゴリ特徴量を補間する際には、LabelEncodeOneHotEncode を事前に実行して、数値データへ変更する必要がある。また、カテゴリデータなので、補間方法を決定する際には注意が必要。統計補間を利用するよりも、fillna(-999) などでの外れ値で欠損を埋めるか、最頻値で埋めるなどの方法がある。