LESSON

カテゴリカル変数のエンコーディング

田中VPoE:「NetShop の顧客データには『会員ランク(ゴールド、シルバー、ブロンズ)』や『利用デバイス(PC、スマホ、タブレット)』のような文字列データがある。機械学習モデルは数値しか扱えないから、これらを数値に変換する必要がある。」

あなた:「単純に番号をつければいいんですか? ゴールド=3、シルバー=2、ブロンズ=1 とか。」

田中VPoE:「それだと『ゴールドはブロンズの3倍』という意味がモデルに伝わってしまう。正しいエンコーディング手法を選ばないと、モデルが誤った解釈をしてしまうんだ。」

カテゴリカル変数の種類

種類特徴
名義変数(Nominal)順序なしデバイス種別、都道府県、支払方法
順序変数(Ordinal)順序あり会員ランク、満足度評価、教育レベル

One-hot Encoding

名義変数に最も一般的な手法です。各カテゴリを独立した列(0/1)に変換します。

import pandas as pd

df = pd.DataFrame({
    'device': ['PC', 'スマホ', 'タブレット', 'PC', 'スマホ'],
    'payment': ['クレジット', '銀行振込', 'コンビニ', 'クレジット', 'クレジット'],
})

# pandas の get_dummies
df_encoded = pd.get_dummies(df, columns=['device', 'payment'], drop_first=True)
print(df_encoded)

# scikit-learn の OneHotEncoder
from sklearn.preprocessing import OneHotEncoder

encoder = OneHotEncoder(sparse_output=False, drop='first')
encoded = encoder.fit_transform(df[['device', 'payment']])
print(f"\n変換後の列名: {encoder.get_feature_names_out()}")

注意点

  • drop_first=True で多重共線性を回避(ダミー変数トラップ防止)
  • カテゴリ数が多いと列数が爆発する(高カーディナリティ問題)

Label Encoding

順序変数に適した手法です。カテゴリに整数を割り当てます。

from sklearn.preprocessing import LabelEncoder, OrdinalEncoder

# LabelEncoder(目的変数用)
le = LabelEncoder()
df['membership_encoded'] = le.fit_transform(df['membership_rank'])

# OrdinalEncoder(順序付き特徴量用)
rank_order = [['ブロンズ', 'シルバー', 'ゴールド', 'プラチナ']]
oe = OrdinalEncoder(categories=rank_order)
df['rank_encoded'] = oe.fit_transform(df[['membership_rank']])

print(df[['membership_rank', 'rank_encoded']])

注意点

  • 名義変数に Label Encoding を使うと、存在しない順序関係をモデルが学習してしまう
  • 決定木系のモデルでは Label Encoding でも問題ない場合がある

Target Encoding

カテゴリごとの目的変数の平均値でエンコードする手法です。

# Target Encoding の実装
def target_encode(train_df, val_df, col, target, smoothing=10):
    """ターゲットエンコーディング(スムージング付き)"""
    global_mean = train_df[target].mean()

    agg = train_df.groupby(col)[target].agg(['mean', 'count'])

    # スムージング: データが少ないカテゴリは全体平均に近づける
    smooth = (agg['count'] * agg['mean'] + smoothing * global_mean) / (agg['count'] + smoothing)

    train_encoded = train_df[col].map(smooth)
    val_encoded = val_df[col].map(smooth).fillna(global_mean)

    return train_encoded, val_encoded

# 使用例
# 都道府県ごとの離反率でエンコード
train_encoded, val_encoded = target_encode(
    train_df, val_df, 'prefecture', 'is_churned'
)

注意点

  • データリーケージ: 検証/テストデータの情報が漏れないよう、学習データのみで計算する
  • スムージング: データ数が少ないカテゴリの過学習を防ぐ
  • 交差検証と組み合わせて使うのがベストプラクティス

高カーディナリティ対策

カテゴリ数が多い(例: 商品ID が数千種類)場合の対策です。

# 方法1: 頻度の低いカテゴリをまとめる
threshold = 50
freq = df['product_category'].value_counts()
rare_categories = freq[freq < threshold].index
df['product_category_grouped'] = df['product_category'].replace(
    rare_categories, 'その他'
)

# 方法2: 頻度エンコーディング
freq_map = df['product_category'].value_counts(normalize=True)
df['category_freq'] = df['product_category'].map(freq_map)

# 方法3: Target Encoding(上述)
# カテゴリ数に関わらず1列にエンコードできる

エンコーディング手法の選択ガイド

条件推奨手法
名義変数、カテゴリ数が少ない(10未満)One-hot Encoding
順序変数Ordinal Encoding
カテゴリ数が多いTarget Encoding or 頻度 Encoding
決定木系モデルLabel Encoding でも可
線形モデルOne-hot Encoding

まとめ

  • One-hot Encoding: 名義変数の標準手法、カテゴリ数が少ない場合に有効
  • Label Encoding: 順序変数に適用、名義変数への使用は注意
  • Target Encoding: 高カーディナリティに有効、データリーケージに注意
  • モデルの種類と変数の性質に応じてエンコーディング手法を選択する

チェックリスト

  • 名義変数と順序変数の違いを理解した
  • One-hot Encoding のコードを書ける
  • Target Encoding のデータリーケージリスクを理解した
  • 高カーディナリティへの対処法を把握した

次のステップへ

次のレッスンでは、数値特徴量のスケーリングと正規化を学びます。特徴量のスケールがモデルに与える影響を理解しましょう。


推定読了時間: 30分