2019-02-15 09:05:36 +08:00
|
|
|
"""
|
|
|
|
|
=========================================================
|
|
|
|
|
Imputing missing values with variants of IterativeImputer
|
|
|
|
|
=========================================================
|
|
|
|
|
|
2020-06-20 23:43:14 +08:00
|
|
|
.. currentmodule:: sklearn
|
|
|
|
|
|
|
|
|
|
The :class:`~impute.IterativeImputer` class is very flexible - it can be
|
2019-02-15 09:05:36 +08:00
|
|
|
used with a variety of estimators to do round-robin regression, treating every
|
|
|
|
|
variable as an output in turn.
|
|
|
|
|
|
|
|
|
|
In this example we compare some estimators for the purpose of missing feature
|
2020-06-20 23:43:14 +08:00
|
|
|
imputation with :class:`~impute.IterativeImputer`:
|
2019-02-15 09:05:36 +08:00
|
|
|
|
2020-06-20 23:43:14 +08:00
|
|
|
* :class:`~linear_model.BayesianRidge`: regularized linear regression
|
|
|
|
|
* :class:`~tree.DecisionTreeRegressor`: non-linear regression
|
|
|
|
|
* :class:`~ensemble.ExtraTreesRegressor`: similar to missForest in R
|
|
|
|
|
* :class:`~neighbors.KNeighborsRegressor`: comparable to other KNN
|
2019-05-02 16:49:23 +08:00
|
|
|
imputation approaches
|
2019-02-15 09:05:36 +08:00
|
|
|
|
|
|
|
|
Of particular interest is the ability of
|
2020-06-20 23:43:14 +08:00
|
|
|
:class:`~impute.IterativeImputer` to mimic the behavior of missForest, a
|
2019-02-15 09:05:36 +08:00
|
|
|
popular imputation package for R. In this example, we have chosen to use
|
2020-06-20 23:43:14 +08:00
|
|
|
:class:`~ensemble.ExtraTreesRegressor` instead of
|
|
|
|
|
:class:`~ensemble.RandomForestRegressor` (as in missForest) due to its
|
2019-02-15 09:05:36 +08:00
|
|
|
increased speed.
|
|
|
|
|
|
2020-06-20 23:43:14 +08:00
|
|
|
Note that :class:`~neighbors.KNeighborsRegressor` is different from KNN
|
2019-02-15 09:05:36 +08:00
|
|
|
imputation, which learns from samples with missing values by using a distance
|
|
|
|
|
metric that accounts for missing values, rather than imputing them.
|
|
|
|
|
|
|
|
|
|
The goal is to compare different estimators to see which one is best for the
|
2020-06-20 23:43:14 +08:00
|
|
|
:class:`~impute.IterativeImputer` when using a
|
|
|
|
|
:class:`~linear_model.BayesianRidge` estimator on the California housing
|
2019-02-15 09:05:36 +08:00
|
|
|
dataset with a single value randomly removed from each row.
|
|
|
|
|
|
|
|
|
|
For this particular pattern of missing values we see that
|
2020-06-20 23:43:14 +08:00
|
|
|
:class:`~ensemble.ExtraTreesRegressor` and
|
|
|
|
|
:class:`~linear_model.BayesianRidge` give the best results.
|
2019-02-15 09:05:36 +08:00
|
|
|
"""
|
|
|
|
|
print(__doc__)
|
|
|
|
|
|
|
|
|
|
import numpy as np
|
|
|
|
|
import matplotlib.pyplot as plt
|
|
|
|
|
import pandas as pd
|
|
|
|
|
|
2019-05-09 07:28:44 +08:00
|
|
|
# To use this experimental feature, we need to explicitly ask for it:
|
|
|
|
|
from sklearn.experimental import enable_iterative_imputer # noqa
|
2019-02-15 09:05:36 +08:00
|
|
|
from sklearn.datasets import fetch_california_housing
|
|
|
|
|
from sklearn.impute import SimpleImputer
|
|
|
|
|
from sklearn.impute import IterativeImputer
|
|
|
|
|
from sklearn.linear_model import BayesianRidge
|
|
|
|
|
from sklearn.tree import DecisionTreeRegressor
|
|
|
|
|
from sklearn.ensemble import ExtraTreesRegressor
|
|
|
|
|
from sklearn.neighbors import KNeighborsRegressor
|
|
|
|
|
from sklearn.pipeline import make_pipeline
|
|
|
|
|
from sklearn.model_selection import cross_val_score
|
|
|
|
|
|
|
|
|
|
N_SPLITS = 5
|
|
|
|
|
|
|
|
|
|
rng = np.random.RandomState(0)
|
|
|
|
|
|
|
|
|
|
X_full, y_full = fetch_california_housing(return_X_y=True)
|
2019-03-05 00:30:21 +08:00
|
|
|
# ~2k samples is enough for the purpose of the example.
|
|
|
|
|
# Remove the following two lines for a slower run with different error bars.
|
|
|
|
|
X_full = X_full[::10]
|
|
|
|
|
y_full = y_full[::10]
|
2019-02-15 09:05:36 +08:00
|
|
|
n_samples, n_features = X_full.shape
|
|
|
|
|
|
|
|
|
|
# Estimate the score on the entire dataset, with no missing values
|
|
|
|
|
br_estimator = BayesianRidge()
|
|
|
|
|
score_full_data = pd.DataFrame(
|
|
|
|
|
cross_val_score(
|
|
|
|
|
br_estimator, X_full, y_full, scoring='neg_mean_squared_error',
|
|
|
|
|
cv=N_SPLITS
|
|
|
|
|
),
|
|
|
|
|
columns=['Full Data']
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# Add a single missing value to each row
|
|
|
|
|
X_missing = X_full.copy()
|
|
|
|
|
y_missing = y_full
|
|
|
|
|
missing_samples = np.arange(n_samples)
|
|
|
|
|
missing_features = rng.choice(n_features, n_samples, replace=True)
|
|
|
|
|
X_missing[missing_samples, missing_features] = np.nan
|
|
|
|
|
|
|
|
|
|
# Estimate the score after imputation (mean and median strategies)
|
|
|
|
|
score_simple_imputer = pd.DataFrame()
|
|
|
|
|
for strategy in ('mean', 'median'):
|
|
|
|
|
estimator = make_pipeline(
|
|
|
|
|
SimpleImputer(missing_values=np.nan, strategy=strategy),
|
|
|
|
|
br_estimator
|
|
|
|
|
)
|
|
|
|
|
score_simple_imputer[strategy] = cross_val_score(
|
|
|
|
|
estimator, X_missing, y_missing, scoring='neg_mean_squared_error',
|
|
|
|
|
cv=N_SPLITS
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# Estimate the score after iterative imputation of the missing values
|
|
|
|
|
# with different estimators
|
|
|
|
|
estimators = [
|
|
|
|
|
BayesianRidge(),
|
|
|
|
|
DecisionTreeRegressor(max_features='sqrt', random_state=0),
|
2019-03-05 00:30:21 +08:00
|
|
|
ExtraTreesRegressor(n_estimators=10, random_state=0),
|
2019-02-15 09:05:36 +08:00
|
|
|
KNeighborsRegressor(n_neighbors=15)
|
|
|
|
|
]
|
|
|
|
|
score_iterative_imputer = pd.DataFrame()
|
2019-03-04 23:31:41 +08:00
|
|
|
for impute_estimator in estimators:
|
2019-02-15 09:05:36 +08:00
|
|
|
estimator = make_pipeline(
|
2019-03-04 23:31:41 +08:00
|
|
|
IterativeImputer(random_state=0, estimator=impute_estimator),
|
2019-02-15 09:05:36 +08:00
|
|
|
br_estimator
|
|
|
|
|
)
|
2019-03-04 23:31:41 +08:00
|
|
|
score_iterative_imputer[impute_estimator.__class__.__name__] = \
|
2019-02-15 09:05:36 +08:00
|
|
|
cross_val_score(
|
|
|
|
|
estimator, X_missing, y_missing, scoring='neg_mean_squared_error',
|
|
|
|
|
cv=N_SPLITS
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
scores = pd.concat(
|
|
|
|
|
[score_full_data, score_simple_imputer, score_iterative_imputer],
|
|
|
|
|
keys=['Original', 'SimpleImputer', 'IterativeImputer'], axis=1
|
|
|
|
|
)
|
|
|
|
|
|
2020-04-11 01:10:34 +08:00
|
|
|
# plot california housing results
|
2019-02-15 09:05:36 +08:00
|
|
|
fig, ax = plt.subplots(figsize=(13, 6))
|
|
|
|
|
means = -scores.mean()
|
|
|
|
|
errors = scores.std()
|
|
|
|
|
means.plot.barh(xerr=errors, ax=ax)
|
|
|
|
|
ax.set_title('California Housing Regression with Different Imputation Methods')
|
|
|
|
|
ax.set_xlabel('MSE (smaller is better)')
|
|
|
|
|
ax.set_yticks(np.arange(means.shape[0]))
|
2020-01-31 02:06:00 +08:00
|
|
|
ax.set_yticklabels([" w/ ".join(label) for label in means.index.tolist()])
|
2019-02-15 09:05:36 +08:00
|
|
|
plt.tight_layout(pad=1)
|
|
|
|
|
plt.show()
|