K_DM Book
用語集
対象者 標準
対象者別の入口 →

対象者の設定はこのブラウザの中にだけ保存されます。

言語 JA

一括で特徴ベクトル化

01.5.4

一括で特徴ベクトル化

最終更新 2020-03-11 読了時間 3 分
まとめ
機械学習モデルは数値の行列しか受け取れないので、元号 や 町名 のような文字列列は 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 を読み込みます。元号・町名 が文字列、和暦・西暦・人口総数 が数値です。

1
2
3
4
import pandas as pd

X = pd.read_csv("../data/sample.csv")
X.head()

元号和暦西暦人口総数町名
0大正9.01920.0394748A町
1大正9.01920.031421B町
2大正9.01920.0226993C町
3大正9.01920.0253689D町
4大正9.01920.0288602E町

make_column_transformer #

sklearn.compose.make_column_transformer を使用します。次のコードは 元号 と 町名 を OneHotEncoder で 0/1 の列に、西暦 と 人口総数 を StandardScaler で標準化し、指定しなかった 和暦 はそのまま通して、1 回の fit_transform で 32 列の行列を作ります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
from sklearn.preprocessing import OneHotEncoder
from sklearn.preprocessing import StandardScaler
from sklearn.compose import make_column_transformer

c_oh = OneHotEncoder()
n_ss = StandardScaler()

ct = make_column_transformer(
    (c_oh, ["元号", "町名"]),  # カテゴリ変数にonehot
    (n_ss, ["西暦", "人口総数"]),  # 数値列をスケーリング
    remainder="passthrough",  # 指定の無い列はそのまま
    sparse_threshold=0,
)

X_transform = ct.fit_transform(X)

# 変換後のテーブル
pd.DataFrame(X_transform).head()

0123456789...22232425262728293031
01.00.00.01.00.00.00.00.00.00.0...0.00.00.00.00.00.00.0-1.6654662.0469089.0
11.00.00.00.01.00.00.00.00.00.0...0.00.00.00.00.00.00.0-1.665466-0.5579779.0
21.00.00.00.00.01.00.00.00.00.0...0.00.00.00.00.00.00.0-1.6654660.8441839.0
31.00.00.00.00.00.01.00.00.00.0...0.00.00.00.00.00.00.0-1.6654661.0355819.0
41.00.00.00.00.00.00.01.00.00.0...0.00.00.00.00.00.00.0-1.6654661.2858919.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 側の列に加えるか、西暦 と重複する情報として落とすかを決めてから使います。