📋 このステップで学ぶこと
なぜ特徴量の変換が必要か
数値特徴量のスケーリング(StandardScaler、MinMaxScaler、RobustScaler)
カテゴリカル特徴量のエンコーディング(Label Encoding、One-Hot Encoding)
Target Encoding(ターゲットエンコーディング)
ColumnTransformerで異なる変換をまとめる
演習問題: 7問
🎯 1. なぜ特徴量の変換が必要か
機械学習モデルに渡すデータは、そのままでは使えないことがよくあります。特徴量の変換が必要な理由を理解しましょう。
問題1:スケールの違い
⚠️ スケールが異なるとどうなる?
例えば「年齢(20〜80)」と「年収(200万〜2000万)」を一緒に使う場合、
年収の方が数値が大きいため、モデルが年収を過度に重視してしまいます。
解決策: スケーリングで全ての特徴量を同じ尺度に揃える
問題2:カテゴリカルデータ
⚠️ 文字列データはそのまま使えない
「性別(男/女)」「都道府県(東京/大阪/…)」などの文字列データは、
機械学習モデルに直接渡すことができません。
解決策: エンコーディングで数値に変換する
スケーリングが必要なアルゴリズム
スケーリング
アルゴリズム
理由
必要
ロジスティック回帰、SVM、KNN、ニューラルネット、PCA
距離や勾配を計算するため、スケールの影響を受ける
不要
決定木、ランダムフォレスト、XGBoost
木の分岐はスケールに依存しない
📊 2. 数値特徴量のスケーリング
数値データを適切な範囲に変換するスケーリング手法を3つ学びます。
StandardScaler(標準化)
📊 StandardScalerとは
データを平均0、標準偏差1 に変換します。
計算式:(x - 平均) / 標準偏差
使いどころ: 最も一般的。正規分布に近いデータに適している。
StandardScalerの使い方を順番に見ていきましょう。
fit():データから平均と標準偏差を計算(学習)
transform():計算した統計量を使ってデータを変換
fit_transform():fitとtransformを一度に実行
# StandardScalerの使い方
# ライブラリをインポート
from sklearn.preprocessing import StandardScaler
import numpy as np
# サンプルデータを作成
# 2つの特徴量:年齢(20〜50)と年収(200〜800万)
X = np.array([
[25, 300], # 25歳、年収300万
[30, 400], # 30歳、年収400万
[35, 500], # 35歳、年収500万
[40, 600], # 40歳、年収600万
[50, 800] # 50歳、年収800万
])
print(“変換前のデータ:”)
print(X)
print(f”年齢の範囲: {X[:, 0].min()} 〜 {X[:, 0].max()}”)
print(f”年収の範囲: {X[:, 1].min()} 〜 {X[:, 1].max()}”)
# StandardScalerでスケーリング
# スケーラーを作成
scaler = StandardScaler()
# fit_transform()でデータを変換
# fit(): 平均と標準偏差を計算
# transform(): その統計量を使って変換
X_scaled = scaler.fit_transform(X)
print(“\n変換後のデータ(StandardScaler):”)
print(X_scaled.round(2))
# 変換後の統計を確認
print(f”\n変換後の平均: {X_scaled.mean(axis=0).round(4)}”) # ほぼ0
print(f”変換後の標準偏差: {X_scaled.std(axis=0).round(4)}”) # ほぼ1
変換前のデータ:
[[ 25 300]
[ 30 400]
[ 35 500]
[ 40 600]
[ 50 800]]
年齢の範囲: 25 〜 50
年収の範囲: 300 〜 800
変換後のデータ(StandardScaler):
[[-1.34 -1.34]
[-0.8 -0.8 ]
[-0.27 -0.27]
[ 0.27 0.27]
[ 1.34 1.34]]
変換後の平均: [0. 0.]
変換後の標準偏差: [1. 1.]
MinMaxScaler(正規化)
📊 MinMaxScalerとは
データを0〜1の範囲 に変換します。
計算式:(x - 最小値) / (最大値 - 最小値)
使いどころ: 0〜1の範囲が必要な場合(画像データなど)
# MinMaxScalerの使い方
from sklearn.preprocessing import MinMaxScaler
# スケーラーを作成
minmax_scaler = MinMaxScaler()
# データを変換
X_minmax = minmax_scaler.fit_transform(X)
print(“変換後のデータ(MinMaxScaler):”)
print(X_minmax.round(2))
# 範囲を確認
print(f”\n変換後の最小値: {X_minmax.min(axis=0)}”) # 0
print(f”変換後の最大値: {X_minmax.max(axis=0)}”) # 1
変換後のデータ(MinMaxScaler):
[[0. 0. ]
[0.2 0.2 ]
[0.4 0.4 ]
[0.6 0.6 ]
[1. 1. ]]
変換後の最小値: [0. 0.]
変換後の最大値: [1. 1.]
RobustScaler(外れ値に強い)
📊 RobustScalerとは
中央値と四分位範囲(IQR) を使ってスケーリングします。
計算式:(x - 中央値) / IQR
使いどころ: 外れ値が多いデータ(外れ値の影響を受けにくい)
# RobustScalerの使い方(外れ値がある場合)
from sklearn.preprocessing import RobustScaler
# 外れ値を含むデータを作成
X_with_outlier = np.array([
[25, 300],
[30, 400],
[35, 500],
[40, 600],
[100, 5000] # 外れ値!
])
print(“外れ値を含むデータ:”)
print(X_with_outlier)
# StandardScalerとRobustScalerを比較
standard_scaler = StandardScaler()
robust_scaler = RobustScaler()
X_standard = standard_scaler.fit_transform(X_with_outlier)
X_robust = robust_scaler.fit_transform(X_with_outlier)
print(“\nStandardScaler(外れ値の影響を受ける):”)
print(X_standard.round(2))
print(“\nRobustScaler(外れ値の影響を受けにくい):”)
print(X_robust.round(2))
外れ値を含むデータ:
[[ 25 300]
[ 30 400]
[ 35 500]
[ 40 600]
[ 100 5000]]
StandardScaler(外れ値の影響を受ける):
[[-0.73 -0.64]
[-0.55 -0.58]
[-0.37 -0.52]
[-0.18 -0.46]
[ 1.83 2.2 ]]
RobustScaler(外れ値の影響を受けにくい):
[[-1. -1. ]
[-0.5 -0.5 ]
[ 0. 0. ]
[ 0.5 0.5 ]
[ 6.5 22.5 ]]
✅ スケーラーの選び方まとめ
StandardScaler 一般的な場合、正規分布に近いデータ
MinMaxScaler 0〜1の範囲が必要な場合
RobustScaler 外れ値が多いデータ
🏷️ 3. カテゴリカル特徴量のエンコーディング
文字列(カテゴリカル)データを数値に変換する方法を学びます。
Label Encoding(ラベルエンコーディング)
📊 Label Encodingとは
カテゴリを0, 1, 2, …の整数 に変換します。
使いどころ: 順序があるカテゴリ(小 < 中 < 大)、目的変数の変換
注意: 順序がないカテゴリに使うと、モデルが誤った順序関係を学習する可能性あり
Label Encodingの使い方:
fit():カテゴリの種類を学習
transform():カテゴリを数値に変換
inverse_transform():数値を元のカテゴリに戻す
# Label Encodingの使い方
from sklearn.preprocessing import LabelEncoder
import pandas as pd
# サンプルデータを作成
df = pd.DataFrame({
‘サイズ’: [‘S’, ‘M’, ‘L’, ‘M’, ‘S’, ‘L’],
‘色’: [‘赤’, ‘青’, ‘赤’, ‘緑’, ‘青’, ‘赤’]
})
print(“元のデータ:”)
print(df)
# サイズ列をLabel Encoding
# サイズには順序がある(S < M < L)ので適している
le_size = LabelEncoder()
# fit_transform()でエンコード
df['サイズ_encoded'] = le_size.fit_transform(df['サイズ'])
print("\nLabel Encoding後:")
print(df)
# どのカテゴリがどの数値に対応するか確認
print(f"\nカテゴリと数値の対応: {dict(zip(le_size.classes_, range(len(le_size.classes_))))}")
元のデータ:
サイズ 色
0 S 赤
1 M 青
2 L 赤
3 M 緑
4 S 青
5 L 赤
Label Encoding後:
サイズ 色 サイズ_encoded
0 S 赤 2
1 M 青 1
2 L 赤 0
3 M 緑 1
4 S 青 2
5 L 赤 0
カテゴリと数値の対応: {‘L’: 0, ‘M’: 1, ‘S’: 2}
⚠️ Label Encodingの注意点
上の例では「L=0, M=1, S=2」となり、サイズの大小関係が逆 になっています。
これはアルファベット順でエンコードされるためです。
順序を指定したい場合は、手動でマッピングを定義する方が安全です。
One-Hot Encoding(ワンホットエンコーディング)
📊 One-Hot Encodingとは
各カテゴリを0と1だけの列 に変換します。
該当するカテゴリの列だけが1、それ以外は0になります。
使いどころ: 順序がないカテゴリ(色、国など)
注意: カテゴリ数が多いと列が増えすぎる
# One-Hot Encodingの使い方(pandas.get_dummies)
# 色列をOne-Hot Encoding
# 色には順序がない(赤 > 青 とは言えない)ので、One-Hotが適切
df_onehot = pd.get_dummies(df, columns=[‘色’])
print(“One-Hot Encoding後:”)
print(df_onehot)
One-Hot Encoding後:
サイズ サイズ_encoded 色_緑 色_赤 色_青
0 S 2 0 1 0
1 M 1 0 0 1
2 L 0 0 1 0
3 M 1 1 0 0
4 S 2 0 0 1
5 L 0 0 1 0
One-Hot Encodingの読み方:
行0を見ると「色_赤=1」なので、この行は「赤」です。
行3を見ると「色_緑=1」なので、この行は「緑」です。
各行で1つだけが1になります。
# sklearn.OneHotEncoderを使う方法(機械学習パイプラインで推奨)
from sklearn.preprocessing import OneHotEncoder
# OneHotEncoderを作成
# sparse_output=False: 密な配列を返す(見やすい)
# handle_unknown=’ignore’: 未知のカテゴリを無視
ohe = OneHotEncoder(sparse_output=False, handle_unknown=’ignore’)
# 色のデータを2次元配列に変換して渡す
colors = df[[‘色’]]
# One-Hot Encoding
colors_encoded = ohe.fit_transform(colors)
print(“OneHotEncoder使用後:”)
print(colors_encoded)
print(f”\nカテゴリ: {ohe.categories_[0]}”)
OneHotEncoder使用後:
[[0. 1. 0.]
[0. 0. 1.]
[0. 1. 0.]
[1. 0. 0.]
[0. 0. 1.]
[0. 1. 0.]]
カテゴリ: [‘緑’ ‘赤’ ‘青’]
エンコーディング
変換方法
使いどころ
注意点
Label Encoding
カテゴリ→整数
順序があるカテゴリ、目的変数
順序がないと誤学習の可能性
One-Hot Encoding
カテゴリ→0/1の複数列
順序がないカテゴリ
カテゴリ数が多いと列が増える
🎯 4. Target Encoding(ターゲットエンコーディング)
📊 Target Encodingとは
カテゴリを、そのカテゴリにおける目的変数の平均値 に置き換えます。
例:「東京」カテゴリの平均年収が500万なら、「東京」→500に変換
メリット: カテゴリ数が多くても1列で済む
注意: データリークのリスクがある(交差検証での対策が必要)
基本的なTarget Encoding(問題あり)
# Target Encodingの実装例(基本版 – データリークあり)
import pandas as pd
import numpy as np
# サンプルデータを作成
df = pd.DataFrame({
‘都市’: [‘東京’, ‘大阪’, ‘東京’, ‘名古屋’, ‘大阪’, ‘東京’, ‘名古屋’, ‘大阪’],
‘年収’: [600, 400, 550, 450, 380, 620, 420, 410]
})
print(“元のデータ:”)
print(df)
# 都市ごとの年収の平均を計算
city_mean = df.groupby(‘都市’)[‘年収’].mean()
print(f”\n都市ごとの平均年収:\n{city_mean}”)
# Target Encodingを適用
# map()で都市を平均年収に置き換え
df[‘都市_target_encoded’] = df[‘都市’].map(city_mean)
print(“\nTarget Encoding後:”)
print(df)
元のデータ:
都市 年収
0 東京 600
1 大阪 400
2 東京 550
3 名古屋 450
4 大阪 380
5 東京 620
6 名古屋 420
7 大阪 410
都市ごとの平均年収:
都市
名古屋 435.000000
大阪 396.666667
東京 590.000000
Name: 年収, dtype: float64
Target Encoding後:
都市 年収 都市_target_encoded
0 東京 600 590.000000
1 大阪 400 396.666667
2 東京 550 590.000000
3 名古屋 450 435.000000
4 大阪 380 396.666667
5 東京 620 590.000000
6 名古屋 420 435.000000
7 大阪 410 396.666667
⚠️ データリーク問題
上のコードでは、変換対象のデータ自身 を使って平均を計算しています。
これはデータリーク (本来知りえない情報を使うこと)になります。
例えば、「東京」の平均年収590万を計算する際に、予測したい「東京」のデータ自身も含まれています。これは「カンニング」と同じです。
データリークを防ぐTarget Encoding
交差検証の各フォールドで、訓練データのみから平均を計算します。
# データリークを防ぐTarget Encoding
from sklearn.model_selection import KFold
import pandas as pd
import numpy as np
# サンプルデータ
df = pd.DataFrame({
‘都市’: [‘東京’, ‘大阪’, ‘東京’, ‘名古屋’, ‘大阪’, ‘東京’, ‘名古屋’, ‘大阪’],
‘年収’: [600, 400, 550, 450, 380, 620, 420, 410]
})
# 結果を格納する列を初期化
df[‘都市_target_safe’] = 0.0
# K-Foldで分割
kf = KFold(n_splits=4, shuffle=True, random_state=42)
for train_idx, val_idx in kf.split(df):
# 訓練データのみから平均を計算(データリーク防止)
train_data = df.iloc[train_idx]
mean_map = train_data.groupby(‘都市’)[‘年収’].mean()
# 検証データに適用
df.loc[val_idx, ‘都市_target_safe’] = df.loc[val_idx, ‘都市’].map(mean_map)
# 未知のカテゴリは全体平均で補完
df.loc[val_idx, ‘都市_target_safe’] = df.loc[val_idx, ‘都市_target_safe’].fillna(
train_data[‘年収’].mean()
)
print(“データリーク対策済みTarget Encoding:”)
print(df[[‘都市’, ‘年収’, ‘都市_target_safe’]])
✅ データリーク対策のポイント
各フォールドで、検証データには含まれない訓練データのみ から平均を計算
これにより、予測時に「未知のデータ」として扱われる
未知のカテゴリ(訓練データにない都市)は全体平均で補完
🔧 5. ColumnTransformerで複数の変換をまとめる
実際のデータでは、数値列とカテゴリカル列が混在しています。ColumnTransformerを使うと、列ごとに異なる変換を一度に適用できます。
なぜColumnTransformerを使うのか?
📊 ColumnTransformerのメリット
一括処理: 数値列にはスケーリング、カテゴリ列にはエンコーディングを同時に適用
Pipeline統合: 前処理とモデルを1つのパイプラインにまとめられる
データリーク防止: 交差検証で各フォールドの訓練データのみから統計量を計算
再現性: 本番環境でも同じ前処理を適用できる
ColumnTransformerの構造を理解しよう
ColumnTransformerの主な引数:
transformers:変換のリスト。各要素は(名前, 変換器, 対象列)のタプル
remainder='drop':指定外の列の扱い
'drop':削除(デフォルト)
'passthrough':そのまま残す
# ColumnTransformerの使い方
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LogisticRegression
import pandas as pd
import numpy as np
# 数値列とカテゴリカル列が混在するデータを作成
df = pd.DataFrame({
‘年齢’: [25, 30, 35, 40, 45],
‘年収’: [300, 450, 500, 600, 700],
‘性別’: [‘男’, ‘女’, ‘男’, ‘女’, ‘男’],
‘職種’: [‘営業’, ‘技術’, ‘技術’, ‘営業’, ‘管理’],
‘購入’: [0, 1, 1, 0, 1] # 目的変数
})
print(“混在データ:”)
print(df)
# 列の分類(どの列が数値で、どの列がカテゴリか明示)
numerical_cols = [‘年齢’, ‘年収’] # 数値列
categorical_cols = [‘性別’, ‘職種’] # カテゴリカル列
# ColumnTransformerを作成
# transformers引数に (名前, 変換器, 対象列) のリストを渡す
preprocessor = ColumnTransformer(
transformers=[
# 数値列にはStandardScalerを適用
(‘num’, StandardScaler(), numerical_cols),
# カテゴリ列にはOneHotEncoderを適用
# handle_unknown=’ignore’: 未知のカテゴリを無視(エラーにしない)
(‘cat’, OneHotEncoder(handle_unknown=’ignore’), categorical_cols)
],
remainder=’drop’ # 指定外の列は削除(’passthrough’だと残す)
)
# 特徴量と目的変数に分割
X = df[numerical_cols + categorical_cols]
y = df[‘購入’]
# 変換を実行
X_transformed = preprocessor.fit_transform(X)
print(“\n変換後のデータ:”)
print(X_transformed.round(2))
print(f”\n変換後の形状: {X_transformed.shape}”)
# 数値2列 + 性別2カテゴリ + 職種3カテゴリ = 7列
パイプラインと組み合わせる
ColumnTransformerをPipelineに組み込むと、前処理とモデルを一括管理できます。
# ColumnTransformerをPipelineに組み込む
# 前処理とモデルを1つのパイプラインにまとめる
pipeline = Pipeline([
(‘preprocessor’, preprocessor), # 前処理(ColumnTransformer)
(‘classifier’, LogisticRegression()) # モデル
])
# これで、生データを直接fit()に渡せる!
# パイプライン内部で自動的に前処理が適用される
pipeline.fit(X, y)
# 予測も生データを直接渡せる
predictions = pipeline.predict(X)
print(“予測結果:”, predictions)
print(“正解: “, y.values)
✅ パイプラインのメリット
前処理とモデルを一貫して管理 できる
交差検証でもデータリークを防げる (CVの各フォールドで前処理が独立)
本番環境でも同じ前処理 が適用される
📝 練習問題
問題1
やさしい
スケーリングの目的
特徴量のスケーリングが必要な主な理由は何ですか?
解答を見る
理由: 特徴量間のスケールの違いを揃えるため。スケールが大きい特徴量がモデルに過度に影響することを防ぎます。
問題2
やさしい
スケーラーの選択
外れ値が多いデータにはどのスケーラーが適していますか?
解答を見る
正解:RobustScaler
RobustScalerは中央値と四分位範囲を使うため、外れ値の影響を受けにくいです。
問題3
ふつう
エンコーディングの選択
「血液型(A, B, O, AB)」のエンコーディングには、Label EncodingとOne-Hot Encodingのどちらが適切ですか?理由も説明してください。
解答を見る
正解:One-Hot Encoding
血液型には順序がありません(A > B とは言えない)。Label Encodingを使うと、モデルが「AはBより小さい」という誤った関係を学習する可能性があります。
問題4
ふつう
StandardScalerの実装
データをStandardScalerでスケーリングするコードを書いてください。
解答を見る
from sklearn.preprocessing import StandardScaler
import numpy as np
# サンプルデータ
X = np.array([[100, 0.1], [200, 0.2], [300, 0.3]])
# スケーラーを作成
scaler = StandardScaler()
# fit_transform()で変換
X_scaled = scaler.fit_transform(X)
print(“変換後:”, X_scaled)
問題5
むずかしい
ColumnTransformerの実装
数値列にStandardScaler、カテゴリ列にOneHotEncoderを適用するColumnTransformerを作成してください。
解答を見る
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
# 列の定義
numerical_cols = [‘age’, ‘income’]
categorical_cols = [‘gender’, ‘city’]
# ColumnTransformerを作成
preprocessor = ColumnTransformer(
transformers=[
(‘num’, StandardScaler(), numerical_cols),
(‘cat’, OneHotEncoder(handle_unknown=’ignore’), categorical_cols)
]
)
# 使用例
X_transformed = preprocessor.fit_transform(df)
問題6
むずかしい
Target Encodingの実装
以下のデータで、カテゴリ列「血液型」を目的変数「購入額」の平均でTarget Encodingしてください。
df = pd.DataFrame({
‘血液型’: [‘A’, ‘B’, ‘A’, ‘O’, ‘AB’, ‘A’, ‘B’, ‘O’],
‘購入額’: [1000, 1500, 1200, 800, 2000, 900, 1600, 750]
})
解答を見る
import pandas as pd
df = pd.DataFrame({
‘血液型’: [‘A’, ‘B’, ‘A’, ‘O’, ‘AB’, ‘A’, ‘B’, ‘O’],
‘購入額’: [1000, 1500, 1200, 800, 2000, 900, 1600, 750]
})
# 血液型ごとの購入額の平均を計算
blood_mean = df.groupby(‘血液型’)[‘購入額’].mean()
print(“血液型ごとの平均購入額:”)
print(blood_mean)
# Target Encodingを適用
df[‘血液型_encoded’] = df[‘血液型’].map(blood_mean)
print(“\nTarget Encoding後:”)
print(df)
# 結果:
# 血液型 購入額 血液型_encoded
# A 1000 1033.33 (A型の平均)
# B 1500 1550.00 (B型の平均)
# …
注意: 本番では交差検証でデータリークを防ぐ必要があります。
問題7
むずかしい
データリークを防ぐTarget Encoding
K-Foldを使って、データリークを防いだTarget Encodingを実装してください。
解答を見る
from sklearn.model_selection import KFold
import pandas as pd
import numpy as np
df = pd.DataFrame({
‘血液型’: [‘A’, ‘B’, ‘A’, ‘O’, ‘AB’, ‘A’, ‘B’, ‘O’],
‘購入額’: [1000, 1500, 1200, 800, 2000, 900, 1600, 750]
})
# 結果を格納する列を初期化
df[‘血液型_safe_encoded’] = 0.0
# K-Foldで分割(4分割)
kf = KFold(n_splits=4, shuffle=True, random_state=42)
for train_idx, val_idx in kf.split(df):
# 訓練データのみから平均を計算(データリーク防止)
train_data = df.iloc[train_idx]
mean_map = train_data.groupby(‘血液型’)[‘購入額’].mean()
# 検証データに適用
df.loc[val_idx, ‘血液型_safe_encoded’] = df.loc[val_idx, ‘血液型’].map(mean_map)
# 訓練データにないカテゴリは全体平均で補完
global_mean = train_data[‘購入額’].mean()
df.loc[val_idx, ‘血液型_safe_encoded’] = df.loc[val_idx, ‘血液型_safe_encoded’].fillna(global_mean)
print(“データリーク対策済みTarget Encoding:”)
print(df[[‘血液型’, ‘購入額’, ‘血液型_safe_encoded’]])
ポイント:
各フォールドで、検証データには含まれない訓練データのみから平均を計算
未知のカテゴリは全体平均で補完
これにより、本番環境でも正しく動作する
📝 STEP 26 のまとめ
✅ このステップで学んだこと
スケーリングの必要性: 特徴量のスケールを揃えてモデルの学習を安定化
StandardScaler: 平均0、標準偏差1に変換(最も一般的)
MinMaxScaler: 0〜1の範囲に変換
RobustScaler: 外れ値に強い変換
Label Encoding: 順序があるカテゴリを整数に変換
One-Hot Encoding: 順序がないカテゴリを0/1の列に変換
Target Encoding: 目的変数の平均で変換(データリーク対策が必要)
ColumnTransformer: 異なる変換を列ごとにまとめて適用
❓ よくある質問
Q1. テストデータのスケーリングはどうする?
訓練データでfit()したスケーラーを使って、テストデータはtransform()のみ実行します。テストデータでfit()してはいけません(データリークになる)。
Q2. One-Hotでカテゴリが増えすぎたら?
Target EncodingやFrequency Encoding(出現頻度でエンコード)を検討します。また、稀なカテゴリを「その他」にまとめる方法もあります。
×
artnasekai
#artnasekai #学習メモ