機械学習データ前処理入門

機械学習データ前処理の第一歩:欠損値の種類と対処法【Pandas入門】

Tags: データ前処理, 欠損値, Python, Pandas, 機械学習

データ前処理は、機械学習モデルの性能を最大化するために不可欠なステップです。収集された生データは、多くの場合、そのままではモデルに入力できません。その理由の一つとして、「欠損値」の存在が挙げられます。

この記事では、機械学習データ前処理の最初の壁とも言える「欠損値」に焦点を当てます。欠損値とは何か、なぜデータに欠損値が含まれるのか、そしてPythonの代表的なデータ分析ライブラリであるpandasを使って、どのように欠損値を見つけ、適切に処理するのかを、初心者の方にも分かりやすく解説します。

データ分析や機械学習プロジェクトに携わる上で、欠損値処理は避けて通れない重要なスキルです。ぜひこの記事を通じて、欠損値処理の基礎を習得してください。

欠損値とは何か?なぜ発生するのか?

「欠損値」(Missing Value)とは、データセットにおいて、特定の観測値に対応する特定の変数の値が存在しない状態を指します。簡単に言えば、「データがない」「空白になっている」状態です。

データに欠損値が含まれる理由は様々です。一般的な原因としては、以下のようなケースが考えられます。

これらの原因により、データセットには意図せず欠損値が含まれてしまうことがあります。

欠損値が機械学習モデルに与える影響

欠損値がそのままデータセットに含まれていると、機械学習モデルの学習や予測に悪影響を及ぼす可能性があります。

これらの問題を避けるために、データ前処理の段階で欠損値を適切に扱うことが非常に重要になります。

Pandasを使った欠損値の検出

まずは、データセットのどこに、どのくらいの欠損値があるのかを確認することから始めます。Pythonのpandasライブラリを使うと、簡単に欠損値を検出できます。

Pandasでは、欠損値は通常 NaN (Not a Number) という特殊な値で表現されます。

サンプルデータの準備

欠損値を含むサンプルデータを作成してみましょう。

import pandas as pd
import numpy as np

# 欠損値を含むサンプルデータを作成
data = {
    'ID': [1, 2, 3, 4, 5, 6],
    '年齢': [25, 30, np.nan, 40, 35, np.nan],
    '売上': [1000, 1500, 1200, np.nan, 1800, 1300],
    '地域': ['東京', '大阪', '名古屋', '東京', '大阪', '福岡'],
    '評価': [5, 4, 5, 3, 4, np.nan]
}

df = pd.DataFrame(data)

print("--- 元のデータフレーム ---")
print(df)
--- 元のデータフレーム ---
   ID   年齢    売上   地域   評価
0   1  25.0  1000.0   東京  5.0
1   2  30.0  1500.0   大阪  4.0
2   3   NaN  1200.0  名古屋  5.0
3   4  40.0     NaN   東京  3.0
4   5  35.0  1800.0   大阪  4.0
5   6   NaN  1300.0   福岡  NaN

np.nan が欠損値を表しています。「年齢」「売上」「評価」の各列に欠損値が含まれていることが分かります。

欠損値の確認方法

Pandas DataFrameには、欠損値であるかどうかを判定する isnull() または isna() メソッドが用意されています。これらは欠損値がある場合に True、ない場合に False を返す真偽値のDataFrameを生成します。

print("\n--- isnull() の結果 ---")
print(df.isnull())
--- isnull() の結果 ---
      ID   年齢   売上    地域     評価
0  False  False  False  False  False
1  False  False  False  False  False
2  False   True  False  False  False
3  False  False   True  False  False
4  False  False  False  False  False
5  False   True  False  False   True

この真偽値のDataFrameに対して sum() メソッドを使うと、列ごとの欠損値の合計数を簡単に確認できます。True は数値の1として扱われるため、合計値を算出できます。

print("\n--- 列ごとの欠損値数 ---")
print(df.isnull().sum())
--- 列ごとの欠損値数 ---
ID    0
年齢    2
売上    1
地域    0
評価    1
dtype: int64

この結果から、「年齢」列に2個、「売上」列に1個、「評価」列に1個の欠損値があることが明確に分かります。まずこのステップで、どの列にどれだけ欠損があるかを把握することが、欠損値処理の最初の重要な作業となります。

Pandasを使った欠損値の対処法

欠損値が検出されたら、次にどのように処理するかを検討します。欠損値の対処法はいくつかありますが、ここでは代表的な2つの方法を紹介します。

  1. 欠損値を含む行や列を削除する
  2. 欠損値を他の値で補完する(Imputation)

どちらの方法を選択するかは、欠損値の量、データの内容、分析やモデルの目的によって異なります。

1. 欠損値を含む行や列を削除する

欠損値が非常に少ない場合や、特定の列(変数)の欠損値が多すぎる場合は、その行や列を削除するという選択肢があります。

print("\n--- 欠損値を含む行を削除 (any) ---")
df_dropped_rows = df.dropna(how='any')
print(df_dropped_rows)
--- 欠損値を含む行を削除 (any) ---
   ID   年齢    売上  地域   評価
0   1  25.0  1000.0  東京  5.0
1   2  30.0  1500.0  大阪  4.0
4   5  35.0  1800.0  大阪  4.0

欠損値が含まれていたインデックス2, 3, 5の行が削除されました。

print("\n--- 欠損値を含む列を削除 (any) ---")
# サンプルデータ(df)は変更せずにコピーを使用
df_dropped_cols = df.copy().dropna(axis=1, how='any')
print(df_dropped_cols)
--- 欠損値を含む列を削除 (any) ---
   ID    地域
0   1   東京
1   2   大阪
2   3  名古屋
3   4   東京
4   5   大阪
5   6   福岡

「年齢」「売上」「評価」列に欠損値があったため、これらの列が削除されました。

削除はシンプルですが、データ量が減ってしまうというデメリットがあります。特にデータ量が少ない場合は、多くの情報を失うことになるため慎重に判断する必要があります。

2. 欠損値を他の値で補完する (Imputation)

欠損値を削除せずに、他の値で埋める(補完する)方法です。これにより、データ量を維持できます。補完する値としては、その列の平均値、中央値、最頻値などがよく使われます。

Pandasでは fillna() メソッドを使って欠損値を補完できます。

print("\n--- 欠損値を0で補完 (売上列) ---")
# 元のデータフレームをコピーして使用
df_filled_zero = df.copy()
df_filled_zero['売上'] = df_filled_zero['売上'].fillna(0)
print(df_filled_zero)
--- 欠損値を0で補完 (売上列) ---
   ID   年齢    売上   地域   評価
0   1  25.0  1000.0   東京  5.0
1   2  30.0  1500.0   大阪  4.0
2   3   NaN  1200.0  名古屋  5.0
3   4  40.0     0.0   東京  3.0
4   5  35.0  1800.0   大阪  4.0
5   6   NaN  1300.0   福岡  NaN
print("\n--- 欠損値を平均値で補完 (年齢列) ---")
df_filled_mean = df.copy()
mean_age = df_filled_mean['年齢'].mean() # 平均値を計算
df_filled_mean['年齢'] = df_filled_mean['年齢'].fillna(mean_age)
print(df_filled_mean)
--- 欠損値を平均値で補完 (年齢列) ---
   ID       年齢    売上   地域   評価
0   1  25.000000  1000.0   東京  5.0
1   2  30.000000  1500.0   大阪  4.0
2   3  32.500000  1200.0  名古屋  5.0
3   4  40.000000     NaN   東京  3.0
4   5  35.000000  1800.0   大阪  4.0
5   6  32.500000  1300.0   福岡   NaN

平均値 (25 + 30 + 40 + 35) / 4 = 32.5 で欠損値が補完されていることが分かります。

print("\n--- 欠損値を中央値で補完 (売上列を再補完) ---")
df_filled_median = df.copy()
median_sales = df_filled_median['売上'].median() # 中央値を計算
df_filled_median['売上'] = df_filled_median['売上'].fillna(median_sales)
print(df_filled_median)
--- 欠損値を中央値で補完 (売上列を再補完) ---
   ID   年齢     売上   地域   評価
0   1  25.0  1000.00   東京  5.0
1   2  30.0  1500.00   大阪  4.0
2   3   NaN  1200.00  名古屋  5.0
3   4  40.0  1300.00   東京  3.0
4   5  35.0  1800.00   大阪  4.0
5   6   NaN  1300.00   福岡   NaN

売上の中央値 (1000, 1200, 1300, 1500, 1800) は 1300.0 です。欠損値が1300.0で補完されています。

print("\n--- 欠損値を最頻値で補完 (評価列) ---")
df_filled_mode = df.copy()
mode_rating = df_filled_mode['評価'].mode()[0] # 最頻値を計算 (結果はシリーズなので最初の要素 [0] を取得)
df_filled_mode['評価'] = df_filled_mode['評価'].fillna(mode_rating)
print(df_filled_mode)
--- 欠損値を最頻値で補完 (評価列) ---
   ID   年齢    売上   地域   評価
0   1  25.0  1000.0   東京  5.0
1   2  30.0  1500.0   大阪  4.0
2   3   NaN  1200.0  名古屋  5.0
3   4  40.0     NaN   東京  3.0
4   5  35.0  1800.0   大阪  4.0
5   6   NaN  1300.0   福岡  5.0

評価の最頻値は 5.0 と 4.0 ですが、mode() は複数の最頻値がある場合でもリスト(シリーズ)で返します。ここでは最初の最頻値である 5.0 で補完しています。

補完方法を選ぶ際は、その値がデータ分布に与える影響を考慮する必要があります。平均値や中央値での補完はシンプルですが、実際のデータには存在しない値で埋めることになる場合もあります。より高度な補完方法として、機械学習モデルを使って欠損値を予測する方法などもありますが、入門としてはこれらの基本的な方法を理解することが重要です。

なお、fillna() メソッドは元のDataFrameを変更しません。変更を反映させたい場合は、結果を新しい変数に代入するか、inplace=True 引数を使用します(ただし、inplace=True は非推奨になりつつあるため、結果を代入する方が一般的です)。

まとめ

この記事では、機械学習データ前処理における「欠損値」の基本について解説しました。

欠損値処理はデータ分析パイプラインの重要な一部です。ここで紹介した基本的な検出方法と処理方法を理解し、ご自身のデータに適用できるようになることが、機械学習活用に向けた大きな一歩となります。

次に、データ前処理の別の重要なステップである「外れ値」や「カテゴリ変数」の処理についても学んでいきましょう。