Python: Scikit-learnのOneHotEncoder入門!使い方と仕組みを徹底解説

Python

Pythonを使って機械学習モデルを構築する際、「カテゴリ変数」の扱いに悩んだことはありませんか?

機械学習モデルの多くは、「性別:男性・女性」や「色:赤・青・緑」といったテキスト形式のデータをそのまま学習することができません。これらのデータをモデルが理解できる「数値」に変換する前処理が必要不可欠です。

この記事では、Pythonの機械学習ライブラリ**Scikit-learn (sklearn)**が提供するOneHotEncoder(ワンホットエンコーダー)に焦点を当てます。

OneHotEncoderの基本的な使い方から、fittransformの役割、さらにはPandasのget_dummiesとの違いまで、初心者から中級者の方にも分かりやすく、具体的なコード例と共に徹底解説します。

この記事を読み終える頃には、OneHotEncoderを正しく理解し、自信を持ってデータの前処理を行えるようになっているはずです。

本記事で解説するOneHotEncoderに関する公式ドキュメントはこちらです。

はじめに:OneHotEncoderとは? なぜ機械学習に必要なのか

OneHotEncoderは、機械学習の前処理で使われる非常に重要なツールです。まずは、なぜこれが必要なのか、その根本的な理由と仕組みから見ていきましょう。

カテゴリ変数と機械学習モデルの課題

「カテゴリ変数」とは、性別、血液型、地名、商品の種類など、分類や種類を表す変数のことです。

機械学習モデル(例えば、ロジスティック回帰やニューラルネットワークなど)の内部では、入力されたデータを使って数学的な計算(足し算や掛け算)が行われます。

そのため、「赤」という文字や「東京」という地名を直接計算することはできません。もし無理やり「赤=1, 青=2, 緑=3」のように数値(ラベルエンコーディング)を割り当ててしまうと、モデルが「赤(1)と青(2)を足すと緑(3)になる」といった、本来存在しない量的関係を誤って学習してしまう可能性があります。

この問題を解決するのが「ワンホットエンコーディング」です。

ワンホットエンコーディング(One-Hot Encoding)の基本的な仕組み

ワンホットエンコーディングは、カテゴリ変数を「0」と「1」のみで構成されるベクトル(数値の配列)に変換する手法です。

例えば、「色」という特徴量に「赤」「青」「緑」の3つのカテゴリがあった場合、以下のように変換されます。

  • 「赤」 → [1, 0, 0]
  • 「青」 → [0, 1, 0]
  • 「緑」 → [0, 0, 1]

このように、該当するカテゴリの要素だけが「1 (Hot)」になり、その他はすべて「0」になります。これにより、カテゴリ間に不要な順序関係や量的関係を持たせることなく、モデルが扱える数値データに変換できます。

これらの変換された変数は「ダミー変数」と呼ばれることもあります。

Scikit-learnのOneHotEncoderを使うメリット

「ワンホットエンコーディングなら、Pandasのget_dummies関数でもできるのでは?」と思う方もいるかもしれません。確かにget_dummiesは手軽ですが、Scikit-learnのOneHotEncoderには、特に機械学習の実運用において大きなメリットがあります。

  1. 学習データとテストデータのズレを防げる OneHotEncoderは、fit(学習)とtransform(変換)というステップを踏みます。訓練データで「どのカテゴリが存在するか」をfitで学習させ、そのルール(辞書)を使って訓練データとテストデータの両方をtransformします。これにより、「訓練データには “緑” がなかったが、テストデータには “緑” があった」といった場合でも、一貫した変換が保証されます(handle_unknownオプションで制御可能)。
  2. 機械学習パイプラインに組み込める Scikit-learnのPipeline機能とシームレスに連携できます。データの前処理からモデルの学習までを一つの流れとして定義できるため、コードがスッキリし、ミスのない運用が可能になります。
  3. スパース行列に対応している カテゴリの種類が数千、数万と非常に多くなると、ワンホットベクトルは巨大な行列になります(ほとんどが0)。OneHotEncoderは、デフォルトでこのデータを効率的に扱う「スパース行列(疎行列)」として出力するため、メモリ消費を大幅に節約できます。

OneHotEncoderの基本的な使い方(fitとtransform)

OneHotEncoderの基本的な流れは「①インスタンス化 → ②fit(学習) → ③transform(変換)」です。実際のコードで見ていきましょう。

必要なライブラリのインポート

まずは必要なライブラリをインポートします。OneHotEncoderと、データ作成のためにpandasを使います。

import pandas as pd
from sklearn.preprocessing import OneHotEncoder

準備:サンプルデータの作成

ここでは、「色(color)」と「サイズ(size)」という2つのカテゴリ変数を持つ簡単なDataFrameを作成します。

# サンプルデータの作成
df = pd.DataFrame({
    'color': ['Red', 'Blue', 'Green', 'Red', 'Blue'],
    'size': ['S', 'M', 'L', 'S', 'M']
})

print("元のデータ:")
print(df)

実行結果:

元のデータ:
   color size
0    Red    S
1   Blue    M
2  Green    L
3    Red    S
4   Blue    M

OneHotEncoderのインスタンス化と学習(fit)

次に、OneHotEncoderのインスタンス(実体)を作成し、作成したデータ(df)を使って「学習(fit)」させます。

fitメソッドは、「入力されたデータに、どのようなカテゴリが存在するか」を記憶する役割を果たします。

# 1. OneHotEncoderのインスタンスを作成
ohe = OneHotEncoder()

# 2. データを学習(fit)させる
# どのカテゴリが存在するかを記憶する
ohe.fit(df)

# 学習したカテゴリを確認
print("学習したカテゴリ:")
print(ohe.categories_)

実行結果:

学習したカテゴリ:
[array(['Blue', 'Green', 'Red'], dtype=object), array(['L', 'M', 'S'], dtype=object)]

categories_属性で確認すると、color列には「Blue, Green, Red」が、size列には「L, M, S」が存在することをエンコーダが学習した(覚えた)ことがわかります。

データを変換(transform)

fitで学習したルールに基づき、データを実際にワンホット表現に「変換(transform)」します。

# 3. データを変換(transform)する
transformed_data = ohe.transform(df)

print("\n変換後のデータ (スパース行列):")
print(transformed_data)

# 中身をわかりやすくするために .toarray() で密な配列に変換
print("\n変換後のデータ (配列):")
print(transformed_data.toarray())

実行結果:

変換後のデータ (スパース行列):
  (0, 2)	1.0
  (0, 5)	1.0
  (1, 0)	1.0
  (1, 4)	1.0
  (2, 1)	1.0
  (2, 3)	1.0
  (3, 2)	1.0
  (3, 5)	1.0
  (4, 0)	1.0
  (4, 4)	1.0

変換後のデータ (配列):
[[0. 0. 1. 0. 0. 1.]  <- Red, S
 [1. 0. 0. 0. 1. 0.]  <- Blue, M
 [0. 1. 0. 1. 0. 0.]  <- Green, L
 [0. 0. 1. 0. 0. 1.]  <- Red, S
 [1. 0. 0. 0. 1. 0.]] <- Blue, M

注目すべき点は、transformの出力がデフォルトで「スパース行列(sparse matrix)」であることです。これは、データが巨大になった際のメモリ効率を考慮した結果です。

transformed_data.toarray()を使うことで、私たちがよく目にする密なNumpy配列(0と1が詰まった配列)に変換して確認できます。

fit_transform:学習と変換を同時に実行する方法

fit(学習)とtransform(変換)を2段階に分けるのは、訓練データとテストデータを区別するために重要です。しかし、訓練データに対しては、「学習」と「変換」を同時に行いたい場合がほとんどです。

fit_transformの利便性

fit_transformメソッドは、その名の通りfittransformを一度に実行します。訓練データに対してこのメソッドを使うことで、コードを簡潔に書くことができます。

注意点: fit_transform訓練データ(学習用データ)にのみ使用してください。 **テストデータ(検証用データ)**には、訓練データでfit済みのエンコーダを使ってtransformのみを適用します。これにより、データのズレを防ぎます。

fit_transformの具体的なコード例

fit_transformを使って、学習と変換を同時に行い、結果を配列(.toarray())で見てみましょう。

# サンプルデータ (再掲)
df = pd.DataFrame({
    'color': ['Red', 'Blue', 'Green', 'Red', 'Blue'],
    'size': ['S', 'M', 'L', 'S', 'M']
})

# インスタンス化
ohe_ft = OneHotEncoder()

# fit と transform を同時に実行
# .toarray() で最初から密な配列として受け取る
transformed_array = ohe_ft.fit_transform(df).toarray()

print("fit_transformによる変換結果 (配列):")
print(transformed_array)

実行結果:

fit_transformによる変換結果 (配列):
[[0. 0. 1. 0. 0. 1.]
 [1. 0. 0. 0. 1. 0.]
 [0. 1. 0. 1. 0. 0.]
 [0. 0. 1. 0. 0. 1.]
 [1. 0. 0. 0. 1. 0.]]

fittransformを別々に行った場合と、まったく同じ結果が得られました。

よくある疑問と実践的なオプション

OneHotEncoderには、実務で役立つ便利なオプションがいくつかあります。よく使われるものをピックアップして解説します。

変換後の列名(特徴量名)を取得するには?(get_feature_names_out)

[0, 0, 1, 0, 0, 1]という配列だけを見ても、どの列がどのカテゴリ(例:「Red」や「S」)に対応しているのか分かりにくいです。

get_feature_names_outメソッド(Scikit-learn 1.0以降)を使うと、変換後の列名を簡単に取得できます。(旧バージョンではget_feature_namesでした)

# fit_transform 実行済みの ohe_ft を使用
feature_names = ohe_ft.get_feature_names_out()

print("変換後の特徴量名:")
print(feature_names)

実行結果:

変換後の特徴量名:
['color_Blue' 'color_Green' 'color_Red' 'size_L' 'size_M' 'size_S']

この結果から、変換後の配列の最初の列がcolor_Blue、6番目の列がsize_Sに対応していることが明確にわかります。

スパース行列(sparse=True)とは? なぜデフォルトなのか

前述の通り、OneHotEncoderはデフォルトでスパース行列(sparse_output=True)を返します。

これは、カテゴリの種類が非常に多い場合(例:国名、単語など)を想定しているためです。もし1万種類のカテゴリがあれば、変換後は1万列のベクトルになりますが、そのうち「1」は1つだけで、残り9999個は「0」です。

この「0」をすべてメモリに保持するのは非常に無駄が多いため、スパース行列は「1」が存在する位置(インデックス)だけを記憶することで、メモリ使用量を劇的に削減します。

もし、カテゴリ数が少なく、常にNumpy配列(密な行列)として扱いたい場合は、インスタンス化の際にオプションを指定します。

# sparse_output=False を指定 (古いバージョンでは sparse=False)
ohe_dense = OneHotEncoder(sparse_output=False)

transformed_dense = ohe_dense.fit_transform(df)

print("sparse_output=False の場合の変換結果:")
print(transformed_dense)

実行結果:

sparse_output=False の場合の変換結果:
[[0. 0. 1. 0. 0. 1.]
 [1. 0. 0. 0. 1. 0.]
 [0. 1. 0. 1. 0. 0.]
 [0. 0. 1. 0. 0. 1.]
 [1. 0. 0. 0. 1. 0.]]

.toarray()を使わなくても、直接Numpy配列が得られました。

テストデータに未知のカテゴリが含まれていたら?(handle_unknown)

OneHotEncoderの真価が発揮されるのが、このオプションです。

fit(学習)の際には存在しなかったカテゴリが、transform(変換)の際(例:テストデータや本番運用時)に登場すると、デフォルトではエラーが発生します。

# 'Red', 'Blue', 'Green' のみ学習したエンコーダ (ohe_ft) を使用
test_data = pd.DataFrame({'color': ['Red', 'Yellow'], 'size': ['S', 'M']})
# 'Yellow' は未知のカテゴリ

try:
    ohe_ft.transform(test_data)
except ValueError as e:
    print(f"エラーが発生: {e}")

実行結果:

エラーが発生: Found unknown categories ['Yellow'] in column 0 during transform

このエラーは、handle_unknown='ignore'オプションで回避できます。

# handle_unknown='ignore' を指定
ohe_ignore = OneHotEncoder(handle_unknown='ignore', sparse_output=False)
ohe_ignore.fit(df) # 'Red', 'Blue', 'Green' を学習

# 未知の 'Yellow' を含むデータを変換
transformed_ignore = ohe_ignore.transform(test_data)

print("handle_unknown='ignore' の場合の変換結果:")
print(transformed_ignore)

実行結果:

handle_unknown='ignore' の場合の変換結果:
[[0. 0. 1. 0. 0. 1.]  <- Red, S
 [0. 0. 0. 0. 1. 0.]] <- Yellow, M

2行目(’Yellow’, ‘M’)に注目してください。colorに対応する部分(先頭3列)がすべて[0, 0, 0]となっています。size_Mの部分だけが1になっています。

このように、未知のカテゴリを「すべて0」として扱うことで、エラーを発生させずにモデルの運用を継続できます。

変換をやめて元のデータに戻すには?(inverse_transform)

OneHotEncoderには、ワンホット表現から元のカテゴリデータに逆変換するinverse_transformメソッドも用意されています。変換結果の確認やデバッグに便利です。

# ohe_ft で変換した transformed_array を使用
# [[0. 0. 1. 0. 0. 1.] ... ]

original_data = ohe_ft.inverse_transform(transformed_array)

print("逆変換した結果:")
print(original_data)

実行結果:

逆変換した結果:
[['Red' 'S']
 ['Blue' 'M']
 ['Green' 'L']
 ['Red' 'S']
 ['Blue' 'M']]

元のデータ(Numpy配列形式)に正しく戻っていることが確認できました。

Pandasのget_dummiesとの違いと使い分け

OneHotEncoderとよく比較されるのが、Pandasのget_dummies関数です。どちらもカテゴリ変数をダミー変数化しますが、利用シーンが異なります。

Pandas get_dummiesの簡単な使い方

get_dummiesは非常に手軽で、DataFrameを渡すだけですぐに変換後のDataFrameが返ってきます。

# サンプルデータ (再掲)
df = pd.DataFrame({
    'color': ['Red', 'Blue', 'Green', 'Red', 'Blue'],
    'size': ['S', 'M', 'L', 'S', 'M']
})

# get_dummies を使用
df_dummies = pd.get_dummies(df)

print("get_dummies の実行結果:")
print(df_dummies)

実行結果:

get_dummies の実行結果:
   color_Blue  color_Green  color_Red  size_L  size_M  size_S
0         0.0          0.0        1.0     0.0     0.0     1.0
1         1.0          0.0        0.0     0.0     1.0     0.0
2         0.0          1.0        0.0     1.0     0.0     0.0
3         0.0          0.0        1.0     0.0     0.0     1.0
4         1.0          0.0        0.0     0.0     1.0     0.0

get_feature_names_outなどを使わなくても、自動的に分かりやすい列名が付与され、非常に直感的です。

OneHotEncoderを選ぶべきシナリオ

OneHotEncoderは、機械学習の「訓練」と「予測」を伴うパイプラインで真価を発揮します。

  1. 訓練データとテストデータを分けて扱う場合 これが最大の理由です。get_dummiesを訓練データとテストデータに別々に適用すると、カテゴリの不一致(訓練データにしかなかった、テストデータにしかなかった)が起きた際、列数や列の順序がズレてしまい、モデルがエラーを起こします。 OneHotEncoderなら、訓練データでfitしたルールをテストデータにtransformで適用するため、このズレが起きません。
  2. Scikit-learnのPipelineに組み込む場合 Scikit-learnのPipeline機能を使って、一連の前処理(OneHotEncoderによるカテゴリ変換、StandardScalerによる数値データの標準化など)とモデル学習を一体化させる場合、OneHotEncoderは必須コンポーネントです。※数値データのスケーリング(前処理)には、ほかにも様々な手法があります。
    • StandardScaler(標準化)に関する詳しい記事はこちらです。
    • MinMaxScaler(正規化)に関する詳しい記事はこちらです。
    • RobustScaler(外れ値に強いスケーリング)に関する詳しい記事はこちらです。
    • Normalizer(サンプル単位の正規化)に関する詳しい記事はこちらです。
  3. メモリ効率が重要な場合 カテゴリ数が膨大な場合、スパース行列(sparse_output=True)を活用できるOneHotEncoderが有利です。

get_dummiesが便利なシナリオ

get_dummiesは、その手軽さからデータ探索(EDA)や、簡単な分析に向いています。

  1. データ探索的分析(EDA) 「とりあえずデータを可視化したい」「カテゴリと他の数値データの相関を見たい」といった、モデル構築の「前段階」においては、get_dummiesの直感的な出力は非常に便利です。
  2. 訓練/テスト分割を厳密にしない場合 データセット全体を一度に変換して、その中からモデルの評価を行うような(比較的簡易的な)モデリングであれば、get_dummiesでも問題ありません。

まとめ:OneHotEncoderを使いこなして機械学習モデルの精度を上げよう

今回は、Scikit-learnのOneHotEncoderについて、その基本的な仕組みから実践的な使い方、get_dummiesとの違いまでを詳しく解説しました。

この記事の重要なポイント:

  • 機械学習モデルはカテゴリ変数(文字)を直接扱えないため、数値(0と1)に変換する必要がある。
  • OneHotEncoderは、fit(学習)とtransform(変換)にステップが分かれている。
  • 訓練データでfitしたエンコーダを、テストデータにはtransformのみ適用するのが鉄則。
  • handle_unknown='ignore'は、未知のカテゴリに直面した際のエラーを防ぐ実用的なオプション。
  • get_feature_names_outで変換後の列名が確認できる。
  • get_dummiesはEDAに便利だが、厳密な機械学習パイプラインにはOneHotEncoderが適している。

OneHotEncoderは、機械学習モデルの性能を左右する「データ前処理」において、中心的な役割を担うツールです。ぜひ本記事のコードを参考に、ご自身のデータセットで試してみてください。

コメント

タイトルとURLをコピーしました