概要 #
One-Hot Encoding は、カテゴリカル変数を「0/1」の疎ベクトルに変換する最も基本的な手法です。線形モデル・決定木系モデルいずれでも扱いやすく、カテゴリ間の大小関係を導入せずに表現できる点が強みです。一方で、カテゴリ数が増えると次元爆発が起こりやすく、学習・推論コストに直結します。
いつ採用するか #
- カテゴリ種類が 2〜10 程度で、解釈性を保ちたいとき
- 線形モデル(ロジスティック回帰、GLM)に投入して係数を読み解きたいとき
- 木系アルゴリズムに入れる前に、カテゴリ間の順序関係を入れたくないとき
注意すべきケース #
- カテゴリ数が数百を超える場合は Count / Target Encoding を検討
- 連携する推論基盤(例: FaaS, Web API)のレスポンス要件が厳しいとき
- 多値カテゴリに対して
drop='first'を設定すると、解釈性は保てるが欠損が増えやすい
実装フロー #
1. サンプルデータの読み込み #
import pandas as pd
df = pd.read_csv("../data/sample.csv")
categorical_cols = ["元号", "町名"]
numeric_cols = ["西暦", "人口総数"]
df[categorical_cols + numeric_cols].head()
2. ColumnTransformer で数値とカテゴリを同時処理 #
from sklearn.preprocessing import OneHotEncoder
from sklearn.compose import ColumnTransformer
from sklearn.linear_model import Ridge
from sklearn.pipeline import Pipeline
ohe = OneHotEncoder(handle_unknown="ignore", sparse_output=True)
preprocess = ColumnTransformer(
transformers=[
("cat", ohe, categorical_cols),
("num", "passthrough", numeric_cols),
],
remainder="drop",
)
model = Pipeline(
steps=[
("preprocess", preprocess),
("estimator", Ridge())
]
)
model.fit(df[categorical_cols + numeric_cols], df["人口総数"])
ポイント:
handle_unknown="ignore"を指定しておくと、推論時に未学習カテゴリが来ても例外にならず 0 ベクトルで扱えるsparse_output=Trueで疎行列のまま後段へ渡せば、メモリ節約 & 線形モデルで高速に学習できる
3. 特徴量名の確認 #
feature_names = model.named_steps["preprocess"]\
.named_transformers_["cat"].get_feature_names_out(categorical_cols)
feature_names[:10]
モデルから重要度や係数を読む際に、get_feature_names_out を使って列名を取得しておくと分析レポートが書きやすくなります。
よくある落とし穴と対策 #
| 課題 | 対策 |
|---|---|
| 高次元化で学習時間が肥大化 | High-cardinality な列を別手法(Count Encoding 等)に切り替え、10件未満のカテゴリを Other に集約 |
| ワンホット列が全て 0 になるサンプルが発生 | handle_unknown="ignore" + モデル入力にバイアス項(定数列)を追加しておく |
| カテゴリの追加・削除で推論が壊れる | 学習時に ColumnTransformer を保存し、必ず同じ順序・列構成で推論する。sklearn の Pipeline で一体管理する |
データアナリスト向けチェックリスト #
カテゴリ粒度を確認したか?
例: 町名が 1,000 件ある場合は上位区分にまとめないと次元が増えすぎる。ベースラインの線形モデルで係数を確認したか?
係数の符号が期待と異なる場合は、データのリークや前処理の問題を疑う。推論環境での疎行列サポートを確認したか?
一部のモデルサーバーでは疎行列が扱えないため、toarray()で密行列化するとメモリが数倍になる。