PythonでScikit-learnを使い、欠損値を回帰補完やKNN補完で補完する方法

データ解析において、欠損値はよくある問題です。欠損値を適切に補完することで、データの有用性を高めることができます。本記事では、PythonのScikit-learnライブラリを使って、欠損値を回帰補完やKNN補完で補完する方法を解説します。

こんな人におすすめ

・欠損値を予測値で補完したい
・欠損値を回帰モデルで補完したい
・欠損値をKNNで補完したい
・欠損値をランダムフォレストで補完したい

主なライブラリ

numpy
pandas
sklearn

本ページで使用するライブラリをインポートするときは以下のコードを使用します。

import time
import numpy as np
import pandas as pd
from sklearn.impute import KNNImputer
from sklearn.linear_model import LinearRegression, Ridge, Lasso

欠損値とは?

欠損値とは、データの一部が欠けている状態を指します。
欠損値を適切に処理しないと、データの分布やパターンが歪められてしまうことがあります。

欠損値処理方法の概要

欠損値を処理する方法には、以下のようなものがあります。
(1)欠損値を除外する
(2)欠損値を定数で置換する
(3)欠損値を平均値や中央値で置換する
(4)欠損値を回帰モデルやKNNなどの予測値で置換する
今回は「(4)欠損値を回帰モデルやKNNなどの予測値で置換する」を説明します。

Scikit-learnのKNN補完手法

KNN補完は、K近傍法を用いて欠損値を補完する方法です。KNN補完では、欠損値を補完するために、その特徴量と他の特徴量との距離を計算し、最も近いK個のデータ点の値を用いて補完します。

Scikit-learnのKNNImputerクラスを使って、KNN補完を行うことができます。以下は、KNN補完を行うコードの例です。

# KNN補完を行うためのインスタンスを生成する
imputer = KNNImputer(n_neighbors=5, weights='uniform', metric='nan_euclidean')

# データに欠損値がある場合
X = [[1, 2, np.nan], [3, 4, 5], [np.nan, 6, 7]]
imputer.fit(X)
X_imputed = imputer.transform(X)
print(X_imputed)

このコードでは、KNNImputerクラスを使ってKNN補完を行っています。
n_neighborsはK近傍法のKの値を設定する引数です。
weightsは、距離の重み付け方法を指定する引数で、’uniform’を指定すると等重み、’distance’を指定すると距離の逆数に応じた重み付けが行われます。
metricは、距離の計算方法を指定する引数です。

KNN補完は、Kの値の選び方によって結果が変わってくるため、適切なKの値を選ぶことが重要です。

Scikit-learnの回帰補完手法

Scikit-learnはPythonの機械学習ライブラリで、回帰補完のためのモジュールも提供しています。回帰補完は、欠損値を回帰分析によって予測し、補完する方法です。Scikit-learnの回帰補完手法には、以下の3つがあります。
・Linear Regression
・Ridge Regression
・Lasso Regression

Linear Regressionで補完手法する

線形回帰は、説明変数と目的変数の間の線形関係を仮定する回帰分析の手法です。欠損値を持つ特徴量の値を予測するために、線形回帰を使用することができます。Scikit-learnのLinearRegressionクラスを使用して、線形回帰モデルを作成し、欠損値を補完することができます。

以下のコードは、線形回帰を使用して欠損値を補完する例です。

# データの準備
data = pd.DataFrame({'a':[1,2,np.nan,4,np.nan,6,7,np.nan,9,10], 'b':[1,8,5,9,10,2,7,3,4,6], 'c':[1,2,7,8,9,10,4,3,5,6]})

print(data)
# a b c
# 0 1.0 1 1
# 1 2.0 8 2
# 2 NaN 5 7
# 3 4.0 9 8
# 4 NaN 10 9
# 5 6.0 2 10
# 6 7.0 7 4
# 7 NaN 3 3
# 8 9.0 4 5
# 9 10.0 6 6

# 欠損値を持つ特徴量のリストを取得する
features_with_nan = [feature for feature in data.columns if data[feature].isnull().sum() > 0]

# 線形回帰モデルの作成
linreg = LinearRegression()

# 特徴量ごとに欠損値を補完する
for feature in features_with_nan:
# 欠損値を持つサンプルを除いたデータで線形回帰モデルを学習する
X_train = data.loc[data[feature].notnull()].drop(feature, axis=1)
y_train = data.loc[data[feature].notnull(), feature]
linreg.fit(X_train, y_train)

# 欠損値を予測する
X_test = data.loc[data[feature].isnull()].drop(feature, axis=1)
data.loc[data[feature].isnull(), feature] = linreg.predict(X_test)

print(data)
# a b c
# 0 1.000000 1 1
# 1 2.000000 8 2
# 2 6.452527 5 7
# 3 4.000000 9 8
# 4 7.645601 10 9
# 5 6.000000 2 10
# 6 7.000000 7 4
# 7 4.434447 3 3
# 8 9.000000 4 5
# 9 10.000000 6 6

Ridge Regressionで補完手法する

Ridge回帰は、線形回帰にL2正則化を加えた回帰分析の手法です。L2正則化は、モデルの複雑さに対するペナルティを課すことで、過学習を防ぐことができます。Scikit-learnのRidgeクラスを使用して、Ridge回帰モデルを作成し、欠損値を補完することができます。

以下のコードは、Ridge回帰を使用して欠損値を補完する例です。

# データの準備
data = pd.DataFrame({'a':[1,2,np.nan,4,np.nan,6,7,np.nan,9,10], 'b':[1,8,5,9,10,2,7,3,4,6], 'c':[1,2,7,8,9,10,4,3,5,6]})

print(data)
# a b c
# 0 1.0 1 1
# 1 2.0 8 2
# 2 NaN 5 7
# 3 4.0 9 8
# 4 NaN 10 9
# 5 6.0 2 10
# 6 7.0 7 4
# 7 NaN 3 3
# 8 9.0 4 5
# 9 10.0 6 6

# 欠損値を持つ特徴量のリストを取得する
features_with_nan = [feature for feature in data.columns if data[feature].isnull().sum() > 0]

# 線形回帰モデルの作成
ridge = Ridge()

# 特徴量ごとに欠損値を補完する
for feature in features_with_nan:
# 欠損値を持つサンプルを除いたデータで線形回帰モデルを学習する
X_train = data.loc[data[feature].notnull()].drop(feature, axis=1)
y_train = data.loc[data[feature].notnull(), feature]
ridge.fit(X_train, y_train)

# 欠損値を予測する
X_test = data.loc[data[feature].isnull()].drop(feature, axis=1)
data.loc[data[feature].isnull(), feature] = ridge.predict(X_test)

print(data)
# a b c
# 0 1.000000 1 1
# 1 2.000000 8 2
# 2 6.438233 5 7
# 3 4.000000 9 8
# 4 7.613567 10 9
# 5 6.000000 2 10
# 6 7.000000 7 4
# 7 4.452096 3 3
# 8 9.000000 4 5
# 9 10.000000 6 6

Lasso Regressionで補完手法する

Lasso回帰を使った欠損値補完は、リッジ回帰と同様に線形回帰の一種を用いる方法です。Lasso回帰は、線形回帰の係数を0に近づけることによって、不要な特徴量を除去する手法です。そのため、多重共線性があるデータに対しても効果的な補完ができるとされています。

欠損値を含む特徴量をターゲットに、欠損値を含まない特徴量を説明変数として、Lasso回帰を行うことで欠損値を補完します。

以下のコードは、Lasso回帰を使用して欠損値を補完する例です。

# データの準備
data = pd.DataFrame({'a':[1,2,np.nan,4,np.nan,6,7,np.nan,9,10], 'b':[1,8,5,9,10,2,7,3,4,6], 'c':[1,2,7,8,9,10,4,3,5,6]})

print(data)
# a b c
# 0 1.0 1 1
# 1 2.0 8 2
# 2 NaN 5 7
# 3 4.0 9 8
# 4 NaN 10 9
# 5 6.0 2 10
# 6 7.0 7 4
# 7 NaN 3 3
# 8 9.0 4 5
# 9 10.0 6 6

# 欠損値を持つ特徴量のリストを取得する
features_with_nan = [feature for feature in data.columns if data[feature].isnull().sum() > 0]

# 線形回帰モデルの作成
lasso = Lasso()

# 特徴量ごとに欠損値を補完する
for feature in features_with_nan:
# 欠損値を持つサンプルを除いたデータで線形回帰モデルを学習する
X_train = data.loc[data[feature].notnull()].drop(feature, axis=1)
y_train = data.loc[data[feature].notnull(), feature]
lasso.fit(X_train, y_train)

# 欠損値を予測する
X_test = data.loc[data[feature].isnull()].drop(feature, axis=1)
data.loc[data[feature].isnull(), feature] = lasso.predict(X_test)

print(data)
# a b c
# 0 1.000000 1 1
# 1 2.000000 8 2
# 2 6.255869 5 7
# 3 4.000000 9 8
# 4 6.992958 10 9
# 5 6.000000 2 10
# 6 7.000000 7 4
# 7 4.781690 3 3
# 8 9.000000 4 5
# 9 10.000000 6 6

まとめ

PythonのScikit-learnライブラリを使えば、欠損値を回帰補完やKNN補完で補完することができます。欠損値を補完することで、データの分析やモデル構築の精度を向上させることができます。ただし、欠損値が多い場合や欠損値のパターンが複雑な場合には、補完方法を選ぶだけでなく、欠損値のパターンに応じた特別な処理をする必要があることに注意してください。