STEP 27:欠損値処理と特徴量作成

🔧 STEP 27: 欠損値処理と特徴量作成

実データで必須の欠損値対処と、新しい特徴量を作る技術

📋 このステップで学ぶこと

  • 欠損値の確認と可視化
  • SimpleImputer(基本的な補完)
  • KNN Imputer(近傍値で補完)
  • Iterative Imputer(反復的補完)
  • 交互作用特徴量の作成
  • 集計特徴量の作成
  • 日時特徴量の作成

⏱️ 目安時間:2.5時間

📝 演習問題:7問

🎯 1. 欠損値の確認

実際のデータには欠損値(NaN、空白、Null)がよく含まれています。まずは欠損値を確認する方法を学びましょう。

欠損値とは:

データが入っていない箇所のこと。理由は様々です。
・入力ミス、システムエラー
・アンケートで「回答しない」を選んだ
・そもそも該当しない(例:未婚者の配偶者年齢)

# 欠損値の確認方法 import pandas as pd import numpy as np # 欠損値を含むサンプルデータを作成 np.random.seed(42) df = pd.DataFrame({ ‘age’: [25, 30, np.nan, 45, 50, np.nan, 35, 40], # 年齢(2件欠損) ‘income’: [300, np.nan, 500, 600, np.nan, 400, 350, np.nan], # 年収(3件欠損) ‘gender’: [‘男’, ‘女’, ‘男’, np.nan, ‘女’, ‘男’, ‘女’, ‘男’], # 性別(1件欠損) ‘purchased’: [1, 0, 1, 1, 0, 1, 0, 1] # 購入したか(欠損なし) }) print(“サンプルデータ:”) print(df)
サンプルデータ: age income gender purchased 0 25.0 300.0 男 1 1 30.0 NaN 女 0 2 NaN 500.0 男 1 3 45.0 600.0 NaN 1 4 50.0 NaN 女 0 5 NaN 400.0 男 1 6 35.0 350.0 女 0 7 40.0 NaN 男 1

欠損値の数を確認する

欠損値確認の3つの方法:
  • df.isnull().sum():各列の欠損数
  • df.isnull().sum().sum():全体の欠損数
  • df.isnull().mean() * 100:各列の欠損率(%)
# 欠損値の確認 # 方法1: 各列の欠損数 print(“各列の欠損数:”) print(df.isnull().sum()) # 方法2: 全体の欠損数 print(f”\n全体の欠損数: {df.isnull().sum().sum()}”) # 方法3: 欠損率(%) print(“\n各列の欠損率:”) print((df.isnull().mean() * 100).round(1))
各列の欠損数: age 2 income 3 gender 1 purchased 0 dtype: int64 全体の欠損数: 6 各列の欠損率: age 25.0 income 37.5 gender 12.5 purchased 0.0 dtype: float64
💡 欠損値への対処方針
  • 欠損率が低い(〜5%):削除または単純な補完
  • 欠損率が中程度(5〜30%):高度な補完(KNN Imputerなど)
  • 欠損率が高い(30%以上):その列自体を削除するか、「欠損かどうか」を新しい特徴量に

📊 2. SimpleImputerで欠損値を補完

SimpleImputerは、欠損値を単一の値(平均、中央値、最頻値など)で補完する最も基本的な方法です。

数値データの補完(平均値・中央値)

SimpleImputerの主なパラメータ:
  • strategy='mean':平均値で補完(デフォルト)
  • strategy='median':中央値で補完(外れ値がある場合に推奨)
  • strategy='most_frequent':最頻値で補完(カテゴリデータ向け)
  • strategy='constant', fill_value=0:指定した値で補完
# SimpleImputerの使い方(数値データ) from sklearn.impute import SimpleImputer import numpy as np # 数値列だけ抽出 df_numeric = df[[‘age’, ‘income’]].copy() print(“補完前:”) print(df_numeric) print(f”\nageの平均: {df_numeric[‘age’].mean():.1f}”) print(f”incomeの平均: {df_numeric[‘income’].mean():.1f}”)
# SimpleImputerを作成(平均値で補完) imputer_mean = SimpleImputer( strategy=’mean’ # 平均値で補完 ) # fit_transform()で補完を実行 # fit(): 各列の平均値を計算 # transform(): 欠損値を平均値で置き換え df_numeric_imputed = imputer_mean.fit_transform(df_numeric) # NumPy配列が返るのでDataFrameに変換 df_numeric_imputed = pd.DataFrame( df_numeric_imputed, columns=[‘age’, ‘income’] ) print(“平均値で補完後:”) print(df_numeric_imputed)
補完前: age income 0 25.0 300.0 1 30.0 NaN 2 NaN 500.0 3 45.0 600.0 4 50.0 NaN 5 NaN 400.0 6 35.0 350.0 7 40.0 NaN ageの平均: 37.5 incomeの平均: 430.0 平均値で補完後: age income 0 25.00 300.0 1 30.00 430.0 2 37.50 500.0 3 45.00 600.0 4 50.00 430.0 5 37.50 400.0 6 35.00 350.0 7 40.00 430.0

カテゴリデータの補完(最頻値)

# カテゴリデータの補完(最頻値) # カテゴリ列だけ抽出 df_cat = df[[‘gender’]].copy() print(“補完前:”) print(df_cat) print(f”\n最頻値: {df_cat[‘gender’].mode()[0]}”) # SimpleImputerを作成(最頻値で補完) imputer_mode = SimpleImputer( strategy=’most_frequent’ # 最頻値で補完 ) # 補完を実行 df_cat_imputed = imputer_mode.fit_transform(df_cat) df_cat_imputed = pd.DataFrame(df_cat_imputed, columns=[‘gender’]) print(“\n最頻値で補完後:”) print(df_cat_imputed)
補完前: gender 0 男 1 女 2 男 3 NaN 4 女 5 男 6 女 7 男 最頻値: 男 最頻値で補完後: gender 0 男 1 女 2 男 3 男 4 女 5 男 6 女 7 男

🧠 3. 高度な欠損値補完

KNN Imputerの仕組みを理解しよう

📊 KNN Imputerとは

欠損値がある行に対して、最も似ている(距離が近い)k個の行を見つけ、それらの値の平均で補完します。

KNN Imputerが欠損値を補完する手順:

  1. 欠損値がある行を特定
  2. 欠損値がない行の中から、最も似ている(ユークリッド距離が近い)k個の行を見つける
  3. そのk個の行の該当する列の値を平均して補完
💡 なぜKNN Imputerを使うのか?

SimpleImputerは全データの平均で補完しますが、KNN Imputerは「似ているサンプル」の値を使うため、より適切な補完ができます。

例:若い人の年収が欠損している場合、全年齢の平均より、同年代の人の年収の平均の方が適切です。

KNN Imputerのパラメータを理解しよう

パラメータ 説明 デフォルト
n_neighbors 参照する近傍サンプル数(大きいと安定、小さいと個別性が高い) 5
weights ‘uniform’(等しく扱う)or ‘distance’(近いほど重み大) ‘uniform’
# KNN Imputer: 近傍のサンプルから補完 from sklearn.impute import KNNImputer import numpy as np # サンプルデータ(欠損値あり) # 各行が1サンプル、各列が1特徴量 X = np.array([ [1, 2, np.nan], # 行0: 3列目が欠損 [3, 4, 3], # 行1: 欠損なし [np.nan, 6, 5], # 行2: 1列目が欠損 [8, 8, 7], # 行3: 欠損なし [9, np.nan, 9] # 行4: 2列目が欠損 ]) print(“補完前のデータ:”) print(X) print(“\n欠損値の位置: (0,2), (2,0), (4,1)”)
# KNN Imputerで補完 # n_neighbors=2: 最も近い2つのサンプルを参照 imputer = KNNImputer(n_neighbors=2) # fit_transform()で欠損値を補完 X_imputed = imputer.fit_transform(X) print(“KNN Imputer補完後 (k=2):”) print(X_imputed.round(2)) # 補完の仕組みを確認(行0の3列目の欠損) # 行0 [1, 2, ?] に最も近い2つのサンプルは? # → 行1 [3, 4, 3] と 行2 [?, 6, 5] の3列目の平均で補完
補完前のデータ: [[ 1. 2. nan] [ 3. 4. 3.] [nan 6. 5.] [ 8. 8. 7.] [ 9. nan 9.]] 欠損値の位置: (0,2), (2,0), (4,1) KNN Imputer補完後 (k=2): [[1. 2. 4. ] [3. 4. 3. ] [2. 6. 5. ] [8. 8. 7. ] [9. 6. 9. ]]

Iterative Imputerの仕組みを理解しよう

📊 Iterative Imputerとは

欠損値がある列を目的変数、他の列を特徴量として回帰モデルを構築し、欠損値を予測します。これを全ての欠損列に対して繰り返し(iterative)行います。

Iterative Imputerの手順:

  1. まず、全ての欠損値を仮の値(平均など)で埋める
  2. 1つの欠損列を選び、その列を目的変数、他の列を特徴量として回帰モデルを学習
  3. 学習したモデルで欠損値を予測して更新
  4. 全ての欠損列に対して2〜3を繰り返す
  5. 値が収束するまで(または最大反復回数まで)繰り返す
# Iterative Imputer: 他の特徴量から予測して補完 # experimentalモジュールからenable_iterative_imputerをインポートする必要がある from sklearn.experimental import enable_iterative_imputer from sklearn.impute import IterativeImputer # Iterative Imputerで補完 imputer_iter = IterativeImputer( max_iter=10, # 最大10回繰り返す random_state=42 # 再現性のため ) X_imputed_iter = imputer_iter.fit_transform(X) print(“Iterative Imputer補完後:”) print(X_imputed_iter.round(2))
💡 KNN Imputer vs Iterative Imputer
手法 特徴 使いどころ
KNN Imputer 類似サンプルの平均で補完 特徴量間の関係がシンプルな場合
Iterative Imputer 回帰モデルで予測して補完 複雑な関係がある場合

ただし、計算コストが高いので、大規模データではSimpleImputerから試すことを推奨します。

✨ 4. 特徴量作成の基本

既存の特徴量から新しい特徴量を作ることを特徴量エンジニアリングと呼びます。モデルの性能向上に大きく貢献します。

交互作用特徴量とは?なぜ作るのか?

📊 交互作用特徴量とは

2つ以上の特徴量を掛け合わせたり、割り算したりして作る新しい特徴量です。

なぜ作るのか?単独では見えない特徴量間の関係性を捉えられるからです。

具体例で理解しよう:

  • 面積 = 長さ × 幅:長さと幅を別々に見るより、面積の方が価格との関係がわかりやすい
  • BMI = 体重 / 身長²:体重と身長を別々に見るより、BMIの方が健康状態を表す
  • 1人あたりの部屋数 = 部屋数 / 家族人数:住居の余裕度を表す
# 交互作用特徴量の作成 import pandas as pd import numpy as np # サンプルデータ(不動産データをイメージ) df = pd.DataFrame({ ‘length’: [10, 20, 15, 25], # 長さ(m) ‘width’: [5, 10, 8, 12], # 幅(m) ‘price_per_unit’: [100, 150, 120, 180] # 単価(万円/m²) }) print(“元データ:”) print(df)

交互作用特徴量を作成する

各特徴量を作る理由を考えながら作成しましょう:

# 交互作用特徴量を作成 # 面積 = 長さ × 幅 # なぜ?不動産では面積が価格の主要な決定要因 df[‘area’] = df[‘length’] * df[‘width’] # 総額 = 面積 × 単価 # なぜ?実際に支払う金額を計算 df[‘total_price’] = df[‘area’] * df[‘price_per_unit’] # 長さ/幅の比率 # なぜ?正方形に近いほど使いやすい土地かもしれない df[‘length_width_ratio’] = df[‘length’] / df[‘width’] print(“\n交互作用特徴量を追加:”) print(df)
元データ: length width price_per_unit 0 10 5 100 1 20 10 150 2 15 8 120 3 25 12 180 交互作用特徴量を追加: length width price_per_unit area total_price length_width_ratio 0 10 5 100 50 5000 2.000 1 20 10 150 200 30000 2.000 2 15 8 120 120 14400 1.875 3 25 12 180 300 54000 2.083
💡 交互作用特徴量を作るコツ
  • ドメイン知識を活用:業界の専門知識を使って意味のある特徴量を作る
  • 単位を考える:掛け算・割り算の結果が解釈可能な単位になっているか確認
  • 相関を確認:作った特徴量と目的変数の相関を確認して有効かチェック

📈 5. 集計特徴量の作成

groupby().agg()の使い方を理解しよう

📊 集計特徴量とは

グループ(顧客ID、カテゴリなど)ごとに統計量(合計、平均、最大など)を計算して作る特徴量です。

なぜ作るのか?個々のトランザクションデータを「顧客の特徴」としてまとめられるからです。

agg()で使える主な集計関数:

関数 説明 ビジネス例
'sum' 合計 総購入金額
'mean' 平均 平均購入単価
'count' 件数 購入回数
'max' 最大値 最高購入額
'min' 最小値 最低購入額
'std' 標準偏差 購入金額のばらつき

ステップ1:元データの確認

# グループ別の集計特徴量 import pandas as pd import numpy as np # サンプルデータ(顧客の購入履歴) # 1人の顧客が複数回購入している df = pd.DataFrame({ ‘customer_id’: [1, 1, 1, 2, 2, 3, 3, 3, 3], ‘product_category’: [‘食品’, ‘衣類’, ‘食品’, ‘電化製品’, ‘食品’, ‘衣類’, ‘衣類’, ‘食品’, ‘電化製品’], ‘amount’: [1000, 5000, 800, 30000, 1200, 8000, 6000, 500, 25000] }) print(“元データ(購入履歴):”) print(df) print(f”\n顧客数: {df[‘customer_id’].nunique()}人”) print(f”トランザクション数: {len(df)}件”)

ステップ2:顧客ごとの集計

# 顧客ごとの集計特徴量を作成 # groupby(‘customer_id’): 顧客IDでグループ化 # agg(): 複数の集計を一度に計算 customer_features = df.groupby(‘customer_id’).agg({ ‘amount’: [‘sum’, ‘mean’, ‘count’, ‘max’, ‘min’, ‘std’] }).reset_index() # カラム名をフラット化(2段階のカラム名を1段階に) # 元: (‘amount’, ‘sum’) → 新: ‘total_amount’ customer_features.columns = [ ‘customer_id’, # グループキー ‘total_amount’, # 合計 → 総購入金額 ‘avg_amount’, # 平均 → 平均購入単価 ‘purchase_count’, # 件数 → 購入回数 ‘max_amount’, # 最大 → 最高購入額 ‘min_amount’, # 最小 → 最低購入額 ‘std_amount’ # 標準偏差 → 購入金額のばらつき ] print(“顧客別の集計特徴量:”) print(customer_features)
元データ(購入履歴): customer_id product_category amount 0 1 食品 1000 1 1 衣類 5000 2 1 食品 800 3 2 電化製品 30000 4 2 食品 1200 5 3 衣類 8000 6 3 衣類 6000 7 3 食品 500 8 3 電化製品 25000 顧客数: 3人 トランザクション数: 9件 顧客別の集計特徴量: customer_id total_amount avg_amount purchase_count max_amount min_amount std_amount 0 1 6800 2266.67 3 5000 800 2329.90 1 2 31200 15600.00 2 30000 1200 20364.68 2 3 39500 9875.00 4 25000 500 10863.52
💡 集計特徴量のビジネス解釈
  • 顧客1:購入回数3回、平均2,267円 → 少額を頻繁に購入する顧客
  • 顧客2:購入回数2回、平均15,600円 → 高額商品を購入する顧客
  • 顧客3:購入回数4回、ばらつき大 → 多様な購入パターンの顧客

このように、集計特徴量を作ることで顧客の購買行動の特徴が見えてきます。

ステップ3:カテゴリ別のクロス集計

# カテゴリ別のクロス集計特徴量 # 顧客ごと × カテゴリごとの購入金額を集計 category_features = df.groupby([‘customer_id’, ‘product_category’])[‘amount’].sum() # unstack(): 行方向のインデックスを列に展開 # fill_value=0: 購入履歴がないカテゴリは0で埋める category_features = category_features.unstack(fill_value=0) # カラム名を整理 category_features.columns = [f’amount_{col}’ for col in category_features.columns] category_features = category_features.reset_index() print(“カテゴリ別の購入金額:”) print(category_features)
カテゴリ別の購入金額: customer_id amount_衣類 amount_食品 amount_電化製品 0 1 5000 1800 0 1 2 0 1200 30000 2 3 14000 500 25000

🕐 6. 日時特徴量の作成

日時特徴量とは:

日時データから年、月、曜日、時間帯などを抽出した特徴量。
時系列パターン(週末に売上が増える、など)を捉えられます。

# 日時特徴量の作成例 # 日時データを作成 date_df = pd.DataFrame({ ‘datetime’: pd.to_datetime([ ‘2024-01-15 10:30:00’, ‘2024-03-22 15:45:00’, ‘2024-07-04 08:00:00’, ‘2024-12-25 20:15:00’, ‘2024-06-30 12:00:00’ ]), ‘sales’: [100, 150, 80, 200, 120] }) print(“元データ:”) print(date_df)
# 日時から様々な特徴量を抽出 # dt アクセサを使って日時の各要素を取得 df_date = date_df.copy() df_date[‘year’] = df_date[‘datetime’].dt.year # 年 df_date[‘month’] = df_date[‘datetime’].dt.month # 月(1-12) df_date[‘day’] = df_date[‘datetime’].dt.day # 日(1-31) df_date[‘dayofweek’] = df_date[‘datetime’].dt.dayofweek # 曜日(0=月曜, 6=日曜) df_date[‘hour’] = df_date[‘datetime’].dt.hour # 時(0-23) df_date[‘is_weekend’] = df_date[‘dayofweek’].isin([5, 6]).astype(int) # 週末フラグ df_date[‘quarter’] = df_date[‘datetime’].dt.quarter # 四半期(1-4) print(“\n日時特徴量を追加:”) print(df_date[[‘datetime’, ‘year’, ‘month’, ‘dayofweek’, ‘hour’, ‘is_weekend’, ‘quarter’]])
元データ: datetime sales 0 2024-01-15 10:30:00 100 1 2024-03-22 15:45:00 150 2 2024-07-04 08:00:00 80 3 2024-12-25 20:15:00 200 4 2024-06-30 12:00:00 120 日時特徴量を追加: datetime year month dayofweek hour is_weekend quarter 0 2024-01-15 10:30:00 2024 1 0 10 0 1 1 2024-03-22 15:45:00 2024 3 4 15 0 1 2 2024-07-04 08:00:00 2024 7 3 8 0 3 3 2024-12-25 20:15:00 2024 12 2 20 0 4 4 2024-06-30 12:00:00 2024 6 6 12 1 2
✅ よく使う特徴量エンジニアリングのパターン
  • 比率:A / B(例:クリック率 = クリック数 / 表示数)
  • 差分:A – B(例:利益 = 売上 – コスト)
  • 対数変換:log(A)(歪んだ分布を正規化)
  • ビニング:連続値をカテゴリに(例:年齢を10代、20代…に)
  • 集計:グループごとの統計量(平均、合計、カウント)

📝 練習問題

問題1 やさしい

欠損値の確認

DataFrameの各列の欠損数を確認するコードは?

# 各列の欠損数を確認 df.isnull().sum() # 欠損率(%)を確認 (df.isnull().mean() * 100).round(1)
問題2 やさしい

SimpleImputerの補完方法

SimpleImputerで中央値を使って補完するには、strategyに何を指定しますか?

正解:’median’
from sklearn.impute import SimpleImputer imputer = SimpleImputer(strategy=’median’) X_imputed = imputer.fit_transform(X)
問題3 ふつう

カテゴリデータの補完

カテゴリデータの欠損値を補完するには、どのstrategyが適していますか?

正解:’most_frequent’(最頻値)

カテゴリデータには平均や中央値は使えないため、最頻値で補完します。

問題4 ふつう

BMIの計算

身長(cm)と体重(kg)からBMIを計算する特徴量を作成してください。

# BMI = 体重(kg) / (身長(m))² # 身長はcmなのでmに変換(100で割る) df[‘bmi’] = df[‘weight’] / (df[‘height’] / 100) ** 2
問題5 むずかしい

KNN Imputerの実装

KNN Imputerで近傍3サンプルを使って欠損値を補完するコードを書いてください。

from sklearn.impute import KNNImputer # KNNImputerを作成(近傍3サンプル) imputer = KNNImputer(n_neighbors=3) # fit_transform()で補完 X_imputed = imputer.fit_transform(X)
問題6 むずかしい

日時特徴量の作成

datetime列から曜日(0-6)を抽出するコードを書いてください。

# dtアクセサを使って曜日を抽出 # 0=月曜日, 6=日曜日 df[‘dayofweek’] = df[‘datetime’].dt.dayofweek # 週末フラグも作成できる df[‘is_weekend’] = df[‘dayofweek’].isin([5, 6]).astype(int)
問題7 むずかしい

集計特徴量の作成

顧客ごとの購入回数と合計金額を計算するコードを書いてください。

# groupbyで顧客ごとに集計 customer_stats = df.groupby(‘customer_id’).agg({ ‘amount’: [‘count’, ‘sum’] }).reset_index() # カラム名をフラット化 customer_stats.columns = [‘customer_id’, ‘purchase_count’, ‘total_amount’] # reset_index()でcustomer_idを列に戻す

📝 STEP 27 のまとめ

✅ このステップで学んだこと
  • 欠損値の確認:isnull().sum()で欠損数を確認
  • SimpleImputer:平均、中央値、最頻値で補完
  • KNN Imputer:類似サンプルの値で補完(より精度が高い)
  • Iterative Imputer:回帰モデルで予測して補完(最も高度)
  • 交互作用特徴量:BMIのように複数の特徴量を組み合わせ
  • 集計特徴量:groupbyでグループごとの統計量を計算
  • 日時特徴量:dtアクセサで年、月、曜日などを抽出
🚀 次のステップへ

次のSTEP 28では、特徴量選択を学びます。重要な特徴量を選び、不要な特徴量を除去する方法を習得しましょう!

❓ よくある質問

Q1. 欠損値は削除と補完、どちらが良い?
欠損率が低い(〜5%):削除でもOK
欠損率が高い:補完が推奨。削除するとデータが大幅に減る
欠損にパターンがある:「欠損かどうか」を新しい特徴量にすることも有効
Q2. KNN ImputerとIterative Imputer、どちらを使うべき?
KNN Imputer:データサイズが中程度で、特徴量間の関係がシンプルな場合
Iterative Imputer:特徴量間に複雑な関係がある場合
まずはSimpleImputerから試し、精度が不十分なら高度な手法を検討しましょう。
Q3. 特徴量は多いほど良い?
必ずしもそうではありません。不要な特徴量が多いと、計算コストが増え、過学習のリスクも高まります。次のSTEPで学ぶ「特徴量選択」が重要です。
📝

学習メモ

機械学習入門 - Step 27

📋 過去のメモ一覧
#artnasekai #学習メモ
LINE