scikit-learn/examples/model_selection/plot_cv_indices.py

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

219 lines
6.1 KiB
Python
Raw Normal View History

2018-07-31 08:41:12 +08:00
"""
Visualizing cross-validation behavior in scikit-learn
=====================================================
Choosing the right cross-validation object is a crucial part of fitting a
model properly. There are many ways to split data into training and test
sets in order to avoid model overfitting, to standardize the number of
groups in test sets, etc.
This example visualizes the behavior of several common scikit-learn objects
for comparison.
2018-07-31 08:41:12 +08:00
"""
from sklearn.model_selection import (
TimeSeriesSplit,
KFold,
ShuffleSplit,
StratifiedKFold,
GroupShuffleSplit,
Stratified Group KFold implementation (#18649) * Initial implementation * Forgot to add to second __add__ list * Update split method parameter doc * Added example; changed default test_size to 0.1; added to author list * StratifiedGroupKFold impl and other improvements * Add class to __all__ spec * Remove random_state when no shuffle * Tighter formatting * Update the implementation of StratifiedGroupKFold * Add StratifiedGroupKFold to __init__ * Add y checks to StartifiedGroupKFold * Raise error if n_splits > max num samples in class * Warn if n_splits > mn num samples in class * Add SGKfold to general repr test * Add SGKFold to 2d_y test case * Add SGKfold to value erros test case Parameters are the same as for StratifiedKFold to ensure similar behavior given n_groups == n_samples * Add SGKFold to StratifiedKFold test cases The idea is to ensure similar behavior when groups are trivial (n_groups == n_samples) * Add SGKFold to reproducibility test case * Add SGKFold to GroupKFold test case * Add SGKFold to nested cv test case * Add SGKFold to random_state with shuffle=False test case * Add SGKFold to constant splits test case * Fix repr test case * Fix formatting issues * Add samples to a fold with least num samples Required to produce balanced size folds when the distribution of y is more or less the same * Remove GroupShuffleSplit impl * Add notes to StratifiedGroupKFold * Fix doctest * Added stratified group kfold tests * Better variable naming * Add section to documentation * Remove leftover StratifiedGroupShuffleSplit import * Add changelist and reference to original kernel * Better naming for least populated class check * Better expression for number of labels * Remove use of Counter We already have this data in output of np.unique * Add tests for homogeneous groups * Add StratifiedGroupKFold test against GroupKFold * Add changes to changelist in docstring * Add StratifiedGroupKFold to classes.rst * Fix description of StratifiedGroupKFold * Move license notice out of docstring * Disambiguate labels to classes in doc * Add changelog entry * Fix changelog author entry * Fix StratifiedGroupKFold docstring * Better variable names * Remove defaultdict in favor of numpy indexing * Extracted best_fold search into a separate method * Make use of numpy broadcasting instead of for loop * Encode groups and use arrays instead of dicts * Use numpy sort instead of python * Clarify shuffling behavior of StratifiedGroupKF in docs * Switch name from label_idx to class_idx * Remove accidentally leftover comment * Fix np.sort keyword to support numpy < 1.15 * Fix typo in docstring * Add StratifiedGroupKFold to visualization doc * Add visualization for uneven group as an example * Fix image numbers to match updated example * Add author * Add SGKF visualization to docs * Add comments for groups in stratified CV tests Co-authored-by: Leandro Hermida <hermidal@cs.umd.edu> Co-authored-by: marrodion <rodion_martynov@epam.com>
2021-03-20 18:57:42 +08:00
GroupKFold,
StratifiedShuffleSplit,
StratifiedGroupKFold,
)
2018-07-31 08:41:12 +08:00
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Patch
2018-07-31 08:41:12 +08:00
np.random.seed(1338)
cmap_data = plt.cm.Paired
cmap_cv = plt.cm.coolwarm
n_splits = 4
# %%
2018-07-31 08:41:12 +08:00
# Visualize our data
# ------------------
#
# First, we must understand the structure of our data. It has 100 randomly
# generated input datapoints, 3 classes split unevenly across datapoints,
# and 10 "groups" split evenly across datapoints.
#
# As we'll see, some cross-validation objects do specific things with
# labeled data, others behave differently with grouped data, and others
# do not use this information.
#
# To begin, we'll visualize our data.
# Generate the class/group data
n_points = 100
X = np.random.randn(100, 10)
percentiles_classes = [0.1, 0.3, 0.6]
y = np.hstack([[ii] * int(100 * perc) for ii, perc in enumerate(percentiles_classes)])
# Evenly spaced groups repeated once
groups = np.hstack([[ii] * 10 for ii in range(10)])
def visualize_groups(classes, groups, name):
# Visualize dataset groups
fig, ax = plt.subplots()
ax.scatter(
range(len(groups)),
[0.5] * len(groups),
c=groups,
marker="_",
lw=50,
cmap=cmap_data,
)
ax.scatter(
range(len(groups)),
[3.5] * len(groups),
c=classes,
marker="_",
lw=50,
cmap=cmap_data,
)
ax.set(
ylim=[-1, 5],
yticks=[0.5, 3.5],
yticklabels=["Data\ngroup", "Data\nclass"],
xlabel="Sample index",
)
2018-07-31 08:41:12 +08:00
visualize_groups(y, groups, "no groups")
# %%
2018-07-31 08:41:12 +08:00
# Define a function to visualize cross-validation behavior
# --------------------------------------------------------
#
# We'll define a function that lets us visualize the behavior of each
# cross-validation object. We'll perform 4 splits of the data. On each
# split, we'll visualize the indices chosen for the training set
# (in blue) and the test set (in red).
def plot_cv_indices(cv, X, y, group, ax, n_splits, lw=10):
"""Create a sample plot for indices of a cross-validation object."""
# Generate the training/testing visualizations for each CV split
for ii, (tr, tt) in enumerate(cv.split(X=X, y=y, groups=group)):
# Fill in indices with the training/test groups
indices = np.array([np.nan] * len(X))
indices[tt] = 1
indices[tr] = 0
# Visualize the results
ax.scatter(
range(len(indices)),
[ii + 0.5] * len(indices),
c=indices,
marker="_",
lw=lw,
cmap=cmap_cv,
vmin=-0.2,
vmax=1.2,
)
# Plot the data classes and groups at the end
ax.scatter(
range(len(X)), [ii + 1.5] * len(X), c=y, marker="_", lw=lw, cmap=cmap_data
)
ax.scatter(
range(len(X)), [ii + 2.5] * len(X), c=group, marker="_", lw=lw, cmap=cmap_data
)
# Formatting
yticklabels = list(range(n_splits)) + ["class", "group"]
ax.set(
yticks=np.arange(n_splits + 2) + 0.5,
yticklabels=yticklabels,
xlabel="Sample index",
ylabel="CV iteration",
ylim=[n_splits + 2.2, -0.2],
xlim=[0, 100],
)
ax.set_title("{}".format(type(cv).__name__), fontsize=15)
return ax
# %%
# Let's see how it looks for the :class:`~sklearn.model_selection.KFold`
# cross-validation object:
2018-07-31 08:41:12 +08:00
fig, ax = plt.subplots()
cv = KFold(n_splits)
plot_cv_indices(cv, X, y, groups, ax, n_splits)
# %%
2018-07-31 08:41:12 +08:00
# As you can see, by default the KFold cross-validation iterator does not
# take either datapoint class or group into consideration. We can change this
Stratified Group KFold implementation (#18649) * Initial implementation * Forgot to add to second __add__ list * Update split method parameter doc * Added example; changed default test_size to 0.1; added to author list * StratifiedGroupKFold impl and other improvements * Add class to __all__ spec * Remove random_state when no shuffle * Tighter formatting * Update the implementation of StratifiedGroupKFold * Add StratifiedGroupKFold to __init__ * Add y checks to StartifiedGroupKFold * Raise error if n_splits > max num samples in class * Warn if n_splits > mn num samples in class * Add SGKfold to general repr test * Add SGKFold to 2d_y test case * Add SGKfold to value erros test case Parameters are the same as for StratifiedKFold to ensure similar behavior given n_groups == n_samples * Add SGKFold to StratifiedKFold test cases The idea is to ensure similar behavior when groups are trivial (n_groups == n_samples) * Add SGKFold to reproducibility test case * Add SGKFold to GroupKFold test case * Add SGKFold to nested cv test case * Add SGKFold to random_state with shuffle=False test case * Add SGKFold to constant splits test case * Fix repr test case * Fix formatting issues * Add samples to a fold with least num samples Required to produce balanced size folds when the distribution of y is more or less the same * Remove GroupShuffleSplit impl * Add notes to StratifiedGroupKFold * Fix doctest * Added stratified group kfold tests * Better variable naming * Add section to documentation * Remove leftover StratifiedGroupShuffleSplit import * Add changelist and reference to original kernel * Better naming for least populated class check * Better expression for number of labels * Remove use of Counter We already have this data in output of np.unique * Add tests for homogeneous groups * Add StratifiedGroupKFold test against GroupKFold * Add changes to changelist in docstring * Add StratifiedGroupKFold to classes.rst * Fix description of StratifiedGroupKFold * Move license notice out of docstring * Disambiguate labels to classes in doc * Add changelog entry * Fix changelog author entry * Fix StratifiedGroupKFold docstring * Better variable names * Remove defaultdict in favor of numpy indexing * Extracted best_fold search into a separate method * Make use of numpy broadcasting instead of for loop * Encode groups and use arrays instead of dicts * Use numpy sort instead of python * Clarify shuffling behavior of StratifiedGroupKF in docs * Switch name from label_idx to class_idx * Remove accidentally leftover comment * Fix np.sort keyword to support numpy < 1.15 * Fix typo in docstring * Add StratifiedGroupKFold to visualization doc * Add visualization for uneven group as an example * Fix image numbers to match updated example * Add author * Add SGKF visualization to docs * Add comments for groups in stratified CV tests Co-authored-by: Leandro Hermida <hermidal@cs.umd.edu> Co-authored-by: marrodion <rodion_martynov@epam.com>
2021-03-20 18:57:42 +08:00
# by using either:
#
# - ``StratifiedKFold`` to preserve the percentage of samples for each class.
# - ``GroupKFold`` to ensure that the same group will not appear in two
# different folds.
# - ``StratifiedGroupKFold`` to keep the constraint of ``GroupKFold`` while
# attempting to return stratified folds.
2018-07-31 08:41:12 +08:00
Stratified Group KFold implementation (#18649) * Initial implementation * Forgot to add to second __add__ list * Update split method parameter doc * Added example; changed default test_size to 0.1; added to author list * StratifiedGroupKFold impl and other improvements * Add class to __all__ spec * Remove random_state when no shuffle * Tighter formatting * Update the implementation of StratifiedGroupKFold * Add StratifiedGroupKFold to __init__ * Add y checks to StartifiedGroupKFold * Raise error if n_splits > max num samples in class * Warn if n_splits > mn num samples in class * Add SGKfold to general repr test * Add SGKFold to 2d_y test case * Add SGKfold to value erros test case Parameters are the same as for StratifiedKFold to ensure similar behavior given n_groups == n_samples * Add SGKFold to StratifiedKFold test cases The idea is to ensure similar behavior when groups are trivial (n_groups == n_samples) * Add SGKFold to reproducibility test case * Add SGKFold to GroupKFold test case * Add SGKFold to nested cv test case * Add SGKFold to random_state with shuffle=False test case * Add SGKFold to constant splits test case * Fix repr test case * Fix formatting issues * Add samples to a fold with least num samples Required to produce balanced size folds when the distribution of y is more or less the same * Remove GroupShuffleSplit impl * Add notes to StratifiedGroupKFold * Fix doctest * Added stratified group kfold tests * Better variable naming * Add section to documentation * Remove leftover StratifiedGroupShuffleSplit import * Add changelist and reference to original kernel * Better naming for least populated class check * Better expression for number of labels * Remove use of Counter We already have this data in output of np.unique * Add tests for homogeneous groups * Add StratifiedGroupKFold test against GroupKFold * Add changes to changelist in docstring * Add StratifiedGroupKFold to classes.rst * Fix description of StratifiedGroupKFold * Move license notice out of docstring * Disambiguate labels to classes in doc * Add changelog entry * Fix changelog author entry * Fix StratifiedGroupKFold docstring * Better variable names * Remove defaultdict in favor of numpy indexing * Extracted best_fold search into a separate method * Make use of numpy broadcasting instead of for loop * Encode groups and use arrays instead of dicts * Use numpy sort instead of python * Clarify shuffling behavior of StratifiedGroupKF in docs * Switch name from label_idx to class_idx * Remove accidentally leftover comment * Fix np.sort keyword to support numpy < 1.15 * Fix typo in docstring * Add StratifiedGroupKFold to visualization doc * Add visualization for uneven group as an example * Fix image numbers to match updated example * Add author * Add SGKF visualization to docs * Add comments for groups in stratified CV tests Co-authored-by: Leandro Hermida <hermidal@cs.umd.edu> Co-authored-by: marrodion <rodion_martynov@epam.com>
2021-03-20 18:57:42 +08:00
# To better demonstrate the difference, we will assign samples to groups
# unevenly:
uneven_groups = np.sort(np.random.randint(0, 10, n_points))
cvs = [StratifiedKFold, GroupKFold, StratifiedGroupKFold]
for cv in cvs:
fig, ax = plt.subplots(figsize=(6, 3))
plot_cv_indices(cv(n_splits), X, y, uneven_groups, ax, n_splits)
ax.legend(
[Patch(color=cmap_cv(0.8)), Patch(color=cmap_cv(0.02))],
["Testing set", "Training set"],
loc=(1.02, 0.8),
)
# Make the legend fit
plt.tight_layout()
fig.subplots_adjust(right=0.7)
2018-07-31 08:41:12 +08:00
# %%
Stratified Group KFold implementation (#18649) * Initial implementation * Forgot to add to second __add__ list * Update split method parameter doc * Added example; changed default test_size to 0.1; added to author list * StratifiedGroupKFold impl and other improvements * Add class to __all__ spec * Remove random_state when no shuffle * Tighter formatting * Update the implementation of StratifiedGroupKFold * Add StratifiedGroupKFold to __init__ * Add y checks to StartifiedGroupKFold * Raise error if n_splits > max num samples in class * Warn if n_splits > mn num samples in class * Add SGKfold to general repr test * Add SGKFold to 2d_y test case * Add SGKfold to value erros test case Parameters are the same as for StratifiedKFold to ensure similar behavior given n_groups == n_samples * Add SGKFold to StratifiedKFold test cases The idea is to ensure similar behavior when groups are trivial (n_groups == n_samples) * Add SGKFold to reproducibility test case * Add SGKFold to GroupKFold test case * Add SGKFold to nested cv test case * Add SGKFold to random_state with shuffle=False test case * Add SGKFold to constant splits test case * Fix repr test case * Fix formatting issues * Add samples to a fold with least num samples Required to produce balanced size folds when the distribution of y is more or less the same * Remove GroupShuffleSplit impl * Add notes to StratifiedGroupKFold * Fix doctest * Added stratified group kfold tests * Better variable naming * Add section to documentation * Remove leftover StratifiedGroupShuffleSplit import * Add changelist and reference to original kernel * Better naming for least populated class check * Better expression for number of labels * Remove use of Counter We already have this data in output of np.unique * Add tests for homogeneous groups * Add StratifiedGroupKFold test against GroupKFold * Add changes to changelist in docstring * Add StratifiedGroupKFold to classes.rst * Fix description of StratifiedGroupKFold * Move license notice out of docstring * Disambiguate labels to classes in doc * Add changelog entry * Fix changelog author entry * Fix StratifiedGroupKFold docstring * Better variable names * Remove defaultdict in favor of numpy indexing * Extracted best_fold search into a separate method * Make use of numpy broadcasting instead of for loop * Encode groups and use arrays instead of dicts * Use numpy sort instead of python * Clarify shuffling behavior of StratifiedGroupKF in docs * Switch name from label_idx to class_idx * Remove accidentally leftover comment * Fix np.sort keyword to support numpy < 1.15 * Fix typo in docstring * Add StratifiedGroupKFold to visualization doc * Add visualization for uneven group as an example * Fix image numbers to match updated example * Add author * Add SGKF visualization to docs * Add comments for groups in stratified CV tests Co-authored-by: Leandro Hermida <hermidal@cs.umd.edu> Co-authored-by: marrodion <rodion_martynov@epam.com>
2021-03-20 18:57:42 +08:00
# Next we'll visualize this behavior for a number of CV iterators.
2018-07-31 08:41:12 +08:00
#
# Visualize cross-validation indices for many CV objects
# ------------------------------------------------------
#
# Let's visually compare the cross validation behavior for many
# scikit-learn cross-validation objects. Below we will loop through several
# common cross-validation objects, visualizing the behavior of each.
#
# Note how some use the group/class information while others do not.
Stratified Group KFold implementation (#18649) * Initial implementation * Forgot to add to second __add__ list * Update split method parameter doc * Added example; changed default test_size to 0.1; added to author list * StratifiedGroupKFold impl and other improvements * Add class to __all__ spec * Remove random_state when no shuffle * Tighter formatting * Update the implementation of StratifiedGroupKFold * Add StratifiedGroupKFold to __init__ * Add y checks to StartifiedGroupKFold * Raise error if n_splits > max num samples in class * Warn if n_splits > mn num samples in class * Add SGKfold to general repr test * Add SGKFold to 2d_y test case * Add SGKfold to value erros test case Parameters are the same as for StratifiedKFold to ensure similar behavior given n_groups == n_samples * Add SGKFold to StratifiedKFold test cases The idea is to ensure similar behavior when groups are trivial (n_groups == n_samples) * Add SGKFold to reproducibility test case * Add SGKFold to GroupKFold test case * Add SGKFold to nested cv test case * Add SGKFold to random_state with shuffle=False test case * Add SGKFold to constant splits test case * Fix repr test case * Fix formatting issues * Add samples to a fold with least num samples Required to produce balanced size folds when the distribution of y is more or less the same * Remove GroupShuffleSplit impl * Add notes to StratifiedGroupKFold * Fix doctest * Added stratified group kfold tests * Better variable naming * Add section to documentation * Remove leftover StratifiedGroupShuffleSplit import * Add changelist and reference to original kernel * Better naming for least populated class check * Better expression for number of labels * Remove use of Counter We already have this data in output of np.unique * Add tests for homogeneous groups * Add StratifiedGroupKFold test against GroupKFold * Add changes to changelist in docstring * Add StratifiedGroupKFold to classes.rst * Fix description of StratifiedGroupKFold * Move license notice out of docstring * Disambiguate labels to classes in doc * Add changelog entry * Fix changelog author entry * Fix StratifiedGroupKFold docstring * Better variable names * Remove defaultdict in favor of numpy indexing * Extracted best_fold search into a separate method * Make use of numpy broadcasting instead of for loop * Encode groups and use arrays instead of dicts * Use numpy sort instead of python * Clarify shuffling behavior of StratifiedGroupKF in docs * Switch name from label_idx to class_idx * Remove accidentally leftover comment * Fix np.sort keyword to support numpy < 1.15 * Fix typo in docstring * Add StratifiedGroupKFold to visualization doc * Add visualization for uneven group as an example * Fix image numbers to match updated example * Add author * Add SGKF visualization to docs * Add comments for groups in stratified CV tests Co-authored-by: Leandro Hermida <hermidal@cs.umd.edu> Co-authored-by: marrodion <rodion_martynov@epam.com>
2021-03-20 18:57:42 +08:00
cvs = [
KFold,
GroupKFold,
ShuffleSplit,
StratifiedKFold,
StratifiedGroupKFold,
2018-07-31 08:41:12 +08:00
GroupShuffleSplit,
StratifiedShuffleSplit,
TimeSeriesSplit,
]
for cv in cvs:
this_cv = cv(n_splits=n_splits)
fig, ax = plt.subplots(figsize=(6, 3))
plot_cv_indices(this_cv, X, y, groups, ax, n_splits)
ax.legend(
[Patch(color=cmap_cv(0.8)), Patch(color=cmap_cv(0.02))],
["Testing set", "Training set"],
loc=(1.02, 0.8),
)
# Make the legend fit
plt.tight_layout()
fig.subplots_adjust(right=0.7)
plt.show()