2013-07-09 17:12:17 +08:00
|
|
|
"""
|
2018-04-16 16:18:29 +08:00
|
|
|
====================================================
|
2013-07-09 17:12:17 +08:00
|
|
|
Imputing missing values before building an estimator
|
2018-04-16 16:18:29 +08:00
|
|
|
====================================================
|
2013-07-09 17:12:17 +08:00
|
|
|
|
2013-07-28 15:03:56 +08:00
|
|
|
Missing values can be replaced by the mean, the median or the most frequent
|
2019-02-15 09:05:36 +08:00
|
|
|
value using the basic :class:`sklearn.impute.SimpleImputer`.
|
2014-11-30 22:31:22 +08:00
|
|
|
The median is a more robust estimator for data with high magnitude variables
|
|
|
|
|
which could dominate results (otherwise known as a 'long tail').
|
2013-07-09 17:12:17 +08:00
|
|
|
|
2019-09-04 07:08:59 +08:00
|
|
|
With ``KNNImputer``, missing values can be imputed using the weighted
|
|
|
|
|
or unweighted mean of the desired number of nearest neighbors.
|
|
|
|
|
|
2019-02-15 09:05:36 +08:00
|
|
|
Another option is the :class:`sklearn.impute.IterativeImputer`. This uses
|
|
|
|
|
round-robin linear regression, treating every variable as an output in
|
|
|
|
|
turn. The version implemented assumes Gaussian (output) variables. If your
|
|
|
|
|
features are obviously non-Normal, consider transforming them to look more
|
|
|
|
|
Normal so as to potentially improve performance.
|
|
|
|
|
|
2018-07-17 03:22:13 +08:00
|
|
|
In addition of using an imputing method, we can also keep an indication of the
|
|
|
|
|
missing information using :func:`sklearn.impute.MissingIndicator` which might
|
|
|
|
|
carry some information.
|
2013-07-09 17:12:17 +08:00
|
|
|
"""
|
2019-02-15 09:05:36 +08:00
|
|
|
print(__doc__)
|
|
|
|
|
|
2013-07-09 17:12:17 +08:00
|
|
|
import numpy as np
|
2018-04-16 16:18:29 +08:00
|
|
|
import matplotlib.pyplot as plt
|
2013-07-09 17:12:17 +08:00
|
|
|
|
2019-05-09 07:28:44 +08:00
|
|
|
# To use the experimental IterativeImputer, we need to explicitly ask for it:
|
|
|
|
|
from sklearn.experimental import enable_iterative_imputer # noqa
|
2018-04-16 16:18:29 +08:00
|
|
|
from sklearn.datasets import load_diabetes
|
2013-07-09 17:12:17 +08:00
|
|
|
from sklearn.datasets import load_boston
|
|
|
|
|
from sklearn.ensemble import RandomForestRegressor
|
2018-07-17 03:22:13 +08:00
|
|
|
from sklearn.pipeline import make_pipeline, make_union
|
2019-09-04 07:08:59 +08:00
|
|
|
from sklearn.impute import (
|
|
|
|
|
SimpleImputer, KNNImputer, IterativeImputer, MissingIndicator)
|
2015-09-11 02:26:39 +08:00
|
|
|
from sklearn.model_selection import cross_val_score
|
2013-07-09 17:12:17 +08:00
|
|
|
|
|
|
|
|
rng = np.random.RandomState(0)
|
|
|
|
|
|
2019-02-15 09:05:36 +08:00
|
|
|
N_SPLITS = 5
|
2019-05-09 21:19:20 +08:00
|
|
|
REGRESSOR = RandomForestRegressor(random_state=0)
|
2019-02-15 09:05:36 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_scores_for_imputer(imputer, X_missing, y_missing):
|
|
|
|
|
estimator = make_pipeline(
|
|
|
|
|
make_union(imputer, MissingIndicator(missing_values=0)),
|
|
|
|
|
REGRESSOR)
|
|
|
|
|
impute_scores = cross_val_score(estimator, X_missing, y_missing,
|
|
|
|
|
scoring='neg_mean_squared_error',
|
|
|
|
|
cv=N_SPLITS)
|
|
|
|
|
return impute_scores
|
|
|
|
|
|
2018-04-16 16:18:29 +08:00
|
|
|
|
|
|
|
|
def get_results(dataset):
|
|
|
|
|
X_full, y_full = dataset.data, dataset.target
|
|
|
|
|
n_samples = X_full.shape[0]
|
|
|
|
|
n_features = X_full.shape[1]
|
|
|
|
|
|
|
|
|
|
# Estimate the score on the entire dataset, with no missing values
|
2019-02-15 09:05:36 +08:00
|
|
|
full_scores = cross_val_score(REGRESSOR, X_full, y_full,
|
|
|
|
|
scoring='neg_mean_squared_error',
|
|
|
|
|
cv=N_SPLITS)
|
2018-04-16 16:18:29 +08:00
|
|
|
|
|
|
|
|
# Add missing values in 75% of the lines
|
|
|
|
|
missing_rate = 0.75
|
|
|
|
|
n_missing_samples = int(np.floor(n_samples * missing_rate))
|
|
|
|
|
missing_samples = np.hstack((np.zeros(n_samples - n_missing_samples,
|
|
|
|
|
dtype=np.bool),
|
|
|
|
|
np.ones(n_missing_samples,
|
|
|
|
|
dtype=np.bool)))
|
|
|
|
|
rng.shuffle(missing_samples)
|
|
|
|
|
missing_features = rng.randint(0, n_features, n_missing_samples)
|
|
|
|
|
X_missing = X_full.copy()
|
|
|
|
|
X_missing[np.where(missing_samples)[0], missing_features] = 0
|
|
|
|
|
y_missing = y_full.copy()
|
2019-02-15 09:05:36 +08:00
|
|
|
|
|
|
|
|
# Estimate the score after replacing missing values by 0
|
|
|
|
|
imputer = SimpleImputer(missing_values=0,
|
|
|
|
|
strategy='constant',
|
|
|
|
|
fill_value=0)
|
|
|
|
|
zero_impute_scores = get_scores_for_imputer(imputer, X_missing, y_missing)
|
2018-04-16 16:18:29 +08:00
|
|
|
|
|
|
|
|
# Estimate the score after imputation (mean strategy) of the missing values
|
2019-02-15 09:05:36 +08:00
|
|
|
imputer = SimpleImputer(missing_values=0, strategy="mean")
|
|
|
|
|
mean_impute_scores = get_scores_for_imputer(imputer, X_missing, y_missing)
|
2018-04-16 16:18:29 +08:00
|
|
|
|
2019-09-04 07:08:59 +08:00
|
|
|
# Estimate the score after kNN-imputation of the missing values
|
|
|
|
|
imputer = KNNImputer(missing_values=0)
|
|
|
|
|
knn_impute_scores = get_scores_for_imputer(imputer, X_missing, y_missing)
|
|
|
|
|
|
2019-02-15 09:05:36 +08:00
|
|
|
# Estimate the score after iterative imputation of the missing values
|
|
|
|
|
imputer = IterativeImputer(missing_values=0,
|
|
|
|
|
random_state=0,
|
2019-07-02 20:32:12 +08:00
|
|
|
n_nearest_features=5,
|
|
|
|
|
sample_posterior=True)
|
2019-02-15 09:05:36 +08:00
|
|
|
iterative_impute_scores = get_scores_for_imputer(imputer,
|
|
|
|
|
X_missing,
|
|
|
|
|
y_missing)
|
2018-04-16 16:18:29 +08:00
|
|
|
|
|
|
|
|
return ((full_scores.mean(), full_scores.std()),
|
|
|
|
|
(zero_impute_scores.mean(), zero_impute_scores.std()),
|
2019-02-15 09:05:36 +08:00
|
|
|
(mean_impute_scores.mean(), mean_impute_scores.std()),
|
2019-09-04 07:08:59 +08:00
|
|
|
(knn_impute_scores.mean(), knn_impute_scores.std()),
|
2019-02-15 09:05:36 +08:00
|
|
|
(iterative_impute_scores.mean(), iterative_impute_scores.std()))
|
2018-04-16 16:18:29 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
results_diabetes = np.array(get_results(load_diabetes()))
|
|
|
|
|
mses_diabetes = results_diabetes[:, 0] * -1
|
|
|
|
|
stds_diabetes = results_diabetes[:, 1]
|
|
|
|
|
|
|
|
|
|
results_boston = np.array(get_results(load_boston()))
|
|
|
|
|
mses_boston = results_boston[:, 0] * -1
|
|
|
|
|
stds_boston = results_boston[:, 1]
|
|
|
|
|
|
|
|
|
|
n_bars = len(mses_diabetes)
|
|
|
|
|
xval = np.arange(n_bars)
|
|
|
|
|
|
|
|
|
|
x_labels = ['Full data',
|
|
|
|
|
'Zero imputation',
|
2019-02-15 09:05:36 +08:00
|
|
|
'Mean Imputation',
|
2019-09-04 07:08:59 +08:00
|
|
|
'KNN Imputation',
|
|
|
|
|
'Iterative Imputation']
|
|
|
|
|
colors = ['r', 'g', 'b', 'orange', 'black']
|
2018-04-16 16:18:29 +08:00
|
|
|
|
|
|
|
|
# plot diabetes results
|
|
|
|
|
plt.figure(figsize=(12, 6))
|
|
|
|
|
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')
|
|
|
|
|
|
2018-06-14 14:41:38 +08:00
|
|
|
ax1.set_title('Imputation Techniques with Diabetes Data')
|
2018-04-16 16:18:29 +08:00
|
|
|
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)
|
|
|
|
|
|
|
|
|
|
# plot boston results
|
|
|
|
|
ax2 = plt.subplot(122)
|
|
|
|
|
for j in xval:
|
|
|
|
|
ax2.barh(j, mses_boston[j], xerr=stds_boston[j],
|
|
|
|
|
color=colors[j], alpha=0.6, align='center')
|
|
|
|
|
|
2018-06-14 14:41:38 +08:00
|
|
|
ax2.set_title('Imputation Techniques with Boston Data')
|
2018-04-16 16:18:29 +08:00
|
|
|
ax2.set_yticks(xval)
|
|
|
|
|
ax2.set_xlabel('MSE')
|
|
|
|
|
ax2.invert_yaxis()
|
|
|
|
|
ax2.set_yticklabels([''] * n_bars)
|
|
|
|
|
|
|
|
|
|
plt.show()
|