今日も窓辺でプログラム

外資系企業勤めのエンジニアが勉強した内容をまとめておくブログ

SimpleImputerで欠損値を補完する

この記事について

Scikit-learnのSimpleImputerクラスを使用すると、データセットの欠損値を簡単に補完することができます。Kaggleの練習用Competitionでも使われているタイタニックのデータセットを例に、SimpleImputerをどのように使うか見ていきたいと思います。

データセット

タイタニックデータセットのロード

今回は seaboarn を使用してタイタニックのデータセットをロードします。

import seaborn as sns
df = sns.load_dataset("titanic") 
print(df.head())
   survived  pclass     sex   age  ...  deck  embark_town  alive  alone
0         0       3    male  22.0  ...   NaN  Southampton     no  False
1         1       1  female  38.0  ...     C    Cherbourg    yes  False
2         1       3  female  26.0  ...   NaN  Southampton    yes   True
3         1       1  female  35.0  ...     C  Southampton    yes  False
4         0       3    male  35.0  ...   NaN  Southampton     no   True

[5 rows x 15 columns]

このように df にはタイタニックの乗客の年齢、性別といったデータが格納された状態になります。

欠損値の状況を確認する

info() を使って各列の欠損値の状況を確認してみます。

df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 15 columns):
survived       891 non-null int64
pclass         891 non-null int64
sex            891 non-null object
age            714 non-null float64
sibsp          891 non-null int64
parch          891 non-null int64
fare           891 non-null float64
embarked       889 non-null object
class          891 non-null category
who            891 non-null object
adult_male     891 non-null bool
deck           203 non-null category
embark_town    889 non-null object
alive          891 non-null object
alone          891 non-null bool
dtypes: bool(2), category(2), float64(2), int64(4), object(5)
memory usage: 80.6+ KB

データのサンプル数は891で、age, embarked, embark_town, deck の4列に欠損値があることがわかります。下記コードで具体的な欠損値の個数もわかります。

df.isna().sum()
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

年齢データが欠損しているサンプル

年齢カラムに入っているデータの概要を見てみます。

df.age.describe()
count    714.000000
mean      29.699118
std       14.526497
min        0.420000
25%       20.125000
50%       28.000000
75%       38.000000
max       80.000000
Name: age, dtype: float64

平均が29.69、中央値が28で、最年少が0.42歳、最高齢が80歳というデータのようです。

年齢が欠損しているサンプルは下記のようにして取り出せます。

print(df[df.age.isna()].head())
    survived  pclass     sex  age  ...  deck  embark_town  alive alone
5          0       3    male  NaN  ...   NaN   Queenstown     no  True
17         1       2    male  NaN  ...   NaN  Southampton    yes  True
19         1       3  female  NaN  ...   NaN    Cherbourg    yes  True
26         0       3    male  NaN  ...   NaN    Cherbourg     no  True
28         1       3  female  NaN  ...   NaN   Queenstown    yes  True

[5 rows x 15 columns]

欠損値の補完

平均値での補完

では実際に年齢の補完をしてみます。SimpleImputerクラスではstrategyという引数を指定できます。これは欠損値を補完する方法を指定するもので、平均値 (mean)、中央値 (median)、最頻値 (most_frequent)、定数 (constant) の4つの中からしていできます。例えば、年齢を平均値で補完する場合は下記のようなコードになります。

from sklearn.impute import SimpleImputer

mean_imputer = SimpleImputer(strategy='mean')
mean_imputed_df = df.copy()
mean_imputed_df[['age']] = mean_imputer.fit_transform(mean_imputed_df[['age']])

print(mean_imputed_df[df.age.isna()].head())
    survived  pclass     sex        age  ...  deck  embark_town  alive alone
5          0       3    male  29.699118  ...   NaN   Queenstown     no  True
17         1       2    male  29.699118  ...   NaN  Southampton    yes  True
19         1       3  female  29.699118  ...   NaN    Cherbourg    yes  True
26         0       3    male  29.699118  ...   NaN    Cherbourg     no  True
28         1       3  female  29.699118  ...   NaN   Queenstown    yes  True

[5 rows x 15 columns]

ageがNaNだったサンプルも、すべて平均値の29.69...という値で補完されている様子が確認できます。

中央値での補完

中央値や最頻値の場合も同様です。試しに中央値でも年齢を補完してみると下記のようになります。

median_imputer = SimpleImputer(strategy='median')
median_imputed_df = df.copy()
median_imputed_df[['age']] = median_imputer.fit_transform(median_imputed_df[['age']])

print(median_imputed_df[df.age.isna()].head())
    survived  pclass     sex   age  ...  deck  embark_town  alive alone
5          0       3    male  28.0  ...   NaN   Queenstown     no  True
17         1       2    male  28.0  ...   NaN  Southampton    yes  True
19         1       3  female  28.0  ...   NaN    Cherbourg    yes  True
26         0       3    male  28.0  ...   NaN    Cherbourg     no  True
28         1       3  female  28.0  ...   NaN   Queenstown    yes  True

[5 rows x 15 columns]

今度はageの列が中央値である28.0で補完されているようすが確認できます。

カテゴリ変数の補完

これまでは年齢という数値に対しての補完でしたが、カテゴリ変数に対する補完も同様に行うことができます。例えば embark_town という乗船した街を表す変数の欠損は2サンプルありました。乗船場所は3種類しか存在しないようで、891人中644人はSouthamptonという街から乗船しているようです。

df.embark_town.describe()
count             889
unique              3
top       Southampton
freq              644
Name: embark_town, dtype: object

embark_townが欠損している人のデータは下記で参照できます。確かに2名分しか欠損データがないようです。

print(df[df.embark_town.isna()].head())
     survived  pclass     sex   age  ...  deck  embark_town  alive alone
61          1       1  female  38.0  ...     B          NaN    yes  True
829         1       1  female  62.0  ...     B          NaN    yes  True

[2 rows x 15 columns]

カテゴリ変数である embark_town も SimpleImputerを使って補完することができます。カテゴリ変数には平均値や中央値は存在しないので、最頻値か定数を指定してあげる必要があります。今回は最頻値をしていしてみます。

most_frequent_imputer = SimpleImputer(strategy='most_frequent')
embark_town_imputed_df = df.copy()
embark_town_imputed_df['embark_town'] = most_frequent_imputer.fit_transform(embark_town_imputed_df[['embark_town']])

print(embark_town_imputed_df[df.embark_town.isna()].head())
     survived  pclass     sex   age  ...  deck  embark_town  alive alone
61          1       1  female  38.0  ...     B  Southampton    yes  True
829         1       1  female  62.0  ...     B  Southampton    yes  True

[2 rows x 15 columns]

確かに、embark_townの列が最頻値であるSouthamptonで補完されているようです。