データ分析や機械学習のプロジェクトを進める中で、私たちは必ずと言っていいほど「欠損値(Missing Value)」の問題に直面します。欠損値とは、データが収集されなかったり、入力ミスで失われたりした「空白」のデータのことです。
この記事を読んでいるあなたも、「欠損値があるせいでモデルがエラーになる」「欠損値をどう処理すればいいか分からない」といった悩みを抱えているかもしれません。
ご安心ください。Pythonの機械学習ライブラリである**Scikit-learn(サイキット・ラーン)**には、この問題を解決するための強力なツールが用意されています。それが今回紹介するSimpleImputerです。
この記事では、データ前処理の第一歩として、SimpleImputerの基本的な使い方から、実務で役立つPandas DataFrameでの活用法まで、初心者の方にも分かりやすく徹底解説していきます。
本記事で解説するSimpleImputerの公式ドキュメントはこちらです。
この記事で学べること:
SimpleImputerの基本的な使い方(fitとtransform)- 欠損値を埋める4つの異なる戦略(
strategy)の違いと使い分け - 実務で必須!Pandas DataFrameの欠損値を補完する方法
1. はじめに:データ分析と欠損値処理の重要性
まず、なぜ欠損値処理がそんなに重要なのでしょうか?
欠損値を放置するリスク
もしデータに欠損値(多くの場合、NaNやNoneとして表現されます)が含まれたまま機械学習モデル(例: 線形回帰、ランダムフォレストなど)に学習させようとすると、多くの場合エラーが発生して処理が停止してしまいます。
また、仮にエラーが出ないモデルであっても、欠損値の扱いや補完方法が不適切だと、モデルの予測精度が著しく低下する原因となります。
Scikit-learnのSimpleImputerが解決策
SimpleImputerは、こうした欠損値を「あるルールに基づいて」補完(Imputation)するためのクラスです。
「あるルール」とは、例えば「列の平均値で埋める」「列の中央値で埋める」といった単純明快な戦略(Strategy)のことです。これにより、モデルが学習可能な状態にデータを整えることができます。
SimpleImputerは、Scikit-learnを使ったデータ前処理の基本中の基本であり、欠損値処理の第一歩として非常に強力なツールです。
2. SimpleImputerとは?Scikit-learnの欠損値補完ツール
SimpleImputerは、Scikit-learnライブラリのsklearn.imputeモジュールに含まれているクラスです。
その名の通り、**シンプル(Simple)な方法で欠損値を補完(Impute)**する機能を提供します。
(補足)古いバージョンとの違い
以前のScikit-learn(バージョン 0.20未満)では、sklearn.preprocessing.Imputerという名前で同様の機能が提供されていました。
しかし、現在は機能が整理され、sklearn.impute.SimpleImputerとして提供されています。これから学ぶ方は、SimpleImputerを使うものと覚えておけば問題ありません。
3. SimpleImputerの基本的な使い方(NumPy配列編)
まずは、最も基本的な使い方であるNumPy配列を使った例を見ていきましょう。
必要なライブラリのインポート
最初に、SimpleImputerと、データを作成するためにnumpyをインポートします。
import numpy as np
from sklearn.impute import SimpleImputerサンプルデータの準備
np.nanを使って、欠損値を含むNumPy配列を作成します。
# 欠損値(np.nan)を含むデータを作成
data_np = np.array([[1.0, 2.0, np.nan],
[4.0, np.nan, 6.0],
[7.0, 8.0, 9.0],
[np.nan, 11.0, 12.0]])
print("元のデータ:")
print(data_np)実行結果:
元のデータ:
[[ 1. 2. nan]
[ 4. nan 6.]
[ 7. 8. 9.]
[nan 11. 12.]]SimpleImputerの基本的な流れ
SimpleImputerの使い方は、Scikit-learnの他の変換器(Transformer)と同様、fit(学習)と**transform**(変換)の2ステップで行います。
- インスタンスの作成:
SimpleImputer()で、どのような戦略で補完するかを決めてインスタンスを作成します。 fit()(学習):fit()メソッドにデータを渡します。SimpleImputerは、渡されたデータを分析し、「補完に使う値(例: 各列の平均値)」を計算して内部に記憶します。transform()(変換):transform()メソッドにデータを渡します。fit()で記憶した値を使って、実際の欠損値(np.nan)を補完(置換)します。
コード例(fitとtransform)
デフォルト(何も指定しない場合)の戦略は'mean'(平均値)です。
# 1. インスタンスの作成 (戦略 strategy='mean' はデフォルト)
# strategy='mean' は、各列の「NaNを除いた平均値」で補完することを意味します
imputer = SimpleImputer(strategy='mean')
# 2. fit() で学習(各列の平均値を計算させる)
# 1列目: (1+4+7)/3 = 4.0
# 2列目: (2+8+11)/3 = 7.0
# 3列目: (6+9+12)/3 = 9.0
imputer.fit(data_np)
# 内部に計算された統計値を確認 (statistics_)
print(f"各列の平均値 (補完に使われる値): {imputer.statistics_}")
# 3. transform() で変換(実際に補完する)
data_imputed = imputer.transform(data_np)
print("\n補完後のデータ:")
print(data_imputed)実行結果:
各列の平均値 (補完に使われる値): [4. 7. 9.]
補完後のデータ:
[[ 1. 2. 9.]
[ 4. 7. 6.]
[ 7. 8. 9.]
[ 4. 11. 12.]]1列目のnp.nanは4.0に、2列目のnp.nanは7.0に、3列目のnp.nanは9.0に置き換わっていることが確認できます。
fit_transform()で学習と補完を同時に行う
実務では、fit()とtransform()を別々に行うのではなく、fit_transform()メソッドを使って一度に実行するのが一般的です。
# データをもう一度用意
data_np = np.array([[1.0, 2.0, np.nan],
[4.0, np.nan, 6.0],
[7.0, 8.0, 9.0],
[np.nan, 11.0, 12.0]])
# インスタンス作成
imputer_mean = SimpleImputer(strategy='mean')
# fit()とtransform()を同時に実行
data_imputed_ft = imputer_mean.fit_transform(data_np)
print("fit_transformによる補完後のデータ:")
print(data_imputed_ft)実行結果:
fit_transformによる補完後のデータ:
[[ 1. 2. 9.]
[ 4. 7. 6.]
[ 7. 8. 9.]
[ 4. 11. 12.]]同じ結果が得られました。こちらの書き方の方がコードが短く済むため、よく使われます。
4. 4つの補完戦略(strategy)を理解する
SimpleImputerの最も重要なパラメータがstrategy(戦略)です。これにより、欠損値を「何で埋めるか」を指定できます。
ここでは主要な4つの戦略(mean, median, most_frequent, constant)を見ていきましょう。
strategy=’mean’(平均値)
これは先ほども使用したデフォルトの戦略で、各列の平均値(NaNを除外して計算)で欠損値を補完します。
- 特徴:
- 最も一般的に使われる方法です。
- データの統計的な分布(平均値)を大きく変えにくいです。
- 注意点:
- 外れ値(極端に大きい値や小さい値)に弱いです。例えば、[1, 2, 3, 1000] というデータがあると、平均値は大きく1000側に引っ張られてしまいます。
(コード例はセクション3で示したため、ここでは省略します)
strategy=’median’(中央値)
各列の中央値(NaNを除外して計算)で欠損値を補完します。中央値とは、データを小さい順に並べたときに、ちょうど真ん中に来る値のことです。
- 特徴:
- **外れ値に強い(堅牢である)**という大きなメリットがあります。先ほどの[1, 2, 3, 1000] の例でも、中央値は2.5となり、外れ値1000の影響を受けません。
- データの分布に歪みがある場合(例: 所得データなど)に適しています。
- 使い所:
- データに外れ値が含まれている可能性が高い場合に、
meanの代わりに選択すると良いでしょう。
- データに外れ値が含まれている可能性が高い場合に、
# 中央値で補完する例
imputer_median = SimpleImputer(strategy='median')
data_imputed_median = imputer_median.fit_transform(data_np)
print(f"中央値 (補完に使われる値): {imputer_median.statistics_}")
print("\n中央値で補完後のデータ:")
print(data_imputed_median)実行結果:
中央値 (補完に使われる値): [4. 8. 9.]
中央値で補完後のデータ:
[[ 1. 2. 9.]
[ 4. 8. 6.]
[ 7. 8. 9.]
[ 4. 11. 12.]]2列目の平均値は7.0でしたが、中央値([2.0, 8.0, 11.0] の真ん中)は8.0となったため、np.nanが8.0で補完されています。
strategy=’most_frequent’(最頻値)
各列の最頻値(NaNを除外して計算)で欠損値を補完します。最頻値とは、データの中で最も頻繁に出現する値のことです。
- 特徴:
- 数値データ(
int,float)だけでなく、**カテゴリカルデータ(文字列、object型)**の欠損値補完にも使えます。
- 数値データ(
- 使い所:
- 性別(’男性’, ‘女性’)やアンケートの回答(’A’, ‘B’, ‘C’)など、文字列で表されるデータの欠損値を補完したい場合に非常に便利です。
数値データの例
data_np_freq = np.array([[1.0, 10.0],
[2.0, 20.0],
[1.0, 10.0],
[2.0, np.nan],
[np.nan, 10.0]])
imputer_freq = SimpleImputer(strategy='most_frequent')
data_imputed_freq = imputer_freq.fit_transform(data_np_freq)
print(f"最頻値 (補完に使われる値): {imputer_freq.statistics_}")
print("\n最頻値で補完後のデータ:")
print(data_imputed_freq)実行結果:
最頻値 (補完に使われる値): [ 1. 10.]
最頻値で補完後のデータ:
[[ 1. 10.]
[ 2. 20.]
[ 1. 10.]
[ 2. 10.]
[ 1. 10.]]1列目は1.0が、2列目は10.0が最頻値のため、それで補完されました。
文字列データ(カテゴリカルデータ)の例
カテゴリカルデータの前処理には、SimpleImputerのほかにもOneHotEncoderやLabelEncoderといった手法がよく使われます。これらは欠損値補完とは異なりますが、機械学習モデルがカテゴリデータを扱えるように変換する重要な処理です。(OneHotEncoderに関する詳しい記事はこちらです。LabelEncoderに関する詳しい記事はこちらです。)
# object型(文字列)を含むデータ
data_str = np.array([['A', 'Red'],
['B', 'Blue'],
['A', 'Red'],
[np.nan, 'Green'],
['B', np.nan],
['A', 'Red']], dtype=object) # dtype=object が重要
imputer_str = SimpleImputer(strategy='most_frequent')
data_imputed_str = imputer_str.fit_transform(data_str)
print(f"最頻値 (補完に使われる値): {imputer_str.statistics_}")
print("\n最頻値で補完後のデータ:")
print(data_imputed_str)実行結果:
最頻値 (補完に使われる値): ['A' 'Red']
最頻値で補完後のデータ:
[['A' 'Red']
['B' 'Blue']
['A' 'Red']
['A' 'Green']
['B' 'Red']
['A' 'Red']]1列目のnp.nanは最頻値の'A'に、2列目のnp.nanは最頻値の'Red'に補完されました。
strategy=’constant’(定数値)
strategy='constant'は、指定した**固定値(定数値)で欠損値を補完します。 この戦略を使う場合は、fill_value**パラメータも同時に指定して、何で埋めるかを明示する必要があります。
- 特徴:
0で埋めたい、'Unknown'(不明)という文字列で埋めたいなど、統計値ではなく特定の意味を持つ値で補完したい場合に便利です。
- 使い所:
- 数値データの場合は
fill_value=0やfill_value=-1など。 - カテゴリカルデータの場合は
fill_value='Missing'やfill_value='N/A'などがよく使われます。
- 数値データの場合は
数値データの例(0で埋める)
# constant と fill_value=0 を指定
imputer_const_num = SimpleImputer(strategy='constant', fill_value=0)
data_imputed_const = imputer_const_num.fit_transform(data_np)
print("\n0で補完後のデータ:")
print(data_imputed_const)実行結果:
0で補完後のデータ:
[[ 1. 2. 0.]
[ 4. 0. 6.]
[ 7. 8. 9.]
[ 0. 11. 12.]]文字列データの例(’Unknown’で埋める)
imputer_const_str = SimpleImputer(strategy='constant', fill_value='Unknown')
data_imputed_const_str = imputer_const_str.fit_transform(data_str)
print(f"\n'Unknown'で補完後のデータ:")
print(data_imputed_const_str)実行結果:
'Unknown'で補完後のデータ:
[['A' 'Red']
['B' 'Blue']
['A' 'Red']
['Unknown' 'Green']
['B' 'Unknown']
['A' 'Red']]5. 【実践】Pandas DataFrameでSimpleImputerを使う方法
さて、ここまではNumPy配列で基本を学びましたが、実務のデータ分析ではPandas DataFrameを使うことがほとんどです。
DataFrameでSimpleImputerを使う場合、いくつかの注意点があります。
サンプルDataFrameの準備
pandasをインポートし、数値列(’age’, ‘score’)とカテゴリ列(’gender’)が混在し、それぞれに欠損値を含むDataFrameを作成します。
import pandas as pd
df = pd.DataFrame({
'age': [25, 30, np.nan, 35, 40],
'gender': ['Male', 'Female', 'Male', np.nan, 'Female'],
'score': [88, 95, 76, np.nan, 89]
})
print("元のDataFrame:")
print(df)
print("\nDataFrameの情報:")
df.info()実行結果:
元のDataFrame:
age gender score
0 25.0 Male 88.0
1 30.0 Female 95.0
2 NaN Male 76.0
3 35.0 NaN NaN
4 40.0 Female 89.0
DataFrameの情報:
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 5 entries, 0 to 4
Data columns (total 3 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 age 4 non-null float64
1 gender 4 non-null object
2 score 4 non-null float64
dtypes: float64(2), object(1)
memory usage: 248.0+ bytesageとscore(数値)、gender(文字列)に欠損値(NaN)があることがわかります。
方法1:数値列のみを選択して適用する
最もシンプルでよく使われる方法が、数値列だけを先に補完する方法です。
注意点: SimpleImputer(に限らずScikit-learnの変換器)は、fit_transformの戻り値としてNumPy配列を返します。Pandas DataFrameは返してくれません。
そのため、補完した結果(NumPy配列)を、元のDataFrameの形式に戻す作業が必要になります。
# 1. 数値列(float64, int64)だけを抽出
numeric_cols = df.select_dtypes(include=['float64', 'int64']).columns
print(f"数値列: {numeric_cols}")
# 2. 数値列用のImputerを作成(ここでは中央値 median を使用)
imputer_numeric = SimpleImputer(strategy='median')
# 3. 数値列にのみ fit_transform を適用
# df[numeric_cols] で数値列だけのDataFrameを渡す
# 戻り値はNumPy配列
df_numeric_imputed = imputer_numeric.fit_transform(df[numeric_cols])
# 4. 補完結果を新しいDataFrameに変換(カラム名を戻す)
df_numeric_imputed_pd = pd.DataFrame(df_numeric_imputed, columns=numeric_cols)
print("\n数値列を補完した結果 (DataFrame):")
print(df_numeric_imputed_pd)
# 5. 元のDataFrameの数値列を、補完後のデータで置き換える
# (カテゴリ列 'gender' はそのまま)
df_imputed = df.copy() # 元のdfを変更しないようにコピー
df_imputed[numeric_cols] = df_numeric_imputed_pd
print("\n最終的なDataFrame (数値列のみ補完):")
print(df_imputed)実行結果:
数値列: Index(['age', 'score'], dtype='object')
数値列を補完した結果 (DataFrame):
age score
0 25.0 88.0
1 30.0 95.0
2 32.5 76.0
3 35.0 88.5
4 40.0 89.0
最終的なDataFrame (数値列のみ補完):
age gender score
0 25.0 Male 88.0
1 30.0 Female 95.0
2 32.5 Male 76.0
3 35.0 NaN 88.5
4 40.0 Female 89.0ageのNaNが中央値32.5に、scoreのNaNが中央値88.5に補完されました。genderのNaNはそのままです。
このように、欠損値補完の後は、StandardScalerやMinMaxScalerといったクラスを使って数値のスケール(尺度)を揃える「スケーリング」処理を行うのが一般的です。これらもデータ前処理の重要なステップです。(StandardScalerに関する詳しい記事はこちらです。MinMaxScalerに関する詳しい記事はこちらです。)
方法2:ColumnTransformerと組み合わせて列ごとに戦略を変える
この方法は少し発展的ですが、Scikit-learnのパイプライン機能の核心であり、非常に強力です。
ColumnTransformer(sklearn.composeモジュール)を使うと、「この列にはこの処理」「あの列にはあの処理」というルールを一度に定義できます。
今回は、以下のように設定します。
- 数値列(
'age','score')には:SimpleImputer(strategy='median')を適用 - カテゴリ列(
'gender')には:SimpleImputer(strategy='most_frequent')を適用
from sklearn.compose import ColumnTransformer
# 1. 処理を適用する列リストを定義
numeric_features = ['age', 'score']
categorical_features = ['gender']
# 2. 各列に適用する「処理のパイプライン」を定義
# 今回はImputerだけだが、実際にはこの後にStandardScalerやOneHotEncoderを繋げることが多い
numeric_transformer = SimpleImputer(strategy='median')
categorical_transformer = SimpleImputer(strategy='most_frequent')
# 3. ColumnTransformer で上記ルールをまとめる
# (名前, 変換器, 対象列リスト) のタプルで指定
preprocessor = ColumnTransformer(
transformers=[
('num', numeric_transformer, numeric_features),
('cat', categorical_transformer, categorical_features)
])
# 4. ColumnTransformer を DataFrame に fit_transform する
# これで、数値列とカテゴリ列の欠損値補完が一括で実行される
data_processed = preprocessor.fit_transform(df)
print("\nColumnTransformerによる一括処理後のデータ (NumPy配列):")
print(data_processed)
# 補足: ColumnTransformerもNumPy配列を返すため、DataFrameに戻す作業は必要
# ただし、処理の順番が変わる(定義順 'num' -> 'cat')ことに注意
processed_cols = numeric_features + categorical_features
df_processed = pd.DataFrame(data_processed, columns=processed_cols)
print("\n処理結果をDataFrameに戻したもの:")
print(df_processed)実行結果:
ColumnTransformerによる一括処理後のデータ (NumPy配列):
[[25.0 88.0 'Male']
[30.0 95.0 'Female']
[32.5 76.0 'Male']
[35.0 88.5 'Male']
[40.0 89.0 'Female']]
処理結果をDataFrameに戻したもの:
age score gender
0 25.0 88.0 Male
1 30.0 95.0 Female
2 32.5 76.0 Male
3 35.0 88.5 Male
4 40.0 89.0 Femaleageとscoreが中央値で補完され、genderのNaN(3番目の行)が最頻値である'Male'で補完されていることがわかります。
ColumnTransformerは、SimpleImputerと、前述のStandardScalerやOneHotEncoderを組み合わせて「データ前処理パイプライン」を作る際に必須のテクニックです。
6. まとめ:SimpleImputerを使いこなしてデータ前処理を効率化しよう
今回は、Scikit-learnを使った欠損値処理の第一歩として、SimpleImputerの使い方を徹底的に解説しました。
SimpleImputerは欠損値処理の基本: Scikit-learnのfit->transform(またはfit_transform)の作法で簡単に使えます。- 4つの
strategyが重要:'mean'(平均値): デフォルト。外れ値に弱い。'median'(中央値): 外れ値に強い(堅牢)。'most_frequent'(最頻値): カテゴリカルデータ(文字列)にも使える。'constant'(定数値):fill_valueと組み合わせて固定値で埋める。
- Pandas DataFrameでの利用が実務の鍵:
SimpleImputerはNumPy配列を返すため、DataFrameに戻す作業が必要です。ColumnTransformerを使うと、列の型(数値/カテゴリ)ごとに異なる補完戦略を一括で適用でき、非常に強力です。
データ分析や機械学習において、前処理はモデル構築と同じくらい、あるいはそれ以上に重要です。
まずはSimpleImputerを使って、厄介な欠損値を手なずけるところから始めてみましょう!


コメント