欠損値(NaN)取り扱い方法を解説 | 機械学習におけるデータ予測精度向上のための手法
はじめに
機械学習・データ分析の前処理として、欠損値(NaN)を処理する必要があります。欠損値の処理方法としては次のような方法が考えられます。
- 削除:欠損値を含む行または列を削除する方法。ただし、データが少ない場合や欠損値の割合が高い場合はデータが減ることに注意が必要。
- 置き換え:欠損値に代表的な値を設定する方法。数値の場合は、平均値や中央値、最頻値に置き換える方法。カテゴリカルデータの場合は最頻値や特定の値を代入。
- 欠損値フラグ:欠損値を示す専用のフラグを設定する方法。これにより、欠損値の情報が保持され、後続の処理で適切な扱いが可能。
ここでは、2つめの置き換えを行うコードを紹介します。
データが多い場合は、削除なども有効です。また、欠損値を示すフラグを設定したり、列の値が全て正の場合は、欠損値は-1にして区別できるようにするなども有効です。
nanを除去する
自分で処理する(fillna()
)
pandasのfillna
を使って、自分で置き換える方法です。まず、動作確認用のdf
を以下のように作成したとします。
import math
import pandas as pd
df = pd.DataFrame([[1,2,3],[3,math.nan,5],[math.nan, 3, 4]], columns=['A','B','C'])
df
A | B | C | |
---|---|---|---|
0 | 1.0 | 2.0 | 3 |
1 | 3.0 | NaN | 5 |
2 | NaN | 3.0 | 4 |
フレーム全体を置換
例えば、データフレーム(df
)全体のNaNを0に置き換える場合は、以下のコードを実行します。
df = df.fillna(0)
A | B | C | |
---|---|---|---|
0 | 1.0 | 2.0 | 3 |
1 | 3.0 | 0.0 | 5 |
2 | 0.0 | 3.0 | 4 |
NaNは、0に置き換えられます。
特例列を置換
なお、特定列を置換する場合は以下のように列を指定できます。
df = df.fillna({'A': -1.0})
A | B | C | |
---|---|---|---|
0 | 1.0 | 2.0 | 3 |
1 | 3.0 | NaN | 5 |
2 | -1.0 | 3.0 | 4 |
他の列をコピー
他の列をコピーしたい場合には以下のようにすればOKです。
df = df.fillna({'B':df['C']})
A | B | C | |
---|---|---|---|
0 | 1.0 | 2.0 | 3 |
1 | 3.0 | 5.0 | 5 |
2 | NaN | 3.0 | 4 |
平均値で置き換え
また、各列毎の平均値に置き換えたい場合などは以下のようにします。
df = df.fillna(df.mean(axis=0))
A | B | C | |
---|---|---|---|
0 | 1.0 | 2.0 | 3 |
1 | 3.0 | 2.5 | 5 |
2 | 2.0 | 3.0 | 4 |
A列とB列のNaNは、各列の平均値と置換されます。このように、fillna
を使って各列のNaNを置き換えすることができます。
Simple Imputer
を使う(scikit-learn)
scikit-learnのSimpleImputer
を使っても欠損値の置き換えが可能です。まず、sklearnをインポートします。
from sklearn.impute import SimpleImputer
データフレームを一気に変換する例です。
imp = SimpleImputer(strategy="mean")
imp.fit(df)
df = pd.DataFrame(imp.transform(df), columns=df.columns)
最初の行でSimpleImputerのオブジェクトを作成しています。strategy
としては以下が設定できます。
mean | 平均値と置き換え |
median | 中央値と置き換え |
most_frequent | 最頻値と置き換え |
constant | 固定値で置き換えl。固定値はfill_value パラメータで設定 |
fit
で、各列に対して置き換える値が計算し、transform
で変換を実行します。なお、fit_transform()
という計算・置き換えを一気に行う関数もありますが、トレーニングデータで計算した値で、トレーニングデータ、テストデータの両方の置き換えをしたい場合があるので、個別で行う方法を覚えていた方が良いです。
なお、各列を個別に変換(異なるstrategyで変換)したい場合には、書き方注意です.
imps = {}
for col, strategy in zip(df.columns, ['median', 'mean', 'mean']):
imps[col] = SimpleImputer(strategy=strategy)
df[col] = imps[col].fit_transform(np.array(df[col]).reshape(-1, 1))
上のコードのように、各列だけを変換する場合にはnumpyの配列に変換し、1列の行列に変換して入力する必要があります。
予測で用いるときは
新たに入力されたデータに対して予測などを行う場合には、各列の欠損値をどのような値で埋めたのかを覚えておき、新しい入力データにも同じ処理を行う必要があります。例えば、fillnaメソッドを使用する場合は、各列の中央値や平均値などで置換したデータを記憶しておく必要があります。また、SimpleImputerを使用する場合も、後で参照できるように変換毎に別のオブジェクトとして記憶しておきます。上記の例では、imps
に記憶しています。