LESSON

欠損値処理

田中VPoE「NetShop社のデータを見てみたら、案の定、欠損値がたくさんある。顧客の年齢が未入力だったり、商品カテゴリが空だったり。」

あなた「欠損値がある行は全部削除すればいいんじゃないですか?」

田中VPoE「それは危険だ。欠損が多いと分析対象が大幅に減るし、欠損がランダムでない場合はバイアスが生じる。欠損のパターンを理解してから対処法を選ぶ必要があるんだ。」

欠損値とは

欠損値(Missing Value)は、データが記録されていない値のことです。Pandasでは NaN(Not a Number)として表現されます。

import pandas as pd
import numpy as np

# 欠損値の確認
df.isnull().sum()           # カラムごとの欠損数
df.isnull().mean() * 100    # カラムごとの欠損率(%)

# 欠損値のあるカラムだけ表示
missing = df.isnull().sum()
missing[missing > 0].sort_values(ascending=False)

欠損パターンの分析

欠損が発生するメカニズムには3つのパターンがあります:

パターン英語説明
完全にランダムMCAR欠損が他の変数と無関係データ入力のランダムエラー
条件付きランダムMAR欠損が観測変数に依存若い顧客ほど年齢を入力しない
ランダムでないMNAR欠損が欠損値自体に依存高所得者ほど所得を入力しない

パターンの確認方法

# 欠損の有無でグループ分け
has_age = df[df['age'].notna()]
no_age = df[df['age'].isna()]

# 他の変数の分布を比較
print("年齢あり - 平均購入額:", has_age['amount'].mean())
print("年齢なし - 平均購入額:", no_age['amount'].mean())

# 欠損パターンの可視化
import seaborn as sns
import matplotlib.pyplot as plt

# 欠損値のヒートマップ
plt.figure(figsize=(12, 6))
sns.heatmap(df.isnull(), cbar=True, yticklabels=False)
plt.title('欠損値パターン')
plt.show()

欠損値の対処法

1. 削除

# 欠損を含む行を削除
df_clean = df.dropna()

# 特定カラムの欠損を含む行を削除
df_clean = df.dropna(subset=['customer_id', 'amount'])

# 欠損率が高いカラムを削除
threshold = 0.5  # 50%以上欠損
cols_to_drop = df.columns[df.isnull().mean() > threshold]
df_clean = df.drop(columns=cols_to_drop)

削除が適切な場合

  • 欠損がMCARで、欠損率が5%未満
  • 分析に不要なカラムの欠損
  • レコード数が十分に多い場合

2. 定数補完

# 固定値で補完
df['category'] = df['category'].fillna('不明')
df['amount'] = df['amount'].fillna(0)

3. 統計量による補完

# 平均値で補完
df['age'] = df['age'].fillna(df['age'].mean())

# 中央値で補完(外れ値に強い)
df['amount'] = df['amount'].fillna(df['amount'].median())

# 最頻値で補完(カテゴリカル変数向け)
df['category'] = df['category'].fillna(df['category'].mode()[0])

4. グループ別補完

# カテゴリ別の中央値で補完
df['price'] = df.groupby('category')['price'].transform(
    lambda x: x.fillna(x.median())
)

# 地域別の平均年齢で補完
df['age'] = df.groupby('region')['age'].transform(
    lambda x: x.fillna(x.mean())
)

5. 前方補完・後方補完(時系列)

# 前方補完(直前の値で埋める)
df['value'] = df['value'].fillna(method='ffill')

# 後方補完(直後の値で埋める)
df['value'] = df['value'].fillna(method='bfill')

# 線形補間
df['value'] = df['value'].interpolate(method='linear')

6. KNN補完(高度な手法)

from sklearn.impute import KNNImputer

imputer = KNNImputer(n_neighbors=5)
df_numeric = df.select_dtypes(include=[np.number])
df_imputed = pd.DataFrame(
    imputer.fit_transform(df_numeric),
    columns=df_numeric.columns
)

補完戦略の選び方

データの特性推奨手法理由
欠損率5%未満・MCAR行削除データ損失が少ない
カテゴリカル変数最頻値 or 「不明」カテゴリ分布を保持
正規分布の数値平均値代表値として適切
歪んだ分布の数値中央値外れ値に頑健
グループ内差が大きいグループ別補完グループの特性を反映
時系列データ前方補完 or 線形補間時間的な連続性を保持
変数間の相関が強いKNN補完多変量の関係を考慮

欠損値処理の注意点

  1. 欠損フラグの作成:補完した場合、元々欠損だったことを示すフラグ列を追加しておく
df['age_was_missing'] = df['age'].isnull().astype(int)
df['age'] = df['age'].fillna(df['age'].median())
  1. 補完前後の分布確認:補完によって分布が大きく変わっていないか確認する

  2. ドキュメント化:どの手法で補完したかを分析レポートに記録する

まとめ

項目ポイント
欠損パターンMCAR/MAR/MNARの3パターンを理解する
まず分析欠損率と欠損パターンを確認してから対処法を選ぶ
削除MCARで少量の場合のみ
補完データの特性に応じて適切な手法を選択
記録欠損フラグの追加と処理内容のドキュメント化

チェックリスト

  • 欠損値の3パターン(MCAR/MAR/MNAR)を説明できる
  • 欠損率の確認方法を知っている
  • データの特性に応じた補完手法を選択できる
  • 欠損フラグの重要性を理解している

次のステップへ

欠損値処理を学びました。次は、複数のデータセットの結合と変形について学びましょう。


推定読了時間:30分