OneHotEncoderで未知カテゴリのエラーを止めるにはhandle_unknownの設定を確認します。ただしignoreの全ゼロは「未知の値を学習できた」という意味ではありません。 元の入力と学習済みカテゴリを比べ、どの特徴量で未知値が何件あったかも残しましょう。
対象は、訓練とテストを分けて機械学習を練習しており、文字の区分を数値へ変換する段階に進んだ人です。scikit-learnのfitとtransformを初めて使う場合は、訓練データだけで標準化する手順も併せて確認すると、学習と適用の境界を整理できます。
表を読み込んで列を選ぶ段階から確認したい場合は、PythonでCSV分析を始める学習手順を先に参照してください。今回の配列はファイルを使わず、カテゴリ変換そのものへ問題を絞るために用意しています。
一次資料確認日:2026年10月22日。OneHotEncoderの公式API、カテゴリ特徴量の公式ガイド、前処理で避けたいデータ漏洩、Pipelineを確認しました。実行確認はPython 3.12.14、NumPy 2.5.4、scikit-learn 1.9.1です。六行の訓練データと四行の入力は編集部が作った架空教材で、予測精度や受講成果の実測ではありません。
未知カテゴリを許す設定と、情報が残るかの確認を分ける
fitで見た値と、transformで初めて来た値を区別する
エンコーダーが未知と扱うのは、その特徴量についてfit時に登録されていない値です。日本語として珍しい値か、世の中で一般的な値かではありません。今回の学習データにeastとwestしかなければ、northは普通の方角名でも未知です。
反対に、意味が分からないコードでも、学習時に含まれていれば既知のカテゴリとして処理されます。したがって「辞書にある言葉か」を調べるより、学習済みのcategories_と入力を比べることが出発点になります。
ここでテストデータに対してもう一度fitすると、未知値が登録されてエラーが消える場合があります。しかし、それは同じ変換を適用したことにはなりません。学習側と列の数や意味が変わり、後段のモデルが期待する入力と合わなくなる可能性があります。
エラーがなくなることと、同じ定義で変換できることを分けて考えます。本稿では六行からカテゴリを決め、後から来る四行では登録内容を変えません。新しい値が来た事実を残したうえで、どの対応を選ぶかを考えます。
データを分ける前後の処理が曖昧な場合は、先に訓練と評価の役割を整理してください。全体のカテゴリを先に見てから分割する処理は、評価用に残した情報を前処理の設計へ混ぜることがあります。目的に合わせた分割の境界を維持します。
ignoreの全ゼロは、カテゴリを追加したことではない
handle_unknownをignoreにすると、未知カテゴリが来た特徴量の出力は全ゼロになります。元の値northという情報が、自動でnorth専用の列へ保存されるわけではありません。変換後の列は、学習時に決めた構成を保ちます。
eastとwestの二列なら、eastは一列目が1、westは二列目が1になります。northは両方0です。この状態でも後段へ配列を渡せる場合はありますが、northをeastとwestの平均のように理解した、と説明することはできません。
入力を全ゼロへ写す処理と、その入力に対して適切な予測ができることは別です。予測が適切かは評価設計と実データによる確認が必要になります。この記事は変換の確認に絞り、精度が上がる設定や一般的な許容未知率を示しません。
また、特徴量が複数あると、一つの特徴量が未知でも、行全体は全ゼロにならない場合があります。地域が未知で、受付方法はwebとして既知なら、受付方法の列には1が残ります。行全体を見た判定だけでは、未知の地域を見落とします。
元の入力を手元に残しておけば、変換後には同じ全ゼロになった二種類の未知値も区別して調べられます。共有するログへ原文を無制限に保存する必要はありませんが、調査に必要な集計や仮IDをどう残すかは決めておきましょう。
error・ignore・infrequent_if_existは、止めたい条件から選ぶ
学習教材で例外が出たとき、常にignoreを付けるのが正解とは限りません。未登録コードが来たら担当者へ確認して止めたい処理もあります。入力の変化を見逃したくないのか、変換は続けて後から点検したいのかを先に考えます。
| 設定 | 未知値に対する処理 | 別に確認すること |
|---|---|---|
| error | 未知カテゴリで例外になる | 入力の変更を検出したい場面か、例外後の確認先を決める |
| ignore | 該当特徴量の出力を全ゼロにする | 未知件数を入力から数え、後段で扱ってよいか確認する |
| infrequent_if_exist | 学習時に低頻度枠ができていれば、その枠を使う | 低頻度枠が存在しない場合も確かめる |
この表は推奨順位ではありません。例えば入力仕様が厳密な台帳なら、未知値で止めるほうが、確認前の処理を進めないという方針に合うこともあります。逆に止めずに集計したい場合は、未知を許すことと件数を監視することを組み合わせます。
エラーを無視する設定を入れるときは、コードへ理由も残します。「運用を止めたくない」だけでなく、「未知件数を集計し、この担当者が確認する」といった確認の流れを用意します。設定名だけをコピーして、問題が解決したと見なさないことが大切です。
本稿では、仕様の違いを手計算できるようにignoreを使います。その後、低頻度枠を持つ場合との違いも小さな例で確かめます。業務全般へ同じ設定を勧めるための比較ではありません。
drop=’first’では、既知の基準カテゴリも全ゼロになる
dropをfirstにすると、各特徴量の一つ目のカテゴリに対応する列が省かれます。その結果、その既知カテゴリの表現も全ゼロになります。unknownをignoreにしている場合、未知カテゴリと同じ見た目になる点に注意が必要です。
例えば学習済みカテゴリの順がeast、westなら、eastの列を落とした出力はwestの一列です。eastは0、westは1、未知のnorthも0になります。0という数だけから、eastなのかnorthなのかを復元できません。
そのため「全ゼロの行数を数えれば未知率が分かる」という確認は、dropの条件によって成立しません。後でdrop設定が変わっても使えるように、未知の判定は元の入力と学習済みカテゴリを使って作ります。
dropを使うかどうかは、モデルと特徴量の設計によります。すべてのモデルでfirstを付ける、または付けないと決め付けるのは避けます。公式APIも、列を落とすことの利点だけでなく、表現の対称性が変わる点を説明しています。
今回の教材では、予測モデルを作らず、同じ三つの入力がどんな表現になるかだけを比べます。全ゼロが重なる事実と、モデルへの影響を評価する作業を分けると、確認できた範囲を過大に説明せずに済みます。
欠損や表記ゆれを、知らない地域と同じものにしない
未知カテゴリには、正式に追加された新しい区分だけでなく、空白や大文字小文字の違いが混ざることがあります。例えばeastとEastを同じ扱いにしたいかは、入力仕様の判断です。自動で同じ値になるとは考えず、元の値を確認します。
欠損も別に点検します。空欄、None、文字列の空欄表現が混ざったデータでは、見た目だけで新しいカテゴリと決めにくくなります。今回の例は欠損を含めず、文字列の既知・未知だけへ問題を絞っています。
表記を統一するときは、統一後の値だけでなく、元の値から何件変わったかも残します。変更後に未知が減っても、本来別の区分までまとめてしまっていないかを確認してください。未知件数を小さくすること自体が目的ではありません。
一つの列へ数値と文字列を混ぜるなど、エンコーダーが扱う型の前提に合わない入力もあります。エラー文を見ずにすべてを文字列へ変換するより、データの定義と必要な型を先に決めましょう。数値の大小が意味を持つ列を、区分として変換してよいかも見直します。
区分を別の値へ対応付けるだけの処理は、pandasのmapで欠損を切り分ける教材も参考になります。一方、OneHotEncoderはモデル用の特徴量表現を作る段階なので、出力列と学習済み状態をセットで確認します。
六行で学習し、四行を変換して未知の位置を検算する
一つの特徴量で、既知二件と未知二件を再現する
最初は地域一列だけを使います。訓練はeastとwestが三件ずつの六行、後から来る入力はeast、north、west、northの四行です。north二件が未知ということを、実行する前に書いておきます。
出力の期待値は、eastが1と0、westが0と1、north二件は0と0です。行数は四のまま、出力の列数は二です。未知だからといって行そのものが消えるわけではありません。入力IDとの対応も維持して確認します。
import numpy as np
from sklearn.preprocessing import OneHotEncoder
train = np.array([["east"], ["east"], ["west"],
["east"], ["west"], ["west"]], dtype=object)
incoming = np.array([["east"], ["north"],
["west"], ["north"]], dtype=object)
row_ids = ["N1", "N2", "N3", "N4"]
enc = OneHotEncoder(handle_unknown="ignore", sparse_output=False)
enc.fit(train)
encoded = enc.transform(incoming)
unknown = ~np.isin(incoming[:, 0], enc.categories_[0])
assert enc.categories_[0].tolist() == ["east", "west"]
assert encoded.shape == (4, 2)
np.testing.assert_array_equal(
encoded, [[1, 0], [0, 0], [0, 1], [0, 0]]
)
assert unknown.tolist() == [False, True, False, True]
assert [rid for rid, flag in zip(row_ids, unknown) if flag] == ["N2", "N4"]
assert int(unknown.sum()) == 2
print(enc.get_feature_names_out(["region"]).tolist())
print("未知:", int(unknown.sum()), "/", len(incoming))
この二件を四件で割った値は、今回の架空入力における比率です。実サービスの未知率や、許容できる上限を示す数字ではありません。学習用の小さい分母で、何を数えたかを説明できることが目的です。
実行結果を確認したら、northをwestへ一件変えて、未知が一件へ減るかを試せます。変えた行のIDだけが判定を変えるかも確認してください。行数や列数が合うことに加え、どの行が変わったかを見る練習になります。
dropを追加し、全ゼロだけでは判定できないことを確かめる
同じ訓練と入力で、dropをfirstにしたエンコーダーも作ります。未知二件だけでなく、既知のeast一件も全ゼロになります。この三件を全部未知として報告すると、実際の二件より多く数えてしまいます。
ここで数えたいのは、学習済みのカテゴリにない入力です。出力の見た目が同じかどうかではありません。最初のコードで作ったunknownの判定と、全ゼロの判定を並べてみると、違いを確認できます。
import warnings
drop_enc = OneHotEncoder(
handle_unknown="ignore", drop="first", sparse_output=False
)
drop_enc.fit(train)
with warnings.catch_warnings(record=True) as recorded:
warnings.simplefilter("always")
dropped = drop_enc.transform(incoming)
all_zero = (dropped == 0).all(axis=1)
np.testing.assert_array_equal(dropped, [[0], [0], [1], [0]])
assert int(all_zero.sum()) == 3
assert int(unknown.sum()) == 2
assert bool(all_zero[0]) and not bool(unknown[0])
assert drop_enc.drop_idx_.tolist() == [0]
print("全ゼロ:", int(all_zero.sum()), "本当の未知:", int(unknown.sum()))
警告を捕捉しているのは、この比較教材で出力を読みやすくするためです。業務の処理全体で警告を隠すことを勧めるものではありません。警告文には変換条件を確かめる手掛かりがあるので、実装時は内容も確認してください。
この教材では、既知のN1が0になっても未知へ分類しません。判定の根拠が元の値にあるためです。後から列を一つ落とすか、低頻度カテゴリをまとめるかを変えた場合も、入力側の確認を別に持っておくと、見た目との混同を避けられます。
二つの特徴量では、列ごとの未知と行ごとの未知を分ける
次は地域と受付方法の二列です。地域northが未知の行と、受付方法phoneが未知の行を用意します。一行で両方未知の場合も入れ、列ごとの件数を足すと行数とは一致しないことを確かめます。
四行のうち、地域が未知なのは一行目と四行目、受付方法が未知なのは二行目と四行目です。列別には二件ずつですが、いずれかが未知の行は三件です。同じ四行目を二度数えるので、単純な足し算では四件になってしまいます。
train2 = np.array([
["east", "web"], ["east", "desk"], ["west", "web"],
["east", "web"], ["west", "desk"], ["west", "web"],
], dtype=object)
incoming2 = np.array([
["north", "web"], ["east", "phone"],
["west", "desk"], ["north", "phone"],
], dtype=object)
enc2 = OneHotEncoder(handle_unknown="ignore", sparse_output=False).fit(train2)
z2 = enc2.transform(incoming2)
unknown_by_feature = np.column_stack([
~np.isin(incoming2[:, j], known)
for j, known in enumerate(enc2.categories_)
])
unknown_any = unknown_by_feature.any(axis=1)
assert z2.shape == (4, 4)
assert unknown_by_feature.sum(axis=0).tolist() == [2, 2]
assert unknown_any.tolist() == [True, True, False, True]
assert int(unknown_any.sum()) == 3
assert int((z2 == 0).all(axis=1).sum()) == 1
assert enc2.get_feature_names_out(["region", "channel"]).tolist() == [
"region_east", "region_west", "channel_desk", "channel_web"
]
print("列別:", unknown_by_feature.sum(axis=0).tolist(),
"行別:", int(unknown_any.sum()))
行全体が全ゼロなのは、両方未知の四行目だけです。全ゼロ一件という結果から、未知を含む行が一件しかないと判断すると、地域だけ未知、受付方法だけ未知の二行を見落とします。どの単位を数えたかを明示しましょう。
共有する確認表には、「地域未知二件、受付方法未知二件、いずれか未知三件、対象四件」のように書けます。列別の件数と行別の件数を違う欄へ置くと、足してよい数字かどうかも伝わります。
低頻度枠は、設定しただけで常に作られるわけではない
infrequent_if_existの名前にあるとおり、未知値の割当先となる低頻度枠が存在するかが重要です。設定だけを見て、「その他の列が常に一つ追加される」と考えないでください。fit後のカテゴリ構成と実際の出力を確認します。
次の例はeast四件、west一件、central一件を学習し、二件未満のカテゴリをまとめます。一方、最初のeast三件・west三件では、同じ閾値でもまとめる対象がありません。未知northを入れた結果を二つの条件で比べます。
rare_train = np.array([
["east"], ["east"], ["east"], ["east"], ["west"], ["central"]
], dtype=object)
rare = OneHotEncoder(
handle_unknown="infrequent_if_exist",
min_frequency=2, sparse_output=False
).fit(rare_train)
z_rare = rare.transform([["north"]])
np.testing.assert_array_equal(z_rare, [[0, 1]])
no_rare = OneHotEncoder(
handle_unknown="infrequent_if_exist",
min_frequency=2, sparse_output=False
).fit(train)
z_no_rare = no_rare.transform([["north"]])
np.testing.assert_array_equal(z_no_rare, [[0, 0]])
assert no_rare.infrequent_categories_[0] is None
print("低頻度枠あり:", z_rare.tolist(),
"低頻度枠なし:", z_no_rare.tolist())
この比較から分かるのは、学習データに応じて割当先が違うことです。低頻度へまとめれば予測がよくなる、未知値に十分対応できる、といった結論は出していません。まとめたカテゴリの意味と、後段の結果は別に評価します。
閾値を変えたときも、列数だけでなく、どの元カテゴリが同じ枠へ入ったかを見直します。学習時の分布が変わると、同じ設定でも出力構成が変わることがあります。変換器と列名を学習済み状態として記録する理由になります。
列名・順番・未知件数を残し、後段へ渡す前に点検する
確認用のメモには、訓練側のカテゴリ一覧、dropの設定、出力列名、適用した入力の件数を一組で残します。エンコーダーの設定だけを保存しても、学習したデータが違えば同じ出力になるとは限りません。設定と学習済みの状態を分けることが大切です。
今回の四行にIDを付けたのは、配列の一行目だけを見て判断しないためです。元データが途中で並べ替わる処理へ広げるなら、どのIDがどの出力行へ対応するかも確認します。未知二件という件数が合っていても、違う二件を未知と判定していては目的を満たしません。
検査を一つ追加するなら、既知のwestをもう一件入力し、出力の行数だけが五行へ増え、列名と既存四行の変換結果は変わらないことを予想します。次に未知のsouthを一件足し、未知が三件へ増えるかを確かめます。これも架空教材の条件であり、実データの分布を推定する例ではありません。
複数特徴量で集計する場合は、列ごとの未知率を平均した値と、一つでも未知を含む行の割合を同じ数字として扱わないようにします。先ほどの例なら、列別の未知は各二件、未知を含む行は三件です。指標の名前と分母を併記すると、確認する人が意味を取り違えにくくなります。
未知の元文字列を一覧へ出すときも、すべてをログへ残す必要はありません。例えば新しい地区名だけを件数でまとめるのか、入力元の仮IDから手元の原本を確認するのかを決めます。値の中に自由記入の文章が混ざる場合は、カテゴリの設計そのものを見直すきっかけにもなります。
dropを入れた後に未知判定が変わったように見えたら、判定に使っているデータが出力側へ変わっていないかを調べます。今回のunknownは元のカテゴリとcategories_から作るので、出力列を落としても同じ二件です。この不変条件を持つと、後から設定を変えた際の点検に使えます。
低頻度をまとめる設定を変える場合は、元カテゴリのどれが同じ枠に入るかを先に列挙します。例えばcentralとwestをまとめると、別々の値だったことを出力から読み分けられなくなります。それを目的に合う情報の圧縮とするか、必要な区別の消失とするかは、作ろうとしているモデルで判断します。
新しい入力だけでカテゴリを追加して変換し直す場合は、単なる適用ではなく、学習済みの構成を更新する作業として扱います。後段のモデルと評価も含めて整合を確認する必要があります。「エラーがなくなったから、同じモデルへ渡してよい」という省略は避けてください。
ここで挙げた検査は、未知カテゴリをすべて悪い入力と決めるためではありません。追加された正式区分、入力の誤記、学習データの範囲不足では、次に行うことが違います。まず変換の事実を記録し、その理由と対応を後から確かめるための土台にします。
最後に確認したいのは、四行が四行のままなのか、列名と順番が想定どおりか、どの特徴量に未知があったかです。配列のshapeが合うだけでは、列の意味まで同じと確認したことにはなりません。
訓練用と予測用で別々のエンコーダーを作り直すと、たまたま同じ列数になっても、二列目が違うカテゴリを表す可能性があります。学習済みエンコーダーを一緒に使い、出力名を取得して対応関係を残してください。
複数の前処理とモデルをつなぐときは、公式のPipelineなどで構成をまとめる方法があります。ただし、まとめたから点検が不要になるわけではありません。自分が何をfitしたか、後続でどれを使ったかを説明できる状態を保ちます。
学習の分割でエラーが起きている場合は、stratifyのクラス数と分割後件数の確認から先に確認できます。受講前に必要な前提を整理したい場合は、機械学習スクールのPython・数学の予習へ戻り、つまずいた小さな例を相談用に残しましょう。
よくある質問
Q. handle_unknown='ignore'にすれば未知カテゴリを学習できますか?
未知値を追加学習する指定ではありません。学習時にない値は、該当特徴量の出力が全ゼロになります。元の入力から未知件数を別に確認します。
Q. 全ゼロの行はすべて未知カテゴリですか?
dropで除いた既知の基準カテゴリも全ゼロになり得ます。また複数特徴量では一部だけ未知でも行全体は全ゼロになりません。
Q. テストデータでもfit_transformすれば直りますか?
学習側と別のカテゴリ構成になり得るため、同じ変換を適用したことにはなりません。訓練でfitしたエンコーダーをtransformに使います。
Q. infrequent_if_existならその他の列ができますか?
学習時の頻度条件で低頻度枠ができた場合に使われます。その枠がない場合は未知値が全ゼロになるので、fit後の状態を確認します。
Q. sparse=Falseの例が動かない場合は?
現行APIではsparse_outputを確認します。古い記事の引数名をそのまま使わず、手元のscikit-learnの版と対応する公式資料を照合してください。
Q. 未知率はどの数字を記録すればよいですか?
元の入力に対して、特徴量別の未知件数と、いずれかが未知の行数を分けます。対象件数と分母も記録し、一般的な許容値は別途判断します。
エラーの有無だけで終わらず、どの値が学習済みの範囲外で、どう表現されたかを確認してください。全ゼロを見たときに元の入力、drop、特徴量ごとの境界へ戻れることが、今回の教材で身に付けたい確認の手順です。