在建立估算器之前估算缺失值

可以使用基本sklearn.impute.SimpleImputer将缺失值替换为平均值,中位数或最频繁的值(众数)。

在此示例中,我们将研究不同的插补技术:

  • 归因于常数0

  • 每个特征的平均值进行归因,并结合缺失指标辅助变量

  • k最近邻归因

  • 迭代插补

我们将使用两个数据集:糖尿病数据集,其中包括10个从糖尿病患者那里收集的,旨在预测疾病进展的特征变量;以及加利福尼亚住房数据集,其目标是加利福尼亚地区的平均房价。

由于这些数据集都没有缺失值,因此我们将删除一些值以使用人为缺失的数据创建新版本。 然后将RandomForestRegressor在完整原始数据集上的性能与使用不同技术估算的人工缺失值与变更后的数据集的性能进行比较。

print(__doc__)

# 作者: Maria Telenczuk  <https://github.com/maikia>
# 执照: BSD 3 clause

下载数据并设置缺失值集

首先,我们下载两个数据集。 scikit-learn附带了糖尿病数据集。 它具有442个条目,每个条目具有10个功能。 加州住房数据集更大,有20640个条目和8个要素。 需要下载。 为了加快计算速度,我们将仅使用前400个条目,但可以随意使用整个数据集。

import numpy as np

from sklearn.datasets import fetch_california_housing
from sklearn.datasets import load_diabetes


rng = np.random.RandomState(42)

X_diabetes, y_diabetes = load_diabetes(return_X_y=True)
X_california, y_california = fetch_california_housing(return_X_y=True)
X_california = X_california[:400]
y_california = y_california[:400]


def add_missing_values(X_full, y_full):
    n_samples, n_features = X_full.shape

    # 在75%的行中添加缺失值
    missing_rate = 0.75
    n_missing_samples = int(n_samples * missing_rate)

    missing_samples = np.zeros(n_samples, dtype=np.bool)
    missing_samples[: n_missing_samples] = True

    rng.shuffle(missing_samples)
    missing_features = rng.randint(0, n_features, n_missing_samples)
    X_missing = X_full.copy()
    X_missing[missing_samples, missing_features] = np.nan
    y_missing = y_full.copy()

    return X_missing, y_missing


X_miss_california, y_miss_california = add_missing_values(
    X_california, y_california)

X_miss_diabetes, y_miss_diabetes = add_missing_values(
    X_diabetes, y_diabetes)

插补缺失值并打分

现在,我们将编写一个函数,该函数将对不同插补数据的结果进行评分。 让我们分别看一下每个不良因素:

rng = np.random.RandomState(0)

from sklearn.ensemble import RandomForestRegressor

# 要使用此实验的IterativeImputer,我们需要明确引入以下包和库:
from sklearn.experimental import enable_iterative_imputer  # noqa
from sklearn.impute import SimpleImputer, KNNImputer, IterativeImputer
from sklearn.model_selection import cross_val_score
from sklearn.pipeline import make_pipeline


N_SPLITS = 5
regressor = RandomForestRegressor(random_state=0)

1、丢失的信息

除了估算缺失值外,注入器还具有一个add_indicator参数,该参数标记缺失的值,其中可能包含一些信息。

def get_scores_for_imputer(imputer, X_missing, y_missing):
    estimator = make_pipeline(imputer, regressor)
    impute_scores = cross_val_score(estimator, X_missing, y_missing,
                                    scoring='neg_mean_squared_error',
                                    cv=N_SPLITS)
    return impute_scores


x_labels = ['Full data',
            'Zero imputation',
            'Mean Imputation',
            'KNN Imputation',
            'Iterative Imputation']

mses_california = np.zeros(5)
stds_california = np.zeros(5)
mses_diabetes = np.zeros(5)
stds_diabetes = np.zeros(5)

2、使用0替换缺失值

现在,我们将在缺失值被0代替的数据上估算分数:

def get_impute_zero_score(X_missing, y_missing):

    imputer = SimpleImputer(missing_values=np.nan, add_indicator=True,
                            strategy='constant', fill_value=0)
    zero_impute_scores = get_scores_for_imputer(imputer, X_missing, y_missing)
    return zero_impute_scores.mean(), zero_impute_scores.std()


mses_california[1], stds_california[1] = get_impute_zero_score(
    X_miss_california, y_miss_california)
mses_diabetes[1], stds_diabetes[1] = get_impute_zero_score(X_miss_diabetes,
                                                           y_miss_diabetes)

3、缺失值的kNN填补

sklearn.impute.KNNImputer使用所需的最近邻居数的加权或未加权平均值来估算缺失值。

def get_impute_knn_score(X_missing, y_missing):
    imputer = KNNImputer(missing_values=np.nan, add_indicator=True)
    knn_impute_scores = get_scores_for_imputer(imputer, X_missing, y_missing)
    return knn_impute_scores.mean(), knn_impute_scores.std()


mses_california[2], stds_california[2] = get_impute_knn_score(
    X_miss_california, y_miss_california)
mses_diabetes[2], stds_diabetes[2] = get_impute_knn_score(X_miss_diabetes,
                                                          y_miss_diabetes)

4、均值填补缺失值

def get_impute_mean(X_missing, y_missing):
    imputer = SimpleImputer(missing_values=np.nan, strategy="mean",
                            add_indicator=True)
    mean_impute_scores = get_scores_for_imputer(imputer, X_missing, y_missing)
    return mean_impute_scores.mean(), mean_impute_scores.std()


mses_california[3], stds_california[3] = get_impute_mean(X_miss_california,
                                                         y_miss_california)
mses_diabetes[3], stds_diabetes[3] = get_impute_mean(X_miss_diabetes,
                                                     y_miss_diabetes)

5、缺失值的迭代估算

另一个选项是sklearn.impute.IterativeImputer。 这使用循环线性回归,依次将缺少值的每个特征建模为其他特征的函数。 所实现的版本假定为高斯(输出)变量。 如果您的功能显然不正常,请考虑将其转换为看起来更正常,以潜在地提高性能。

def get_impute_iterative(X_missing, y_missing):
    imputer = IterativeImputer(missing_values=np.nan, add_indicator=True,
                               random_state=0, n_nearest_features=5,
                               sample_posterior=True)
    iterative_impute_scores = get_scores_for_imputer(imputer,
                                                     X_missing,
                                                     y_missing)
    return iterative_impute_scores.mean(), iterative_impute_scores.std()


mses_california[4], stds_california[4] = get_impute_iterative(
    X_miss_california, y_miss_california)
mses_diabetes[4], stds_diabetes[4] = get_impute_iterative(X_miss_diabetes,
                                                          y_miss_diabetes)

mses_diabetes = mses_diabetes * -1
mses_california = mses_california * -1

绘制结果

最后,我们将可视化分数计算并输出:

import matplotlib.pyplot as plt


n_bars = len(mses_diabetes)
xval = np.arange(n_bars)

colors = ['r''g''b''orange''black']

# 绘制糖尿病数据集的结果
plt.figure(figsize=(126))
ax1 = plt.subplot(121)
for j in xval:
    ax1.barh(j, mses_diabetes[j], xerr=stds_diabetes[j],
             color=colors[j], alpha=0.6, align='center')

ax1.set_title('Imputation Techniques with Diabetes Data')
ax1.set_xlim(left=np.min(mses_diabetes) * 0.9,
             right=np.max(mses_diabetes) * 1.1)
ax1.set_yticks(xval)
ax1.set_xlabel('MSE')
ax1.invert_yaxis()
ax1.set_yticklabels(x_labels)

# 绘制加利福尼亚房屋价值数据集的结果
ax2 = plt.subplot(122)
for j in xval:
    ax2.barh(j, mses_california[j], xerr=stds_california[j],
             color=colors[j], alpha=0.6, align='center')

ax2.set_title('Imputation Techniques with California Data')
ax2.set_yticks(xval)
ax2.set_xlabel('MSE')
ax2.invert_yaxis()
ax2.set_yticklabels([''] * n_bars)

plt.show()

# 你也可以尝试其他技巧。例如,对于具有高强度变量的数据,
# 中位数是一个更可靠的估计器,该变量可能会主导结果(否则会出现长尾现象)。

输出:

脚本的总运行时间:0分钟18.774秒