01.5.4
一括で特徴ベクトル化
元号 や 町名 のような文字列列は 0/1 のダミー列に、西暦 や 人口総数 のように桁の違う数値列は平均 0・分散 1 に揃える必要があります。make_column_transformer は「この列にはこの変換器」という組を並べて書くだけで、fit_transform を 1 回呼ぶと全列の変換を同時に行い、結果を横に連結して返します。このページの例では、OneHotEncoder が 元号 と 町名 を 29 列に展開し、StandardScaler が 2 列を標準化し、remainder="passthrough" が 和暦 をそのまま通して、5 列の表が 32 列の行列になります。読み終えると、列ごとの前処理を 1 つのオブジェクトにまとめ、学習データとテストデータに同じ変換を適用できるようになります。直感 #
表の列は性質がばらばらで、町名 の A町 を数値に変えるには「A町なら 1、それ以外は 0」の列を町の数だけ作るのが素直で、人口総数 の 394748 と 西暦 の 1920 を同じ尺度に乗せるには平均を引いて標準偏差で割ります。列ごとに別々に変換して np.hstack でつなぐことも可能ですが、テストデータにも同じ手順を同じ順番で繰り返す必要があり、1 列でも抜けると列数がずれてモデルが動きません。make_column_transformer はこの「列 → 変換器」の対応表を 1 つのオブジェクトに固め、fit で全変換器を学習し、transform で同じ順に連結します。
サンプルデータ #
町ごとの人口を年ごとに記録した sample.csv を読み込みます。元号・町名 が文字列、和暦・西暦・人口総数 が数値です。
| |
| 元号 | 和暦 | 西暦 | 人口総数 | 町名 | |
|---|---|---|---|---|---|
| 0 | 大正 | 9.0 | 1920.0 | 394748 | A町 |
| 1 | 大正 | 9.0 | 1920.0 | 31421 | B町 |
| 2 | 大正 | 9.0 | 1920.0 | 226993 | C町 |
| 3 | 大正 | 9.0 | 1920.0 | 253689 | D町 |
| 4 | 大正 | 9.0 | 1920.0 | 288602 | E町 |
make_column_transformer #
sklearn.compose.make_column_transformer を使用します。次のコードは 元号 と 町名 を OneHotEncoder で 0/1 の列に、西暦 と 人口総数 を StandardScaler で標準化し、指定しなかった 和暦 はそのまま通して、1 回の fit_transform で 32 列の行列を作ります。
| |
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | ... | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1.0 | 0.0 | 0.0 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | -1.665466 | 2.046908 | 9.0 |
| 1 | 1.0 | 0.0 | 0.0 | 0.0 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | -1.665466 | -0.557977 | 9.0 |
| 2 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | -1.665466 | 0.844183 | 9.0 |
| 3 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1.0 | 0.0 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | -1.665466 | 1.035581 | 9.0 |
| 4 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1.0 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | -1.665466 | 1.285891 | 9.0 |
5 rows × 32 columns
実行結果の読み方 #
出力の 5 rows × 32 columns は、OneHotEncoder が作った 29 列、StandardScaler の 2 列、passthrough の 1 列を順に連結した結果です。列 0〜2 は元号のダミー(先頭 5 行はすべて大正なので 1.0, 0.0, 0.0)、列 3 以降は町名のダミーで、行ごとに 1.0 の位置が A町 → B町 → … と 1 つずつ右にずれています。列 29 の -1.665466 が標準化した 西暦、列 30 が標準化した 人口総数、最後の列 31 の 9.0 が変換されずに通った 和暦 です。列名を復元したいときは ct.get_feature_names_out() を pd.DataFrame(..., columns=...) に渡すと onehotencoder__元号_大正 のような名前が付きます。
使いどころと落とし穴 #
make_column_transformer を make_pipeline(ct, LogisticRegression()) のようにモデルと束ねると、交差検証の各フォールドで fit が学習側だけに掛かり、テスト側の平均やカテゴリ一覧が漏れ込みません。OneHotEncoder は既定で未知カテゴリに ValueError を出すので、本番データに新しい町名が現れる可能性があるなら handle_unknown="ignore" を付けます。remainder="passthrough" で素通しした 和暦 は標準化されていないので、距離ベースの手法に渡すなら StandardScaler 側の列に加えるか、西暦 と重複する情報として落とすかを決めてから使います。