Spec-Zone.ru › scikit-learn

Note

Go to the end to download the full example code or to run this example in your browser via JupyterLite or Binder.

Common pitfalls in the interpretation of coefficients of linear models

In linear models, the target value is modeled as a linear combination of the features (see the Linear Models User Guide section for a description of a set of linear models available in scikit-learn). Coefficients in multiple linear models represent the relationship between the given feature, \(X_i\) and the target, \(y\), assuming that all the other features remain constant (conditional dependence). This is different from plotting \(X_i\) versus \(y\) and fitting a linear relationship: in that case all possible values of the other features are taken into account in the estimation (marginal dependence).

This example will provide some hints in interpreting coefficient in linear models, pointing at problems that arise when either the linear model is not appropriate to describe the dataset, or when features are correlated.

Note

Keep in mind that the features \(X\) and the outcome \(y\) are in general the result of a data generating process that is unknown to us. Machine learning models are trained to approximate the unobserved mathematical function that links \(X\) to \(y\) from sample data. As a result, any interpretation made about a model may not necessarily generalize to the true data generating process. This is especially true when the model is of bad quality or when the sample data is not representative of the population.

We will use data from the “Current Population Survey” from 1985 to predict wage as a function of various features such as experience, age, or education.

# Authors: The scikit-learn developers
# SPDX-License-Identifier: BSD-3-Clause
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import scipy as sp
import seaborn as sns

The dataset: wages

We fetch the data from OpenML. Note that setting the parameter as_frame to True will retrieve the data as a pandas dataframe.

from sklearn.datasets import fetch_openml

survey = fetch_openml(data_id=534, as_frame=True)

Then, we identify features X and target y: the column WAGE is our target variable (i.e. the variable which we want to predict).

X = survey.data[survey.feature_names]
X.describe(include="all")
EDUCATION SOUTH SEX EXPERIENCE UNION AGE RACE OCCUPATION SECTOR MARR
count 534.000000 534 534 534.000000 534 534.000000 534 534 534 534
unique NaN 2 2 NaN 2 NaN 3 6 3 2
top NaN no male NaN not_member NaN White Other Other Married
freq NaN 378 289 NaN 438 NaN 440 156 411 350
mean 13.018727 NaN NaN 17.822097 NaN 36.833333 NaN NaN NaN NaN
std 2.615373 NaN NaN 12.379710 NaN 11.726573 NaN NaN NaN NaN
min 2.000000 NaN NaN 0.000000 NaN 18.000000 NaN NaN NaN NaN
25% 12.000000 NaN NaN 8.000000 NaN 28.000000 NaN NaN NaN NaN
50% 12.000000 NaN NaN 15.000000 NaN 35.000000 NaN NaN NaN NaN
75% 15.000000 NaN NaN 26.000000 NaN 44.000000 NaN NaN NaN NaN
max 18.000000 NaN NaN 55.000000 NaN 64.000000 NaN NaN NaN NaN


Note that the dataset contains categorical and numerical variables. We will need to take this into account when preprocessing the dataset thereafter.

X.head()
EDUCATION SOUTH SEX EXPERIENCE UNION AGE RACE OCCUPATION SECTOR MARR
0 8 no female 21 not_member 35 Hispanic Other Manufacturing Married
1 9 no female 42 not_member 57 White Other Manufacturing Married
2 12 no male 1 not_member 19 White Other Manufacturing Unmarried
3 12 no male 4 not_member 22 White Other Other Unmarried
4 12 no male 17 not_member 35 White Other Other Married


Our target for prediction: the wage. Wages are described as floating-point number in dollars per hour.

y = survey.target.values.ravel()
survey.target.head()
0    5.10
1    4.95
2    6.67
3    4.00
4    7.50
Name: WAGE, dtype: float64

We split the sample into a train and a test dataset. Only the train dataset will be used in the following exploratory analysis. This is a way to emulate a real situation where predictions are performed on an unknown target, and we don’t want our analysis and decisions to be biased by our knowledge of the test data.

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)

First, let’s get some insights by looking at the variables’ distributions and at the pairwise relationships between them. Only numerical variables will be used. In the following plot, each dot represents a sample.

train_dataset = X_train.copy()
train_dataset.insert(0, "WAGE", y_train)
_ = sns.pairplot(train_dataset, kind="reg", diag_kind="kde")
plot linear model coefficient interpretation

Looking closely at the WAGE distribution reveals that it has a long tail. For this reason, we should take its logarithm to turn it approximately into a normal distribution (linear models such as ridge or lasso work best for a normal distribution of error).

The WAGE is increasing when EDUCATION is increasing. Note that the dependence between WAGE and EDUCATION represented here is a marginal dependence, i.e. it describes the behavior of a specific variable without keeping the others fixed.

Also, the EXPERIENCE and AGE are strongly linearly correlated.

The machine-learning pipeline

To design our machine-learning pipeline, we first manually check the type of data that we are dealing with:

survey.data.info()
<class 'pandas.DataFrame'>
RangeIndex: 534 entries, 0 to 533
Data columns (total 10 columns):
 #   Column      Non-Null Count  Dtype
---  ------      --------------  -----
 0   EDUCATION   534 non-null    int64
 1   SOUTH       534 non-null    category
 2   SEX         534 non-null    category
 3   EXPERIENCE  534 non-null    int64
 4   UNION       534 non-null    category
 5   AGE         534 non-null    int64
 6   RACE        534 non-null    category
 7   OCCUPATION  534 non-null    category
 8   SECTOR      534 non-null    category
 9   MARR        534 non-null    category
dtypes: category(7), int64(3)
memory usage: 17.3 KB

As seen previously, the dataset contains columns with different data types and we need to apply a specific preprocessing for each data types. In particular categorical variables cannot be included in linear model if not coded as integers first. In addition, to avoid categorical features to be treated as ordered values, we need to one-hot-encode them. Our pre-processor will:

  • one-hot encode (i.e., generate a column by category) the categorical columns, only for non-binary categorical variables;
  • as a first approach (we will see after how the normalisation of numerical values will affect our discussion), keep numerical values as they are.
from sklearn.compose import make_column_transformer
from sklearn.preprocessing import OneHotEncoder

categorical_columns = ["RACE", "OCCUPATION", "SECTOR", "MARR", "UNION", "SEX", "SOUTH"]
numerical_columns = ["EDUCATION", "EXPERIENCE", "AGE"]

preprocessor = make_column_transformer(
    (OneHotEncoder(drop="if_binary"), categorical_columns),
    remainder="passthrough",
    verbose_feature_names_out=False,  # avoid to prepend the preprocessor names
)

We use a ridge regressor with a very small regularization to model the logarithm of the WAGE.

from sklearn.compose import TransformedTargetRegressor
from sklearn.linear_model import Ridge
from sklearn.pipeline import make_pipeline

model = make_pipeline(
    preprocessor,
    TransformedTargetRegressor(
        regressor=Ridge(alpha=1e-10), func=np.log10, inverse_func=sp.special.exp10
    ),
)

Processing the dataset

First, we fit the model.

model.fit(X_train, y_train)
Pipeline(steps=[('columntransformer',
                 ColumnTransformer(remainder='passthrough',
                                   transformers=[('onehotencoder',
                                                  OneHotEncoder(drop='if_binary'),
                                                  ['RACE', 'OCCUPATION',
                                                   'SECTOR', 'MARR', 'UNION',
                                                   'SEX', 'SOUTH'])],
                                   verbose_feature_names_out=False)),
                ('transformedtargetregressor',
                 TransformedTargetRegressor(func=<ufunc 'log10'>,
                                            inverse_func=<ufunc 'exp10'>,
                                            regressor=Ridge(alpha=1e-10)))])
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
Parameters
steps steps: list of tuples

List of (name of step, estimator) tuples that are to be chained in
sequential order. To be compatible with the scikit-learn API, all steps
must define `fit`. All non-last steps must also define `transform`. See
:ref:`Combining Estimators <combining_estimators>` for more details.
[('columntransformer', ...), ('transformedtargetregressor', ...)]
transform_input transform_input: list of str, default=None

The names of the :term:`metadata` parameters that should be transformed by the
pipeline before passing it to the step consuming it.

This enables transforming some input arguments to ``fit`` (other than ``X``)
to be transformed by the steps of the pipeline up to the step which requires
them. Requirement is defined via :ref:`metadata routing <metadata_routing>`.
For instance, this can be used to pass a validation set through the pipeline.

You can only set this if metadata routing is enabled, which you
can enable using ``sklearn.set_config(enable_metadata_routing=True)``.

.. versionadded:: 1.6
None
memory memory: str or object with the joblib.Memory interface, default=None

Used to cache the fitted transformers of the pipeline. The last step
will never be cached, even if it is a transformer. By default, no
caching is performed. If a string is given, it is the path to the
caching directory. Enabling caching triggers a clone of the transformers
before fitting. Therefore, the transformer instance given to the
pipeline cannot be inspected directly. Use the attribute ``named_steps``
or ``steps`` to inspect estimators within the pipeline. Caching the
transformers is advantageous when fitting is time consuming. See
:ref:`sphx_glr_auto_examples_neighbors_plot_caching_nearest_neighbors.py`
for an example on how to enable caching.
None
verbose verbose: bool, default=False

If True, the time elapsed while fitting each step will be printed as it
is completed.
False
Fitted attributes
Name Type Value
feature_names_in_ feature_names_in_: ndarray of shape (`n_features_in_`,)

Names of features seen during :term:`fit`. Only defined if the
underlying estimator exposes such an attribute when fit.

.. versionadded:: 1.0
ndarray[object](10,) ['EDUCATION','SOUTH','SEX',...,'OCCUPATION','SECTOR','MARR']
n_features_in_ n_features_in_: int

Number of features seen during :term:`fit`. Only defined if the
underlying first estimator in `steps` exposes such an attribute
when fit.

.. versionadded:: 0.24
int 10
Parameters
transformers transformers: list of tuples

List of (name, transformer, columns) tuples specifying the
transformer objects to be applied to subsets of the data.

name : str
Like in Pipeline and FeatureUnion, this allows the transformer and
its parameters to be set using ``set_params`` and searched in grid
search.
transformer : {'drop', 'passthrough'} or estimator
Estimator must support :term:`fit` and :term:`transform`.
Special-cased strings 'drop' and 'passthrough' are accepted as
well, to indicate to drop the columns or to pass them through
untransformed, respectively.
columns : str, array-like of str, int, array-like of int, array-like of bool, slice or callable
Indexes the data on its second axis. Integers are interpreted as
positional columns, while strings can reference DataFrame columns
by name. A scalar string or int should be used where
``transformer`` expects X to be a 1d array-like (vector),
otherwise a 2d array will be passed to the transformer.
A callable is passed the input data `X` and can return any of the
above. To select multiple columns by name or dtype, you can use
:obj:`make_column_selector`.
[('onehotencoder', ...)]
remainder remainder: {'drop', 'passthrough'} or estimator, default='drop'

By default, only the specified columns in `transformers` are
transformed and combined in the output, and the non-specified
columns are dropped. (default of ``'drop'``).
By specifying ``remainder='passthrough'``, all remaining columns that
were not specified in `transformers`, but present in the data passed
to `fit` will be automatically passed through. This subset of columns
is concatenated with the output of the transformers. For dataframes,
extra columns not seen during `fit` will be excluded from the output
of `transform`.
By setting ``remainder`` to be an estimator, the remaining
non-specified columns will use the ``remainder`` estimator. The
estimator must support :term:`fit` and :term:`transform`.
Note that using this feature requires that the DataFrame columns
input at :term:`fit` and :term:`transform` have identical order.
'passthrough'
verbose_feature_names_out verbose_feature_names_out: bool, str or Callable[[str, str], str], default=True

- If True, :meth:`ColumnTransformer.get_feature_names_out` will prefix
all feature names with the name of the transformer that generated that
feature. It is equivalent to setting
`verbose_feature_names_out="{transformer_name}__{feature_name}"`.
- If False, :meth:`ColumnTransformer.get_feature_names_out` will not
prefix any feature names and will error if feature names are not
unique.
- If ``Callable[[str, str], str]``,
:meth:`ColumnTransformer.get_feature_names_out` will rename all the features
using the name of the transformer. The first argument of the callable is the
transformer name and the second argument is the feature name. The returned
string will be the new feature name.
- If ``str``, it must be a string ready for formatting. The given string will
be formatted using two field names: ``transformer_name`` and ``feature_name``.
e.g. ``"{feature_name}__{transformer_name}"``. See :meth:`str.format` method
from the standard library for more info.

.. versionadded:: 1.0

.. versionchanged:: 1.6
`verbose_feature_names_out` can be a callable or a string to be formatted.
False
sparse_threshold sparse_threshold: float, default=0.3

If the output of the different transformers contains sparse matrices,
these will be stacked as a sparse matrix if the overall density is
lower than this value. Use ``sparse_threshold=0`` to always return
dense. When the transformed output consists of all dense data, the
stacked result will be dense, and this keyword will be ignored.
0.3
n_jobs n_jobs: int, default=None

Number of jobs to run in parallel.
``None`` means 1 unless in a :obj:`joblib.parallel_backend` context.
``-1`` means using all processors. See :term:`Glossary <n_jobs>`
for more details.
None
transformer_weights transformer_weights: dict, default=None

Multiplicative weights for features per transformer. The output of the
transformer is multiplied by these weights. Keys are transformer names,
values the weights.
None
verbose verbose: bool, default=False

If True, the time elapsed while fitting each transformer will be
printed as it is completed.
False
Fitted attributes
Name Type Value
feature_names_in_ feature_names_in_: ndarray of shape (`n_features_in_`,)

Names of features seen during :term:`fit`. Defined only when `X`
has feature names that are all strings.

.. versionadded:: 1.0
ndarray[object](10,) ['EDUCATION','SOUTH','SEX',...,'OCCUPATION','SECTOR','MARR']
n_features_in_ n_features_in_: int

Number of features seen during :term:`fit`. Only defined if the
underlying transformers expose such an attribute when fit.

.. versionadded:: 0.24
int 10
named_transformers_ named_transformers_: :class:`~sklearn.utils.Bunch`

Read-only attribute to access any transformer by given name.
Keys are transformer names and values are the fitted transformer
objects.
Bunch {'onehotencod...'one-to-one')}
output_indices_ output_indices_: dict

A dictionary from each transformer name to a slice, where the slice
corresponds to indices in the transformed output. This is useful to
inspect which transformer is responsible for which transformed
feature(s).

.. versionadded:: 1.0
dict {'on...er': slice(0, 16, None), 're...er': slice(16, 19, None)}
sparse_output_ sparse_output_: bool

Boolean flag indicating whether the output of ``transform`` is a
sparse matrix or a dense numpy array, which depends on the output
of the individual transformers and the `sparse_threshold` keyword.
bool False
transformers_ transformers_: list

The collection of fitted transformers as tuples of (name,
fitted_transformer, column). `fitted_transformer` can be an estimator,
or `'drop'`; `'passthrough'` is replaced with an equivalent
:class:`~sklearn.preprocessing.FunctionTransformer`. In case there were
no columns selected, this will be the unfitted transformer. If there
are remaining columns, the final element is a tuple of the form:
('remainder', transformer, remaining_columns) corresponding to the
``remainder`` parameter. If there are remaining columns, then
``len(transformers_)==len(transformers)+1``, otherwise
``len(transformers_)==len(transformers)``.

.. versionadded:: 1.7
The format of the remaining columns now attempts to match that of the other
transformers: if all columns were provided as column names (`str`), the
remaining columns are stored as column names; if all columns were provided
as mask arrays (`bool`), so are the remaining columns; in all other cases
the remaining columns are stored as indices (`int`).
list [('on...er', OneHotEncoder...p='if_binary'), ['RACE', 'OC...ON', 'SECTOR', 'MARR', ...]), ('re...er', FunctionTrans...='one-to-one'), ['ED...ON', 'EX...CE', 'AGE'])]
['RACE', 'OCCUPATION', 'SECTOR', 'MARR', 'UNION', 'SEX', 'SOUTH']
Parameters
drop drop: {'first', 'if_binary'} or an array-like of shape (n_features,), default=None

Specifies a methodology to use to drop one of the categories per
feature. This is useful in situations where perfectly collinear
features cause problems, such as when feeding the resulting data
into an unregularized linear regression model.

However, dropping one category breaks the symmetry of the original
representation and can therefore induce a bias in downstream models,
for instance for penalized linear classification or regression models.

- None : retain all features (the default).
- 'first' : drop the first category in each feature. If only one
category is present, the feature will be dropped entirely.
- 'if_binary' : drop the first category in each feature with two
categories. Features with 1 or more than 2 categories are
left intact.
- array : ``drop[i]`` is the category in feature ``X[:, i]`` that
should be dropped.

When `max_categories` or `min_frequency` is configured to group
infrequent categories, the dropping behavior is handled after the
grouping.

.. versionadded:: 0.21
The parameter `drop` was added in 0.21.

.. versionchanged:: 0.23
The option `drop='if_binary'` was added in 0.23.

.. versionchanged:: 1.1
Support for dropping infrequent categories.
'if_binary'
categories categories: 'auto' or a list of array-like, default='auto'

Categories (unique values) per feature:

- 'auto' : Determine categories automatically from the training data.
- list : ``categories[i]`` holds the categories expected in the ith
column. The passed categories should not mix strings and numeric
values within a single feature, and should be sorted in case of
numeric values.

The used categories can be found in the ``categories_`` attribute.

.. versionadded:: 0.20
'auto'
sparse_output sparse_output: bool, default=True

When ``True``, it returns a SciPy sparse matrix/array
in "Compressed Sparse Row" (CSR) format.

.. versionadded:: 1.2
`sparse` was renamed to `sparse_output`
True
dtype dtype: number type, default=np.float64

Desired dtype of output.
<class 'numpy.float64'>
handle_unknown handle_unknown: {'error', 'ignore', 'infrequent_if_exist', 'warn'}, default='error'

Specifies the way unknown categories are handled during :meth:`transform`.

- 'error' : Raise an error if an unknown category is present during transform.
- 'ignore' : When an unknown category is encountered during
transform, the resulting one-hot encoded columns for this feature
will be all zeros. In the inverse transform, an unknown category
will be denoted as None.
- 'infrequent_if_exist' : When an unknown category is encountered
during transform, the resulting one-hot encoded columns for this
feature will map to the infrequent category if it exists. The
infrequent category will be mapped to the last position in the
encoding. During inverse transform, an unknown category will be
mapped to the category denoted `'infrequent'` if it exists. If the
`'infrequent'` category does not exist, then :meth:`transform` and
:meth:`inverse_transform` will handle an unknown category as with
`handle_unknown='ignore'`. Infrequent categories exist based on
`min_frequency` and `max_categories`. Read more in the
:ref:`User Guide <encoder_infrequent_categories>`.
- 'warn' : When an unknown category is encountered during transform
a warning is issued, and the encoding then proceeds as described for
`handle_unknown="infrequent_if_exist"`.

.. versionchanged:: 1.1
`'infrequent_if_exist'` was added to automatically handle unknown
categories and infrequent categories.

.. versionadded:: 1.6
The option `"warn"` was added in 1.6.
'error'
min_frequency min_frequency: int or float, default=None

Specifies the minimum frequency below which a category will be
considered infrequent.

- If `int`, categories with a smaller cardinality will be considered
infrequent.

- If `float`, categories with a smaller cardinality than
`min_frequency * n_samples` will be considered infrequent.

.. versionadded:: 1.1
Read more in the :ref:`User Guide <encoder_infrequent_categories>`.
None
max_categories max_categories: int, default=None

Specifies an upper limit to the number of output features for each input
feature when considering infrequent categories. If there are infrequent
categories, `max_categories` includes the category representing the
infrequent categories along with the frequent categories. If `None`,
there is no limit to the number of output features.

.. versionadded:: 1.1
Read more in the :ref:`User Guide <encoder_infrequent_categories>`.
None
feature_name_combiner feature_name_combiner: "concat" or callable, default="concat"

Callable with signature `def callable(input_feature, category)` that returns a
string. This is used to create feature names to be returned by
:meth:`get_feature_names_out`.

`"concat"` concatenates encoded feature name and category with
`feature + "_" + str(category)`.E.g. feature X with values 1, 6, 7 create
feature names `X_1, X_6, X_7`.

.. versionadded:: 1.3
'concat'
Fitted attributes
Name Type Value
categories_ categories_: list of arrays

The categories of each feature determined during fitting
(in order of the features in X and corresponding with the output
of ``transform``). This includes the category specified in ``drop``
(if any).
list [array(['Hispa... dtype=object), array(['Cleri... dtype=object), array(['Const... dtype=object), array(['Marri... dtype=object), ...]
drop_idx_ drop_idx_: array of shape (n_features,)

- ``drop_idx_[i]`` is the index in ``categories_[i]`` of the category
to be dropped for each feature.
- ``drop_idx_[i] = None`` if no category is to be dropped from the
feature with index ``i``, e.g. when `drop='if_binary'` and the
feature isn't binary.
- ``drop_idx_ = None`` if all the transformed features will be
retained.

If infrequent categories are enabled by setting `min_frequency` or
`max_categories` to a non-default value and `drop_idx[i]` corresponds
to an infrequent category, then the entire infrequent category is
dropped.

.. versionchanged:: 0.23
Added the possibility to contain `None` values.
ndarray[object](7,) [None,None,None,...,0,0,0]
feature_names_in_ feature_names_in_: ndarray of shape (`n_features_in_`,)

Names of features seen during :term:`fit`. Defined only when `X`
has feature names that are all strings.

.. versionadded:: 1.0
ndarray[object](7,) ['RACE','OCCUPATION','SECTOR',...,'UNION','SEX','SOUTH']
n_features_in_ n_features_in_: int

Number of features seen during :term:`fit`.

.. versionadded:: 1.0
int 7
16 features
RACE_Hispanic
RACE_Other
RACE_White
OCCUPATION_Clerical
OCCUPATION_Management
OCCUPATION_Other
OCCUPATION_Professional
OCCUPATION_Sales
OCCUPATION_Service
SECTOR_Construction
SECTOR_Manufacturing
SECTOR_Other
MARR_Unmarried
UNION_not_member
SEX_male
SOUTH_yes
['EDUCATION', 'EXPERIENCE', 'AGE']
3 features
EDUCATION
EXPERIENCE
AGE
19 features
RACE_Hispanic
RACE_Other
RACE_White
OCCUPATION_Clerical
OCCUPATION_Management
OCCUPATION_Other
OCCUPATION_Professional
OCCUPATION_Sales
OCCUPATION_Service
SECTOR_Construction
SECTOR_Manufacturing
SECTOR_Other
MARR_Unmarried
UNION_not_member
SEX_male
SOUTH_yes
EDUCATION
EXPERIENCE
AGE
Parameters
regressor regressor: object, default=None

Regressor object such as derived from
:class:`~sklearn.base.RegressorMixin`. This regressor will
automatically be cloned each time prior to fitting. If `regressor is
None`, :class:`~sklearn.linear_model.LinearRegression` is created and used.
Ridge(alpha=1e-10)
func func: function, default=None

Function to apply to `y` before passing to :meth:`fit`. Cannot be set
at the same time as `transformer`. If `func is None`, the function used will be
the identity function. If `func` is set, `inverse_func` also needs to be
provided. The function needs to return a 2-dimensional array.
<ufunc 'log10'>
inverse_func inverse_func: function, default=None

Function to apply to the prediction of the regressor. Cannot be set at
the same time as `transformer`. The inverse function is used to return
predictions to the same space of the original training labels. If
`inverse_func` is set, `func` also needs to be provided. The inverse
function needs to return a 2-dimensional array.
<ufunc 'exp10'>
transformer transformer: object, default=None

Estimator object such as derived from
:class:`~sklearn.base.TransformerMixin`. Cannot be set at the same time
as `func` and `inverse_func`. If `transformer is None` as well as
`func` and `inverse_func`, the transformer will be an identity
transformer. Note that the transformer will be cloned during fitting.
Also, the transformer is restricting `y` to be a numpy array.
None
check_inverse check_inverse: bool, default=True

Whether to check that `transform` followed by `inverse_transform`
or `func` followed by `inverse_func` leads to the original targets.
True
Fitted attributes
Name Type Value
n_features_in_ n_features_in_: int

Number of features seen during :term:`fit`. Only defined if the
underlying regressor exposes such an attribute when fit.

.. versionadded:: 0.24
int 19
regressor_ regressor_: object

Fitted regressor.
Ridge Ridge(alpha=1e-10)
transformer_ transformer_: object

Transformer used in :meth:`fit` and :meth:`predict`.
FunctionTransformer FunctionTrans...validate=True)
Parameters
alpha alpha: float or array-like of shape (n_targets,), default=1.0

Constant that multiplies the L2 term, controlling regularization
strength. `alpha` must be a non-negative float i.e. in `[0, inf)`.

When `alpha = 0`, the objective is equivalent to ordinary least
squares, solved by the :class:`LinearRegression` object. For numerical
reasons, using `alpha = 0` with the `Ridge` object is not advised.
Instead, you should use the :class:`LinearRegression` object.

If an array is passed, penalties are assumed to be specific to the
targets. Hence they must correspond in number.

See :ref:`sphx_glr_auto_examples_linear_model_plot_ridge_coeffs.py`
for an illustration of the effect of alpha on the model coefficients.
1e-10
fit_intercept fit_intercept: bool, default=True

Whether to fit the intercept for this model. If set
to false, no intercept will be used in calculations
(i.e. ``X`` and ``y`` are expected to be centered).
True
copy_X copy_X: bool, default=True

If True, X will be copied; else, it may be overwritten.
True
max_iter max_iter: int, default=None

Maximum number of iterations for conjugate gradient solver.
For 'sparse_cg' and 'lsqr' solvers, the default value is determined
by scipy.sparse.linalg. For 'sag' solver, the default value is 1000.
For 'lbfgs' solver, the default value is 15000.
None
tol tol: float, default=1e-4

The precision of the solution (`coef_`) is determined by `tol` which
specifies a different convergence criterion for each solver:

- 'svd': `tol` has no impact.

- 'cholesky': `tol` has no impact.

- 'sparse_cg': norm of residuals smaller than `tol`.

- 'lsqr': `tol` is set as atol and btol of scipy.sparse.linalg.lsqr,
which control the norm of the residual vector in terms of the norms of
matrix and coefficients.

- 'sag' and 'saga': relative change of coef smaller than `tol`.

- 'lbfgs': maximum of the absolute (projected) gradient=max|residuals|
smaller than `tol`.

.. versionchanged:: 1.2
Default value changed from 1e-3 to 1e-4 for consistency with other linear
models.
0.0001
solver solver: {'auto', 'svd', 'cholesky', 'lsqr', 'sparse_cg', 'sag', 'saga', 'lbfgs'}, default='auto'

Solver to use in the computational routines:

- 'auto' chooses the solver automatically based on the type of data.

- 'svd' uses a Singular Value Decomposition of X to compute the Ridge
coefficients. It is the most stable solver, in particular more stable
for singular matrices than 'cholesky' at the cost of being slower.

- 'cholesky' uses the standard :func:`scipy.linalg.solve` function to
obtain a closed-form solution.

- 'sparse_cg' uses the conjugate gradient solver as found in
:func:`scipy.sparse.linalg.cg`. As an iterative algorithm, this solver is
more appropriate than 'cholesky' for large-scale data
(possibility to set `tol` and `max_iter`).

- 'lsqr' uses the dedicated regularized least-squares routine
:func:`scipy.sparse.linalg.lsqr`. It is the fastest and uses an iterative
procedure.

- 'sag' uses a Stochastic Average Gradient descent, and 'saga' uses
its improved, unbiased version named SAGA. Both methods also use an
iterative procedure, and are often faster than other solvers when
both n_samples and n_features are large. Note that 'sag' and
'saga' fast convergence is only guaranteed on features with
approximately the same scale. You can preprocess the data with a
scaler from :mod:`sklearn.preprocessing`.

- 'lbfgs' uses L-BFGS-B algorithm implemented in
:func:`scipy.optimize.minimize`. It can be used only when `positive`
is True.

All solvers except 'svd' support both dense and sparse data. However, only
'lsqr', 'sag', 'sparse_cg', and 'lbfgs' support sparse input when
`fit_intercept` is True.

.. versionadded:: 0.17
Stochastic Average Gradient descent solver.
.. versionadded:: 0.19
SAGA solver.
'auto'
positive positive: bool, default=False

When set to ``True``, forces the coefficients to be positive.
Only 'lbfgs' solver is supported in this case.
False
random_state random_state: int, RandomState instance, default=None

Used when ``solver`` == 'sag' or 'saga' to shuffle the data.
See :term:`Glossary <random_state>` for details.

.. versionadded:: 0.17
`random_state` to support Stochastic Average Gradient.
None
Fitted attributes
Name Type Value
coef_ coef_: ndarray of shape (n_features,) or (n_targets, n_features)

Weight vector(s).
ndarray[float64](19,) [-0.01,-0.01, 0.02,..., 0.05, 0.04,-0.03]
intercept_ intercept_: float or ndarray of shape (n_targets,)

Independent term in decision function. Set to 0.0 if
``fit_intercept = False``.
float64 0.7665
n_features_in_ n_features_in_: int

Number of features seen during :term:`fit`.

.. versionadded:: 0.24
int 19
n_iter_ n_iter_: None or ndarray of shape (n_targets,)

Actual number of iterations for each target. Available only for
'sag' and 'lsqr' solvers. Other solvers will return None.

.. versionadded:: 0.17
NoneType None
solver_ solver_: str

The solver that was used at fit time by the computational
routines.

.. versionadded:: 1.5
str 'ch...ky'


Then we check the performance of the computed model by plotting its predictions against the actual values on the test set, and by computing the median absolute error.

from sklearn.metrics import PredictionErrorDisplay, median_absolute_error

mae_train = median_absolute_error(y_train, model.predict(X_train))
y_pred = model.predict(X_test)
mae_test = median_absolute_error(y_test, y_pred)
scores = {
    "MedAE on training set": f"{mae_train:.2f} $/hour",
    "MedAE on testing set": f"{mae_test:.2f} $/hour",
}
_, ax = plt.subplots(figsize=(5, 5))
display = PredictionErrorDisplay.from_predictions(
    y_test, y_pred, kind="actual_vs_predicted", ax=ax, scatter_kwargs={"alpha": 0.5}
)
ax.set_title("Ridge model, small regularization")
for name, score in scores.items():
    ax.plot([], [], " ", label=f"{name}: {score}")
ax.legend(loc="upper left")
plt.tight_layout()
Ridge model, small regularization

The model learnt is far from being a good model making accurate predictions: this is obvious when looking at the plot above, where good predictions should lie on the black dashed line.

In the following section, we will interpret the coefficients of the model. While we do so, we should keep in mind that any conclusion we draw is about the model that we build, rather than about the true (real-world) generative process of the data.

Interpreting coefficients: scale matters

First of all, we can take a look to the values of the coefficients of the regressor we have fitted.

feature_names = model[:-1].get_feature_names_out()

coefs = pd.DataFrame(
    model[-1].regressor_.coef_,
    columns=["Coefficients"],
    index=feature_names,
)

coefs
Coefficients
RACE_Hispanic -0.013561
RACE_Other -0.009117
RACE_White 0.022552
OCCUPATION_Clerical 0.000046
OCCUPATION_Management 0.090529
OCCUPATION_Other -0.025101
OCCUPATION_Professional 0.071964
OCCUPATION_Sales -0.046635
OCCUPATION_Service -0.091052
SECTOR_Construction -0.000187
SECTOR_Manufacturing 0.031266
SECTOR_Other -0.031015
MARR_Unmarried -0.032405
UNION_not_member -0.117154
SEX_male 0.090808
SOUTH_yes -0.033823
EDUCATION 0.054699
EXPERIENCE 0.035005
AGE -0.030867


The AGE coefficient is expressed in “dollars/hour per living years” while the EDUCATION one is expressed in “dollars/hour per years of education”. This representation of the coefficients has the benefit of making clear the practical predictions of the model: an increase of \(1\) year in AGE means a decrease of \(0.030867\) dollars/hour, while an increase of \(1\) year in EDUCATION means an increase of \(0.054699\) dollars/hour. On the other hand, categorical variables (as UNION or SEX) are adimensional numbers taking either the value 0 or 1. Their coefficients are expressed in dollars/hour. Then, we cannot compare the magnitude of different coefficients since the features have different natural scales, and hence value ranges, because of their different unit of measure. This is more visible if we plot the coefficients.

coefs.plot.barh(figsize=(9, 7))
plt.title("Ridge model, small regularization")
plt.axvline(x=0, color=".5")
plt.xlabel("Raw coefficient values")
plt.subplots_adjust(left=0.3)
Ridge model, small regularization

Indeed, from the plot above the most important factor in determining WAGE appears to be the variable UNION, even if our intuition might tell us that variables like EXPERIENCE should have more impact.

Looking at the coefficient plot to gauge feature importance can be misleading as some of them vary on a small scale, while others, like AGE, varies a lot more, several decades.

This is visible if we compare the standard deviations of different features.

X_train_preprocessed = pd.DataFrame(
    model[:-1].transform(X_train), columns=feature_names
)

X_train_preprocessed.std(axis=0).plot.barh(figsize=(9, 7))
plt.title("Feature ranges")
plt.xlabel("Std. dev. of feature values")
plt.subplots_adjust(left=0.3)
Feature ranges

Multiplying the coefficients by the standard deviation of the related feature would reduce all the coefficients to the same unit of measure. As we will see after this is equivalent to normalize numerical variables to their standard deviation, as \(y = \sum{coef_i \times X_i} = \sum{(coef_i \times std_i) \times (X_i / std_i)}\).

In that way, we emphasize that the greater the variance of a feature, the larger the weight of the corresponding coefficient on the output, all else being equal.

coefs = pd.DataFrame(
    model[-1].regressor_.coef_ * X_train_preprocessed.std(axis=0),
    columns=["Coefficient importance"],
    index=feature_names,
)
coefs.plot(kind="barh", figsize=(9, 7))
plt.xlabel("Coefficient values corrected by the feature's std. dev.")
plt.title("Ridge model, small regularization")
plt.axvline(x=0, color=".5")
plt.subplots_adjust(left=0.3)
Ridge model, small regularization

Now that the coefficients have been scaled, we can safely compare them.

Note

Why does the plot above suggest that an increase in age leads to a decrease in wage? Why is the initial pairplot telling the opposite? This difference is the difference between marginal and conditional dependence.

The plot above tells us about dependencies between a specific feature and the target when all other features remain constant, i.e., conditional dependencies. An increase of the AGE will induce a decrease of the WAGE when all other features remain constant. On the contrary, an increase of the EXPERIENCE will induce an increase of the WAGE when all other features remain constant. Also, AGE, EXPERIENCE and EDUCATION are the three variables that most influence the model.

Interpreting coefficients: being cautious about causality

Linear models are a great tool for measuring statistical association, but we should be cautious when making statements about causality, after all correlation doesn’t always imply causation. This is particularly difficult in the social sciences because the variables we observe only function as proxies for the underlying causal process.

In our particular case we can think of the EDUCATION of an individual as a proxy for their professional aptitude, the real variable we’re interested in but can’t observe. We’d certainly like to think that staying in school for longer would increase technical competency, but it’s also quite possible that causality goes the other way too. That is, those who are technically competent tend to stay in school for longer.

An employer is unlikely to care which case it is (or if it’s a mix of both), as long as they remain convinced that a person with more EDUCATION is better suited for the job, they will be happy to pay out a higher WAGE.

This confounding of effects becomes problematic when thinking about some form of intervention e.g. government subsidies of university degrees or promotional material encouraging individuals to take up higher education. The usefulness of these measures could end up being overstated, especially if the degree of confounding is strong. Our model predicts a \(0.054699\) increase in hourly wage for each year of education. The actual causal effect might be lower because of this confounding.

Checking the variability of the coefficients

We can check the coefficient variability through cross-validation: it is a form of data perturbation (related to resampling).

If coefficients vary significantly when changing the input dataset their robustness is not guaranteed, and they should probably be interpreted with caution.

from sklearn.model_selection import RepeatedKFold, cross_validate

cv = RepeatedKFold(n_splits=5, n_repeats=5, random_state=0)
cv_model = cross_validate(
    model,
    X,
    y,
    cv=cv,
    return_estimator=True,
    n_jobs=2,
)

coefs = pd.DataFrame(
    [
        est[-1].regressor_.coef_ * est[:-1].transform(X.iloc[train_idx]).std(axis=0)
        for est, (train_idx, _) in zip(cv_model["estimator"], cv.split(X, y))
    ],
    columns=feature_names,
)
plt.figure(figsize=(9, 7))
sns.stripplot(data=coefs, orient="h", palette="dark:k", alpha=0.5)
sns.boxplot(data=coefs, orient="h", color="cyan", saturation=0.5, whis=10)
plt.axvline(x=0, color=".5")
plt.xlabel("Coefficient importance")
plt.title("Coefficient importance and its variability")
plt.suptitle("Ridge model, small regularization")
plt.subplots_adjust(left=0.3)
Ridge model, small regularization, Coefficient importance and its variability

The problem of correlated variables

The AGE and EXPERIENCE coefficients are affected by strong variability which might be due to the collinearity between the 2 features: as AGE and EXPERIENCE vary together in the data, their effect is difficult to tease apart.

To verify this interpretation we plot the variability of the AGE and EXPERIENCE coefficient.

plt.xlabel("Age coefficient")
plt.ylabel("Experience coefficient")
plt.grid(True)
plt.xlim(-0.4, 0.5)
plt.ylim(-0.4, 0.5)
plt.scatter(coefs["AGE"], coefs["EXPERIENCE"])
_ = plt.title("Co-variations of coefficients for AGE and EXPERIENCE across folds")
Co-variations of coefficients for AGE and EXPERIENCE across folds

Two regions are populated: when the EXPERIENCE coefficient is positive the AGE one is negative and vice-versa.

To go further we remove one of the two features, AGE, and check what is the impact on the model stability.

column_to_drop = ["AGE"]

cv_model = cross_validate(
    model,
    X.drop(columns=column_to_drop),
    y,
    cv=cv,
    return_estimator=True,
    n_jobs=2,
)

coefs = pd.DataFrame(
    [
        est[-1].regressor_.coef_
        * est[:-1].transform(X.drop(columns=column_to_drop).iloc[train_idx]).std(axis=0)
        for est, (train_idx, _) in zip(cv_model["estimator"], cv.split(X, y))
    ],
    columns=feature_names[:-1],
)
plt.figure(figsize=(9, 7))
sns.stripplot(data=coefs, orient="h", palette="dark:k", alpha=0.5)
sns.boxplot(data=coefs, orient="h", color="cyan", saturation=0.5)
plt.axvline(x=0, color=".5")
plt.title("Coefficient importance and its variability")
plt.xlabel("Coefficient importance")
plt.suptitle("Ridge model, small regularization, AGE dropped")
plt.subplots_adjust(left=0.3)
Ridge model, small regularization, AGE dropped, Coefficient importance and its variability

The estimation of the EXPERIENCE coefficient now shows a much reduced variability. EXPERIENCE remains important for all models trained during cross-validation.

Preprocessing numerical variables

As said above (see “The machine-learning pipeline”), we could also choose to scale numerical values before training the model. This can be useful when we apply a similar amount of regularization to all of them in the ridge. The preprocessor is redefined in order to subtract the mean and scale variables to unit variance.

from sklearn.preprocessing import StandardScaler

preprocessor = make_column_transformer(
    (OneHotEncoder(drop="if_binary"), categorical_columns),
    (StandardScaler(), numerical_columns),
)

The model will stay unchanged.

model = make_pipeline(
    preprocessor,
    TransformedTargetRegressor(
        regressor=Ridge(alpha=1e-10), func=np.log10, inverse_func=sp.special.exp10
    ),
)
model.fit(X_train, y_train)
Pipeline(steps=[('columntransformer',
                 ColumnTransformer(transformers=[('onehotencoder',
                                                  OneHotEncoder(drop='if_binary'),
                                                  ['RACE', 'OCCUPATION',
                                                   'SECTOR', 'MARR', 'UNION',
                                                   'SEX', 'SOUTH']),
                                                 ('standardscaler',
                                                  StandardScaler(),
                                                  ['EDUCATION', 'EXPERIENCE',
                                                   'AGE'])])),
                ('transformedtargetregressor',
                 TransformedTargetRegressor(func=<ufunc 'log10'>,
                                            inverse_func=<ufunc 'exp10'>,
                                            regressor=Ridge(alpha=1e-10)))])
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
Parameters
steps steps: list of tuples

List of (name of step, estimator) tuples that are to be chained in
sequential order. To be compatible with the scikit-learn API, all steps
must define `fit`. All non-last steps must also define `transform`. See
:ref:`Combining Estimators <combining_estimators>` for more details.
[('columntransformer', ...), ('transformedtargetregressor', ...)]
transform_input transform_input: list of str, default=None

The names of the :term:`metadata` parameters that should be transformed by the
pipeline before passing it to the step consuming it.

This enables transforming some input arguments to ``fit`` (other than ``X``)
to be transformed by the steps of the pipeline up to the step which requires
them. Requirement is defined via :ref:`metadata routing <metadata_routing>`.
For instance, this can be used to pass a validation set through the pipeline.

You can only set this if metadata routing is enabled, which you
can enable using ``sklearn.set_config(enable_metadata_routing=True)``.

.. versionadded:: 1.6
None
memory memory: str or object with the joblib.Memory interface, default=None

Used to cache the fitted transformers of the pipeline. The last step
will never be cached, even if it is a transformer. By default, no
caching is performed. If a string is given, it is the path to the
caching directory. Enabling caching triggers a clone of the transformers
before fitting. Therefore, the transformer instance given to the
pipeline cannot be inspected directly. Use the attribute ``named_steps``
or ``steps`` to inspect estimators within the pipeline. Caching the
transformers is advantageous when fitting is time consuming. See
:ref:`sphx_glr_auto_examples_neighbors_plot_caching_nearest_neighbors.py`
for an example on how to enable caching.
None
verbose verbose: bool, default=False

If True, the time elapsed while fitting each step will be printed as it
is completed.
False
Fitted attributes
Name Type Value
feature_names_in_ feature_names_in_: ndarray of shape (`n_features_in_`,)

Names of features seen during :term:`fit`. Only defined if the
underlying estimator exposes such an attribute when fit.

.. versionadded:: 1.0
ndarray[object](10,) ['EDUCATION','SOUTH','SEX',...,'OCCUPATION','SECTOR','MARR']
n_features_in_ n_features_in_: int

Number of features seen during :term:`fit`. Only defined if the
underlying first estimator in `steps` exposes such an attribute
when fit.

.. versionadded:: 0.24
int 10
Parameters
transformers transformers: list of tuples

List of (name, transformer, columns) tuples specifying the
transformer objects to be applied to subsets of the data.

name : str
Like in Pipeline and FeatureUnion, this allows the transformer and
its parameters to be set using ``set_params`` and searched in grid
search.
transformer : {'drop', 'passthrough'} or estimator
Estimator must support :term:`fit` and :term:`transform`.
Special-cased strings 'drop' and 'passthrough' are accepted as
well, to indicate to drop the columns or to pass them through
untransformed, respectively.
columns : str, array-like of str, int, array-like of int, array-like of bool, slice or callable
Indexes the data on its second axis. Integers are interpreted as
positional columns, while strings can reference DataFrame columns
by name. A scalar string or int should be used where
``transformer`` expects X to be a 1d array-like (vector),
otherwise a 2d array will be passed to the transformer.
A callable is passed the input data `X` and can return any of the
above. To select multiple columns by name or dtype, you can use
:obj:`make_column_selector`.
[('onehotencoder', ...), ('standardscaler', ...)]
remainder remainder: {'drop', 'passthrough'} or estimator, default='drop'

By default, only the specified columns in `transformers` are
transformed and combined in the output, and the non-specified
columns are dropped. (default of ``'drop'``).
By specifying ``remainder='passthrough'``, all remaining columns that
were not specified in `transformers`, but present in the data passed
to `fit` will be automatically passed through. This subset of columns
is concatenated with the output of the transformers. For dataframes,
extra columns not seen during `fit` will be excluded from the output
of `transform`.
By setting ``remainder`` to be an estimator, the remaining
non-specified columns will use the ``remainder`` estimator. The
estimator must support :term:`fit` and :term:`transform`.
Note that using this feature requires that the DataFrame columns
input at :term:`fit` and :term:`transform` have identical order.
'drop'
sparse_threshold sparse_threshold: float, default=0.3

If the output of the different transformers contains sparse matrices,
these will be stacked as a sparse matrix if the overall density is
lower than this value. Use ``sparse_threshold=0`` to always return
dense. When the transformed output consists of all dense data, the
stacked result will be dense, and this keyword will be ignored.
0.3
n_jobs n_jobs: int, default=None

Number of jobs to run in parallel.
``None`` means 1 unless in a :obj:`joblib.parallel_backend` context.
``-1`` means using all processors. See :term:`Glossary <n_jobs>`
for more details.
None
transformer_weights transformer_weights: dict, default=None

Multiplicative weights for features per transformer. The output of the
transformer is multiplied by these weights. Keys are transformer names,
values the weights.
None
verbose verbose: bool, default=False

If True, the time elapsed while fitting each transformer will be
printed as it is completed.
False
verbose_feature_names_out verbose_feature_names_out: bool, str or Callable[[str, str], str], default=True

- If True, :meth:`ColumnTransformer.get_feature_names_out` will prefix
all feature names with the name of the transformer that generated that
feature. It is equivalent to setting
`verbose_feature_names_out="{transformer_name}__{feature_name}"`.
- If False, :meth:`ColumnTransformer.get_feature_names_out` will not
prefix any feature names and will error if feature names are not
unique.
- If ``Callable[[str, str], str]``,
:meth:`ColumnTransformer.get_feature_names_out` will rename all the features
using the name of the transformer. The first argument of the callable is the
transformer name and the second argument is the feature name. The returned
string will be the new feature name.
- If ``str``, it must be a string ready for formatting. The given string will
be formatted using two field names: ``transformer_name`` and ``feature_name``.
e.g. ``"{feature_name}__{transformer_name}"``. See :meth:`str.format` method
from the standard library for more info.

.. versionadded:: 1.0

.. versionchanged:: 1.6
`verbose_feature_names_out` can be a callable or a string to be formatted.
True
Fitted attributes
Name Type Value
feature_names_in_ feature_names_in_: ndarray of shape (`n_features_in_`,)

Names of features seen during :term:`fit`. Defined only when `X`
has feature names that are all strings.

.. versionadded:: 1.0
ndarray[object](10,) ['EDUCATION','SOUTH','SEX',...,'OCCUPATION','SECTOR','MARR']
n_features_in_ n_features_in_: int

Number of features seen during :term:`fit`. Only defined if the
underlying transformers expose such an attribute when fit.

.. versionadded:: 0.24
int 10
named_transformers_ named_transformers_: :class:`~sklearn.utils.Bunch`

Read-only attribute to access any transformer by given name.
Keys are transformer names and values are the fitted transformer
objects.
Bunch {'onehotencod...ndardScaler()}
output_indices_ output_indices_: dict

A dictionary from each transformer name to a slice, where the slice
corresponds to indices in the transformed output. This is useful to
inspect which transformer is responsible for which transformed
feature(s).

.. versionadded:: 1.0
dict {'on...er': slice(0, 16, None), 're...er': slice(0, 0, None), 'st...er': slice(16, 19, None)}
sparse_output_ sparse_output_: bool

Boolean flag indicating whether the output of ``transform`` is a
sparse matrix or a dense numpy array, which depends on the output
of the individual transformers and the `sparse_threshold` keyword.
bool False
transformers_ transformers_: list

The collection of fitted transformers as tuples of (name,
fitted_transformer, column). `fitted_transformer` can be an estimator,
or `'drop'`; `'passthrough'` is replaced with an equivalent
:class:`~sklearn.preprocessing.FunctionTransformer`. In case there were
no columns selected, this will be the unfitted transformer. If there
are remaining columns, the final element is a tuple of the form:
('remainder', transformer, remaining_columns) corresponding to the
``remainder`` parameter. If there are remaining columns, then
``len(transformers_)==len(transformers)+1``, otherwise
``len(transformers_)==len(transformers)``.

.. versionadded:: 1.7
The format of the remaining columns now attempts to match that of the other
transformers: if all columns were provided as column names (`str`), the
remaining columns are stored as column names; if all columns were provided
as mask arrays (`bool`), so are the remaining columns; in all other cases
the remaining columns are stored as indices (`int`).
list [('on...er', OneHotEncoder...p='if_binary'), ['RACE', 'OC...ON', 'SECTOR', 'MARR', ...]), ('st...er', StandardScaler(), ['ED...ON', 'EX...CE', 'AGE'])]
['RACE', 'OCCUPATION', 'SECTOR', 'MARR', 'UNION', 'SEX', 'SOUTH']
Parameters
drop drop: {'first', 'if_binary'} or an array-like of shape (n_features,), default=None

Specifies a methodology to use to drop one of the categories per
feature. This is useful in situations where perfectly collinear
features cause problems, such as when feeding the resulting data
into an unregularized linear regression model.

However, dropping one category breaks the symmetry of the original
representation and can therefore induce a bias in downstream models,
for instance for penalized linear classification or regression models.

- None : retain all features (the default).
- 'first' : drop the first category in each feature. If only one
category is present, the feature will be dropped entirely.
- 'if_binary' : drop the first category in each feature with two
categories. Features with 1 or more than 2 categories are
left intact.
- array : ``drop[i]`` is the category in feature ``X[:, i]`` that
should be dropped.

When `max_categories` or `min_frequency` is configured to group
infrequent categories, the dropping behavior is handled after the
grouping.

.. versionadded:: 0.21
The parameter `drop` was added in 0.21.

.. versionchanged:: 0.23
The option `drop='if_binary'` was added in 0.23.

.. versionchanged:: 1.1
Support for dropping infrequent categories.
'if_binary'
categories categories: 'auto' or a list of array-like, default='auto'

Categories (unique values) per feature:

- 'auto' : Determine categories automatically from the training data.
- list : ``categories[i]`` holds the categories expected in the ith
column. The passed categories should not mix strings and numeric
values within a single feature, and should be sorted in case of
numeric values.

The used categories can be found in the ``categories_`` attribute.

.. versionadded:: 0.20
'auto'
sparse_output sparse_output: bool, default=True

When ``True``, it returns a SciPy sparse matrix/array
in "Compressed Sparse Row" (CSR) format.

.. versionadded:: 1.2
`sparse` was renamed to `sparse_output`
True
dtype dtype: number type, default=np.float64

Desired dtype of output.
<class 'numpy.float64'>
handle_unknown handle_unknown: {'error', 'ignore', 'infrequent_if_exist', 'warn'}, default='error'

Specifies the way unknown categories are handled during :meth:`transform`.

- 'error' : Raise an error if an unknown category is present during transform.
- 'ignore' : When an unknown category is encountered during
transform, the resulting one-hot encoded columns for this feature
will be all zeros. In the inverse transform, an unknown category
will be denoted as None.
- 'infrequent_if_exist' : When an unknown category is encountered
during transform, the resulting one-hot encoded columns for this
feature will map to the infrequent category if it exists. The
infrequent category will be mapped to the last position in the
encoding. During inverse transform, an unknown category will be
mapped to the category denoted `'infrequent'` if it exists. If the
`'infrequent'` category does not exist, then :meth:`transform` and
:meth:`inverse_transform` will handle an unknown category as with
`handle_unknown='ignore'`. Infrequent categories exist based on
`min_frequency` and `max_categories`. Read more in the
:ref:`User Guide <encoder_infrequent_categories>`.
- 'warn' : When an unknown category is encountered during transform
a warning is issued, and the encoding then proceeds as described for
`handle_unknown="infrequent_if_exist"`.

.. versionchanged:: 1.1
`'infrequent_if_exist'` was added to automatically handle unknown
categories and infrequent categories.

.. versionadded:: 1.6
The option `"warn"` was added in 1.6.
'error'
min_frequency min_frequency: int or float, default=None

Specifies the minimum frequency below which a category will be
considered infrequent.

- If `int`, categories with a smaller cardinality will be considered
infrequent.

- If `float`, categories with a smaller cardinality than
`min_frequency * n_samples` will be considered infrequent.

.. versionadded:: 1.1
Read more in the :ref:`User Guide <encoder_infrequent_categories>`.
None
max_categories max_categories: int, default=None

Specifies an upper limit to the number of output features for each input
feature when considering infrequent categories. If there are infrequent
categories, `max_categories` includes the category representing the
infrequent categories along with the frequent categories. If `None`,
there is no limit to the number of output features.

.. versionadded:: 1.1
Read more in the :ref:`User Guide <encoder_infrequent_categories>`.
None
feature_name_combiner feature_name_combiner: "concat" or callable, default="concat"

Callable with signature `def callable(input_feature, category)` that returns a
string. This is used to create feature names to be returned by
:meth:`get_feature_names_out`.

`"concat"` concatenates encoded feature name and category with
`feature + "_" + str(category)`.E.g. feature X with values 1, 6, 7 create
feature names `X_1, X_6, X_7`.

.. versionadded:: 1.3
'concat'
Fitted attributes
Name Type Value
categories_ categories_: list of arrays

The categories of each feature determined during fitting
(in order of the features in X and corresponding with the output
of ``transform``). This includes the category specified in ``drop``
(if any).
list [array(['Hispa... dtype=object), array(['Cleri... dtype=object), array(['Const... dtype=object), array(['Marri... dtype=object), ...]
drop_idx_ drop_idx_: array of shape (n_features,)

- ``drop_idx_[i]`` is the index in ``categories_[i]`` of the category
to be dropped for each feature.
- ``drop_idx_[i] = None`` if no category is to be dropped from the
feature with index ``i``, e.g. when `drop='if_binary'` and the
feature isn't binary.
- ``drop_idx_ = None`` if all the transformed features will be
retained.

If infrequent categories are enabled by setting `min_frequency` or
`max_categories` to a non-default value and `drop_idx[i]` corresponds
to an infrequent category, then the entire infrequent category is
dropped.

.. versionchanged:: 0.23
Added the possibility to contain `None` values.
ndarray[object](7,) [None,None,None,...,0,0,0]
feature_names_in_ feature_names_in_: ndarray of shape (`n_features_in_`,)

Names of features seen during :term:`fit`. Defined only when `X`
has feature names that are all strings.

.. versionadded:: 1.0
ndarray[object](7,) ['RACE','OCCUPATION','SECTOR',...,'UNION','SEX','SOUTH']
n_features_in_ n_features_in_: int

Number of features seen during :term:`fit`.

.. versionadded:: 1.0
int 7
16 features
RACE_Hispanic
RACE_Other
RACE_White
OCCUPATION_Clerical
OCCUPATION_Management
OCCUPATION_Other
OCCUPATION_Professional
OCCUPATION_Sales
OCCUPATION_Service
SECTOR_Construction
SECTOR_Manufacturing
SECTOR_Other
MARR_Unmarried
UNION_not_member
SEX_male
SOUTH_yes
['EDUCATION', 'EXPERIENCE', 'AGE']
Parameters
copy copy: bool, default=True

If False, try to avoid a copy and do inplace scaling instead.
This is not guaranteed to always work inplace; e.g. if the data is
not a NumPy array or scipy.sparse CSR matrix, a copy may still be
returned.
True
with_mean with_mean: bool, default=True

If True, center the data before scaling.
This does not work (and will raise an exception) when attempted on
sparse matrices, because centering them entails building a dense
matrix which in common use cases is likely to be too large to fit in
memory.
True
with_std with_std: bool, default=True

If True, scale the data to unit variance (or equivalently,
unit standard deviation).
True
Fitted attributes
Name Type Value
feature_names_in_ feature_names_in_: ndarray of shape (`n_features_in_`,)

Names of features seen during :term:`fit`. Defined only when `X`
has feature names that are all strings.

.. versionadded:: 1.0
ndarray[object](3,) ['EDUCATION','EXPERIENCE','AGE']
mean_ mean_: ndarray of shape (n_features,) or None

The mean value for each feature in the training set.
Equal to ``None`` when ``with_mean=False`` and ``with_std=False``.
ndarray[float64](3,) [13.04,18.19,37.23]
n_features_in_ n_features_in_: int

Number of features seen during :term:`fit`.

.. versionadded:: 0.24
int 3
n_samples_seen_ n_samples_seen_: int or ndarray of shape (n_features,)

The number of samples processed by the estimator for each feature.
If there are no missing samples, the ``n_samples_seen`` will be an
integer, otherwise it will be an array of dtype int. If
`sample_weights` are used it will be a float (if no missing data)
or an array of dtype float that sums the weights seen so far.
Will be reset on new calls to fit, but increments across
``partial_fit`` calls.
float64 400
scale_ scale_: ndarray of shape (n_features,) or None

Per feature relative scaling of the data to achieve zero mean and unit
variance. Generally this is calculated using `np.sqrt(var_)`. If a
variance is zero, we can't achieve unit variance, and the data is left
as-is, giving a scaling factor of 1. `scale_` is equal to `None`
when `with_std=False`.

.. versionadded:: 0.17
*scale_*
ndarray[float64](3,) [ 2.63,12.38,11.76]
var_ var_: ndarray of shape (n_features,) or None

The variance for each feature in the training set. Used to compute
`scale_`. Equal to ``None`` when ``with_mean=False`` and
``with_std=False``.
ndarray[float64](3,) [ 6.9 ,153.23,138.4 ]
3 features
EDUCATION
EXPERIENCE
AGE
19 features
onehotencoder__RACE_Hispanic
onehotencoder__RACE_Other
onehotencoder__RACE_White
onehotencoder__OCCUPATION_Clerical
onehotencoder__OCCUPATION_Management
onehotencoder__OCCUPATION_Other
onehotencoder__OCCUPATION_Professional
onehotencoder__OCCUPATION_Sales
onehotencoder__OCCUPATION_Service
onehotencoder__SECTOR_Construction
onehotencoder__SECTOR_Manufacturing
onehotencoder__SECTOR_Other
onehotencoder__MARR_Unmarried
onehotencoder__UNION_not_member
onehotencoder__SEX_male
onehotencoder__SOUTH_yes
standardscaler__EDUCATION
standardscaler__EXPERIENCE
standardscaler__AGE
Parameters
regressor regressor: object, default=None

Regressor object such as derived from
:class:`~sklearn.base.RegressorMixin`. This regressor will
automatically be cloned each time prior to fitting. If `regressor is
None`, :class:`~sklearn.linear_model.LinearRegression` is created and used.
Ridge(alpha=1e-10)
func func: function, default=None

Function to apply to `y` before passing to :meth:`fit`. Cannot be set
at the same time as `transformer`. If `func is None`, the function used will be
the identity function. If `func` is set, `inverse_func` also needs to be
provided. The function needs to return a 2-dimensional array.
<ufunc 'log10'>
inverse_func inverse_func: function, default=None

Function to apply to the prediction of the regressor. Cannot be set at
the same time as `transformer`. The inverse function is used to return
predictions to the same space of the original training labels. If
`inverse_func` is set, `func` also needs to be provided. The inverse
function needs to return a 2-dimensional array.
<ufunc 'exp10'>
transformer transformer: object, default=None

Estimator object such as derived from
:class:`~sklearn.base.TransformerMixin`. Cannot be set at the same time
as `func` and `inverse_func`. If `transformer is None` as well as
`func` and `inverse_func`, the transformer will be an identity
transformer. Note that the transformer will be cloned during fitting.
Also, the transformer is restricting `y` to be a numpy array.
None
check_inverse check_inverse: bool, default=True

Whether to check that `transform` followed by `inverse_transform`
or `func` followed by `inverse_func` leads to the original targets.
True
Fitted attributes
Name Type Value
n_features_in_ n_features_in_: int

Number of features seen during :term:`fit`. Only defined if the
underlying regressor exposes such an attribute when fit.

.. versionadded:: 0.24
int 19
regressor_ regressor_: object

Fitted regressor.
Ridge Ridge(alpha=1e-10)
transformer_ transformer_: object

Transformer used in :meth:`fit` and :meth:`predict`.
FunctionTransformer FunctionTrans...validate=True)
Parameters
alpha alpha: float or array-like of shape (n_targets,), default=1.0

Constant that multiplies the L2 term, controlling regularization
strength. `alpha` must be a non-negative float i.e. in `[0, inf)`.

When `alpha = 0`, the objective is equivalent to ordinary least
squares, solved by the :class:`LinearRegression` object. For numerical
reasons, using `alpha = 0` with the `Ridge` object is not advised.
Instead, you should use the :class:`LinearRegression` object.

If an array is passed, penalties are assumed to be specific to the
targets. Hence they must correspond in number.

See :ref:`sphx_glr_auto_examples_linear_model_plot_ridge_coeffs.py`
for an illustration of the effect of alpha on the model coefficients.
1e-10
fit_intercept fit_intercept: bool, default=True

Whether to fit the intercept for this model. If set
to false, no intercept will be used in calculations
(i.e. ``X`` and ``y`` are expected to be centered).
True
copy_X copy_X: bool, default=True

If True, X will be copied; else, it may be overwritten.
True
max_iter max_iter: int, default=None

Maximum number of iterations for conjugate gradient solver.
For 'sparse_cg' and 'lsqr' solvers, the default value is determined
by scipy.sparse.linalg. For 'sag' solver, the default value is 1000.
For 'lbfgs' solver, the default value is 15000.
None
tol tol: float, default=1e-4

The precision of the solution (`coef_`) is determined by `tol` which
specifies a different convergence criterion for each solver:

- 'svd': `tol` has no impact.

- 'cholesky': `tol` has no impact.

- 'sparse_cg': norm of residuals smaller than `tol`.

- 'lsqr': `tol` is set as atol and btol of scipy.sparse.linalg.lsqr,
which control the norm of the residual vector in terms of the norms of
matrix and coefficients.

- 'sag' and 'saga': relative change of coef smaller than `tol`.

- 'lbfgs': maximum of the absolute (projected) gradient=max|residuals|
smaller than `tol`.

.. versionchanged:: 1.2
Default value changed from 1e-3 to 1e-4 for consistency with other linear
models.
0.0001
solver solver: {'auto', 'svd', 'cholesky', 'lsqr', 'sparse_cg', 'sag', 'saga', 'lbfgs'}, default='auto'

Solver to use in the computational routines:

- 'auto' chooses the solver automatically based on the type of data.

- 'svd' uses a Singular Value Decomposition of X to compute the Ridge
coefficients. It is the most stable solver, in particular more stable
for singular matrices than 'cholesky' at the cost of being slower.

- 'cholesky' uses the standard :func:`scipy.linalg.solve` function to
obtain a closed-form solution.

- 'sparse_cg' uses the conjugate gradient solver as found in
:func:`scipy.sparse.linalg.cg`. As an iterative algorithm, this solver is
more appropriate than 'cholesky' for large-scale data
(possibility to set `tol` and `max_iter`).

- 'lsqr' uses the dedicated regularized least-squares routine
:func:`scipy.sparse.linalg.lsqr`. It is the fastest and uses an iterative
procedure.

- 'sag' uses a Stochastic Average Gradient descent, and 'saga' uses
its improved, unbiased version named SAGA. Both methods also use an
iterative procedure, and are often faster than other solvers when
both n_samples and n_features are large. Note that 'sag' and
'saga' fast convergence is only guaranteed on features with
approximately the same scale. You can preprocess the data with a
scaler from :mod:`sklearn.preprocessing`.

- 'lbfgs' uses L-BFGS-B algorithm implemented in
:func:`scipy.optimize.minimize`. It can be used only when `positive`
is True.

All solvers except 'svd' support both dense and sparse data. However, only
'lsqr', 'sag', 'sparse_cg', and 'lbfgs' support sparse input when
`fit_intercept` is True.

.. versionadded:: 0.17
Stochastic Average Gradient descent solver.
.. versionadded:: 0.19
SAGA solver.
'auto'
positive positive: bool, default=False

When set to ``True``, forces the coefficients to be positive.
Only 'lbfgs' solver is supported in this case.
False
random_state random_state: int, RandomState instance, default=None

Used when ``solver`` == 'sag' or 'saga' to shuffle the data.
See :term:`Glossary <random_state>` for details.

.. versionadded:: 0.17
`random_state` to support Stochastic Average Gradient.
None
Fitted attributes
Name Type Value
coef_ coef_: ndarray of shape (n_features,) or (n_targets, n_features)

Weight vector(s).
ndarray[float64](19,) [-0.01,-0.01, 0.02,..., 0.14, 0.43,-0.36]
intercept_ intercept_: float or ndarray of shape (n_targets,)

Independent term in decision function. Set to 0.0 if
``fit_intercept = False``.
float64 0.9677
n_features_in_ n_features_in_: int

Number of features seen during :term:`fit`.

.. versionadded:: 0.24
int 19
n_iter_ n_iter_: None or ndarray of shape (n_targets,)

Actual number of iterations for each target. Available only for
'sag' and 'lsqr' solvers. Other solvers will return None.

.. versionadded:: 0.17
NoneType None
solver_ solver_: str

The solver that was used at fit time by the computational
routines.

.. versionadded:: 1.5
str 'ch...ky'


Again, we check the performance of the computed model using the median absolute error.

mae_train = median_absolute_error(y_train, model.predict(X_train))
y_pred = model.predict(X_test)
mae_test = median_absolute_error(y_test, y_pred)
scores = {
    "MedAE on training set": f"{mae_train:.2f} $/hour",
    "MedAE on testing set": f"{mae_test:.2f} $/hour",
}

_, ax = plt.subplots(figsize=(5, 5))
display = PredictionErrorDisplay.from_predictions(
    y_test, y_pred, kind="actual_vs_predicted", ax=ax, scatter_kwargs={"alpha": 0.5}
)
ax.set_title("Ridge model, small regularization")
for name, score in scores.items():
    ax.plot([], [], " ", label=f"{name}: {score}")
ax.legend(loc="upper left")
plt.tight_layout()
Ridge model, small regularization

For the coefficient analysis, scaling is not needed this time because it was performed during the preprocessing step.

coefs = pd.DataFrame(
    model[-1].regressor_.coef_,
    columns=["Coefficients importance"],
    index=feature_names,
)
coefs.plot.barh(figsize=(9, 7))
plt.title("Ridge model, small regularization, normalized variables")
plt.xlabel("Raw coefficient values")
plt.axvline(x=0, color=".5")
plt.subplots_adjust(left=0.3)
Ridge model, small regularization, normalized variables

We now inspect the coefficients across several cross-validation folds.

cv_model = cross_validate(
    model,
    X,
    y,
    cv=cv,
    return_estimator=True,
    n_jobs=2,
)
coefs = pd.DataFrame(
    [est[-1].regressor_.coef_ for est in cv_model["estimator"]], columns=feature_names
)
plt.figure(figsize=(9, 7))
sns.stripplot(data=coefs, orient="h", palette="dark:k", alpha=0.5)
sns.boxplot(data=coefs, orient="h", color="cyan", saturation=0.5, whis=10)
plt.axvline(x=0, color=".5")
plt.title("Coefficient variability")
plt.subplots_adjust(left=0.3)
Coefficient variability

The result is quite similar to the non-normalized case.

Linear models with regularization

In machine-learning practice, ridge regression is more often used with non-negligible regularization.

Above, we limited this regularization to a very little amount. Regularization improves the conditioning of the problem and reduces the variance of the estimates. RidgeCV applies cross validation in order to determine which value of the regularization parameter (alpha) is best suited for prediction.

from sklearn.linear_model import RidgeCV

alphas = np.logspace(-10, 10, 21)  # alpha values to be chosen from by cross-validation
model = make_pipeline(
    preprocessor,
    TransformedTargetRegressor(
        regressor=RidgeCV(alphas=alphas),
        func=np.log10,
        inverse_func=sp.special.exp10,
    ),
)
model.fit(X_train, y_train)
Pipeline(steps=[('columntransformer',
                 ColumnTransformer(transformers=[('onehotencoder',
                                                  OneHotEncoder(drop='if_binary'),
                                                  ['RACE', 'OCCUPATION',
                                                   'SECTOR', 'MARR', 'UNION',
                                                   'SEX', 'SOUTH']),
                                                 ('standardscaler',
                                                  StandardScaler(),
                                                  ['EDUCATION', 'EXPERIENCE',
                                                   'AGE'])])),
                ('transformedtargetregressor',
                 TransformedTargetRegressor(func=<ufunc 'log10'>,
                                            inverse_func=<ufunc 'exp10'>,
                                            regressor=RidgeCV(alphas=array([1.e-10, 1.e-09, 1.e-08, 1.e-07, 1.e-06, 1.e-05, 1.e-04, 1.e-03,
       1.e-02, 1.e-01, 1.e+00, 1.e+01, 1.e+02, 1.e+03, 1.e+04, 1.e+05,
       1.e+06, 1.e+07, 1.e+08, 1.e+09, 1.e+10]))))])
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
Parameters
steps steps: list of tuples

List of (name of step, estimator) tuples that are to be chained in
sequential order. To be compatible with the scikit-learn API, all steps
must define `fit`. All non-last steps must also define `transform`. See
:ref:`Combining Estimators <combining_estimators>` for more details.
[('columntransformer', ...), ('transformedtargetregressor', ...)]
transform_input transform_input: list of str, default=None

The names of the :term:`metadata` parameters that should be transformed by the
pipeline before passing it to the step consuming it.

This enables transforming some input arguments to ``fit`` (other than ``X``)
to be transformed by the steps of the pipeline up to the step which requires
them. Requirement is defined via :ref:`metadata routing <metadata_routing>`.
For instance, this can be used to pass a validation set through the pipeline.

You can only set this if metadata routing is enabled, which you
can enable using ``sklearn.set_config(enable_metadata_routing=True)``.

.. versionadded:: 1.6
None
memory memory: str or object with the joblib.Memory interface, default=None

Used to cache the fitted transformers of the pipeline. The last step
will never be cached, even if it is a transformer. By default, no
caching is performed. If a string is given, it is the path to the
caching directory. Enabling caching triggers a clone of the transformers
before fitting. Therefore, the transformer instance given to the
pipeline cannot be inspected directly. Use the attribute ``named_steps``
or ``steps`` to inspect estimators within the pipeline. Caching the
transformers is advantageous when fitting is time consuming. See
:ref:`sphx_glr_auto_examples_neighbors_plot_caching_nearest_neighbors.py`
for an example on how to enable caching.
None
verbose verbose: bool, default=False

If True, the time elapsed while fitting each step will be printed as it
is completed.
False
Fitted attributes
Name Type Value
feature_names_in_ feature_names_in_: ndarray of shape (`n_features_in_`,)

Names of features seen during :term:`fit`. Only defined if the
underlying estimator exposes such an attribute when fit.

.. versionadded:: 1.0
ndarray[object](10,) ['EDUCATION','SOUTH','SEX',...,'OCCUPATION','SECTOR','MARR']
n_features_in_ n_features_in_: int

Number of features seen during :term:`fit`. Only defined if the
underlying first estimator in `steps` exposes such an attribute
when fit.

.. versionadded:: 0.24
int 10
Parameters
transformers transformers: list of tuples

List of (name, transformer, columns) tuples specifying the
transformer objects to be applied to subsets of the data.

name : str
Like in Pipeline and FeatureUnion, this allows the transformer and
its parameters to be set using ``set_params`` and searched in grid
search.
transformer : {'drop', 'passthrough'} or estimator
Estimator must support :term:`fit` and :term:`transform`.
Special-cased strings 'drop' and 'passthrough' are accepted as
well, to indicate to drop the columns or to pass them through
untransformed, respectively.
columns : str, array-like of str, int, array-like of int, array-like of bool, slice or callable
Indexes the data on its second axis. Integers are interpreted as
positional columns, while strings can reference DataFrame columns
by name. A scalar string or int should be used where
``transformer`` expects X to be a 1d array-like (vector),
otherwise a 2d array will be passed to the transformer.
A callable is passed the input data `X` and can return any of the
above. To select multiple columns by name or dtype, you can use
:obj:`make_column_selector`.
[('onehotencoder', ...), ('standardscaler', ...)]
remainder remainder: {'drop', 'passthrough'} or estimator, default='drop'

By default, only the specified columns in `transformers` are
transformed and combined in the output, and the non-specified
columns are dropped. (default of ``'drop'``).
By specifying ``remainder='passthrough'``, all remaining columns that
were not specified in `transformers`, but present in the data passed
to `fit` will be automatically passed through. This subset of columns
is concatenated with the output of the transformers. For dataframes,
extra columns not seen during `fit` will be excluded from the output
of `transform`.
By setting ``remainder`` to be an estimator, the remaining
non-specified columns will use the ``remainder`` estimator. The
estimator must support :term:`fit` and :term:`transform`.
Note that using this feature requires that the DataFrame columns
input at :term:`fit` and :term:`transform` have identical order.
'drop'
sparse_threshold sparse_threshold: float, default=0.3

If the output of the different transformers contains sparse matrices,
these will be stacked as a sparse matrix if the overall density is
lower than this value. Use ``sparse_threshold=0`` to always return
dense. When the transformed output consists of all dense data, the
stacked result will be dense, and this keyword will be ignored.
0.3
n_jobs n_jobs: int, default=None

Number of jobs to run in parallel.
``None`` means 1 unless in a :obj:`joblib.parallel_backend` context.
``-1`` means using all processors. See :term:`Glossary <n_jobs>`
for more details.
None
transformer_weights transformer_weights: dict, default=None

Multiplicative weights for features per transformer. The output of the
transformer is multiplied by these weights. Keys are transformer names,
values the weights.
None
verbose verbose: bool, default=False

If True, the time elapsed while fitting each transformer will be
printed as it is completed.
False
verbose_feature_names_out verbose_feature_names_out: bool, str or Callable[[str, str], str], default=True

- If True, :meth:`ColumnTransformer.get_feature_names_out` will prefix
all feature names with the name of the transformer that generated that
feature. It is equivalent to setting
`verbose_feature_names_out="{transformer_name}__{feature_name}"`.
- If False, :meth:`ColumnTransformer.get_feature_names_out` will not
prefix any feature names and will error if feature names are not
unique.
- If ``Callable[[str, str], str]``,
:meth:`ColumnTransformer.get_feature_names_out` will rename all the features
using the name of the transformer. The first argument of the callable is the
transformer name and the second argument is the feature name. The returned
string will be the new feature name.
- If ``str``, it must be a string ready for formatting. The given string will
be formatted using two field names: ``transformer_name`` and ``feature_name``.
e.g. ``"{feature_name}__{transformer_name}"``. See :meth:`str.format` method
from the standard library for more info.

.. versionadded:: 1.0

.. versionchanged:: 1.6
`verbose_feature_names_out` can be a callable or a string to be formatted.
True
Fitted attributes
Name Type Value
feature_names_in_ feature_names_in_: ndarray of shape (`n_features_in_`,)

Names of features seen during :term:`fit`. Defined only when `X`
has feature names that are all strings.

.. versionadded:: 1.0
ndarray[object](10,) ['EDUCATION','SOUTH','SEX',...,'OCCUPATION','SECTOR','MARR']
n_features_in_ n_features_in_: int

Number of features seen during :term:`fit`. Only defined if the
underlying transformers expose such an attribute when fit.

.. versionadded:: 0.24
int 10
named_transformers_ named_transformers_: :class:`~sklearn.utils.Bunch`

Read-only attribute to access any transformer by given name.
Keys are transformer names and values are the fitted transformer
objects.
Bunch {'onehotencod...ndardScaler()}
output_indices_ output_indices_: dict

A dictionary from each transformer name to a slice, where the slice
corresponds to indices in the transformed output. This is useful to
inspect which transformer is responsible for which transformed
feature(s).

.. versionadded:: 1.0
dict {'on...er': slice(0, 16, None), 're...er': slice(0, 0, None), 'st...er': slice(16, 19, None)}
sparse_output_ sparse_output_: bool

Boolean flag indicating whether the output of ``transform`` is a
sparse matrix or a dense numpy array, which depends on the output
of the individual transformers and the `sparse_threshold` keyword.
bool False
transformers_ transformers_: list

The collection of fitted transformers as tuples of (name,
fitted_transformer, column). `fitted_transformer` can be an estimator,
or `'drop'`; `'passthrough'` is replaced with an equivalent
:class:`~sklearn.preprocessing.FunctionTransformer`. In case there were
no columns selected, this will be the unfitted transformer. If there
are remaining columns, the final element is a tuple of the form:
('remainder', transformer, remaining_columns) corresponding to the
``remainder`` parameter. If there are remaining columns, then
``len(transformers_)==len(transformers)+1``, otherwise
``len(transformers_)==len(transformers)``.

.. versionadded:: 1.7
The format of the remaining columns now attempts to match that of the other
transformers: if all columns were provided as column names (`str`), the
remaining columns are stored as column names; if all columns were provided
as mask arrays (`bool`), so are the remaining columns; in all other cases
the remaining columns are stored as indices (`int`).
list [('on...er', OneHotEncoder...p='if_binary'), ['RACE', 'OC...ON', 'SECTOR', 'MARR', ...]), ('st...er', StandardScaler(), ['ED...ON', 'EX...CE', 'AGE'])]
['RACE', 'OCCUPATION', 'SECTOR', 'MARR', 'UNION', 'SEX', 'SOUTH']
Parameters
drop drop: {'first', 'if_binary'} or an array-like of shape (n_features,), default=None

Specifies a methodology to use to drop one of the categories per
feature. This is useful in situations where perfectly collinear
features cause problems, such as when feeding the resulting data
into an unregularized linear regression model.

However, dropping one category breaks the symmetry of the original
representation and can therefore induce a bias in downstream models,
for instance for penalized linear classification or regression models.

- None : retain all features (the default).
- 'first' : drop the first category in each feature. If only one
category is present, the feature will be dropped entirely.
- 'if_binary' : drop the first category in each feature with two
categories. Features with 1 or more than 2 categories are
left intact.
- array : ``drop[i]`` is the category in feature ``X[:, i]`` that
should be dropped.

When `max_categories` or `min_frequency` is configured to group
infrequent categories, the dropping behavior is handled after the
grouping.

.. versionadded:: 0.21
The parameter `drop` was added in 0.21.

.. versionchanged:: 0.23
The option `drop='if_binary'` was added in 0.23.

.. versionchanged:: 1.1
Support for dropping infrequent categories.
'if_binary'
categories categories: 'auto' or a list of array-like, default='auto'

Categories (unique values) per feature:

- 'auto' : Determine categories automatically from the training data.
- list : ``categories[i]`` holds the categories expected in the ith
column. The passed categories should not mix strings and numeric
values within a single feature, and should be sorted in case of
numeric values.

The used categories can be found in the ``categories_`` attribute.

.. versionadded:: 0.20
'auto'
sparse_output sparse_output: bool, default=True

When ``True``, it returns a SciPy sparse matrix/array
in "Compressed Sparse Row" (CSR) format.

.. versionadded:: 1.2
`sparse` was renamed to `sparse_output`
True
dtype dtype: number type, default=np.float64

Desired dtype of output.
<class 'numpy.float64'>
handle_unknown handle_unknown: {'error', 'ignore', 'infrequent_if_exist', 'warn'}, default='error'

Specifies the way unknown categories are handled during :meth:`transform`.

- 'error' : Raise an error if an unknown category is present during transform.
- 'ignore' : When an unknown category is encountered during
transform, the resulting one-hot encoded columns for this feature
will be all zeros. In the inverse transform, an unknown category
will be denoted as None.
- 'infrequent_if_exist' : When an unknown category is encountered
during transform, the resulting one-hot encoded columns for this
feature will map to the infrequent category if it exists. The
infrequent category will be mapped to the last position in the
encoding. During inverse transform, an unknown category will be
mapped to the category denoted `'infrequent'` if it exists. If the
`'infrequent'` category does not exist, then :meth:`transform` and
:meth:`inverse_transform` will handle an unknown category as with
`handle_unknown='ignore'`. Infrequent categories exist based on
`min_frequency` and `max_categories`. Read more in the
:ref:`User Guide <encoder_infrequent_categories>`.
- 'warn' : When an unknown category is encountered during transform
a warning is issued, and the encoding then proceeds as described for
`handle_unknown="infrequent_if_exist"`.

.. versionchanged:: 1.1
`'infrequent_if_exist'` was added to automatically handle unknown
categories and infrequent categories.

.. versionadded:: 1.6
The option `"warn"` was added in 1.6.
'error'
min_frequency min_frequency: int or float, default=None

Specifies the minimum frequency below which a category will be
considered infrequent.

- If `int`, categories with a smaller cardinality will be considered
infrequent.

- If `float`, categories with a smaller cardinality than
`min_frequency * n_samples` will be considered infrequent.

.. versionadded:: 1.1
Read more in the :ref:`User Guide <encoder_infrequent_categories>`.
None
max_categories max_categories: int, default=None

Specifies an upper limit to the number of output features for each input
feature when considering infrequent categories. If there are infrequent
categories, `max_categories` includes the category representing the
infrequent categories along with the frequent categories. If `None`,
there is no limit to the number of output features.

.. versionadded:: 1.1
Read more in the :ref:`User Guide <encoder_infrequent_categories>`.
None
feature_name_combiner feature_name_combiner: "concat" or callable, default="concat"

Callable with signature `def callable(input_feature, category)` that returns a
string. This is used to create feature names to be returned by
:meth:`get_feature_names_out`.

`"concat"` concatenates encoded feature name and category with
`feature + "_" + str(category)`.E.g. feature X with values 1, 6, 7 create
feature names `X_1, X_6, X_7`.

.. versionadded:: 1.3
'concat'
Fitted attributes
Name Type Value
categories_ categories_: list of arrays

The categories of each feature determined during fitting
(in order of the features in X and corresponding with the output
of ``transform``). This includes the category specified in ``drop``
(if any).
list [array(['Hispa... dtype=object), array(['Cleri... dtype=object), array(['Const... dtype=object), array(['Marri... dtype=object), ...]
drop_idx_ drop_idx_: array of shape (n_features,)

- ``drop_idx_[i]`` is the index in ``categories_[i]`` of the category
to be dropped for each feature.
- ``drop_idx_[i] = None`` if no category is to be dropped from the
feature with index ``i``, e.g. when `drop='if_binary'` and the
feature isn't binary.
- ``drop_idx_ = None`` if all the transformed features will be
retained.

If infrequent categories are enabled by setting `min_frequency` or
`max_categories` to a non-default value and `drop_idx[i]` corresponds
to an infrequent category, then the entire infrequent category is
dropped.

.. versionchanged:: 0.23
Added the possibility to contain `None` values.
ndarray[object](7,) [None,None,None,...,0,0,0]
feature_names_in_ feature_names_in_: ndarray of shape (`n_features_in_`,)

Names of features seen during :term:`fit`. Defined only when `X`
has feature names that are all strings.

.. versionadded:: 1.0
ndarray[object](7,) ['RACE','OCCUPATION','SECTOR',...,'UNION','SEX','SOUTH']
n_features_in_ n_features_in_: int

Number of features seen during :term:`fit`.

.. versionadded:: 1.0
int 7
16 features
RACE_Hispanic
RACE_Other
RACE_White
OCCUPATION_Clerical
OCCUPATION_Management
OCCUPATION_Other
OCCUPATION_Professional
OCCUPATION_Sales
OCCUPATION_Service
SECTOR_Construction
SECTOR_Manufacturing
SECTOR_Other
MARR_Unmarried
UNION_not_member
SEX_male
SOUTH_yes
['EDUCATION', 'EXPERIENCE', 'AGE']
Parameters
copy copy: bool, default=True

If False, try to avoid a copy and do inplace scaling instead.
This is not guaranteed to always work inplace; e.g. if the data is
not a NumPy array or scipy.sparse CSR matrix, a copy may still be
returned.
True
with_mean with_mean: bool, default=True

If True, center the data before scaling.
This does not work (and will raise an exception) when attempted on
sparse matrices, because centering them entails building a dense
matrix which in common use cases is likely to be too large to fit in
memory.
True
with_std with_std: bool, default=True

If True, scale the data to unit variance (or equivalently,
unit standard deviation).
True
Fitted attributes
Name Type Value
feature_names_in_ feature_names_in_: ndarray of shape (`n_features_in_`,)

Names of features seen during :term:`fit`. Defined only when `X`
has feature names that are all strings.

.. versionadded:: 1.0
ndarray[object](3,) ['EDUCATION','EXPERIENCE','AGE']
mean_ mean_: ndarray of shape (n_features,) or None

The mean value for each feature in the training set.
Equal to ``None`` when ``with_mean=False`` and ``with_std=False``.
ndarray[float64](3,) [13.04,18.19,37.23]
n_features_in_ n_features_in_: int

Number of features seen during :term:`fit`.

.. versionadded:: 0.24
int 3
n_samples_seen_ n_samples_seen_: int or ndarray of shape (n_features,)

The number of samples processed by the estimator for each feature.
If there are no missing samples, the ``n_samples_seen`` will be an
integer, otherwise it will be an array of dtype int. If
`sample_weights` are used it will be a float (if no missing data)
or an array of dtype float that sums the weights seen so far.
Will be reset on new calls to fit, but increments across
``partial_fit`` calls.
float64 400
scale_ scale_: ndarray of shape (n_features,) or None

Per feature relative scaling of the data to achieve zero mean and unit
variance. Generally this is calculated using `np.sqrt(var_)`. If a
variance is zero, we can't achieve unit variance, and the data is left
as-is, giving a scaling factor of 1. `scale_` is equal to `None`
when `with_std=False`.

.. versionadded:: 0.17
*scale_*
ndarray[float64](3,) [ 2.63,12.38,11.76]
var_ var_: ndarray of shape (n_features,) or None

The variance for each feature in the training set. Used to compute
`scale_`. Equal to ``None`` when ``with_mean=False`` and
``with_std=False``.
ndarray[float64](3,) [ 6.9 ,153.23,138.4 ]
3 features
EDUCATION
EXPERIENCE
AGE
19 features
onehotencoder__RACE_Hispanic
onehotencoder__RACE_Other
onehotencoder__RACE_White
onehotencoder__OCCUPATION_Clerical
onehotencoder__OCCUPATION_Management
onehotencoder__OCCUPATION_Other
onehotencoder__OCCUPATION_Professional
onehotencoder__OCCUPATION_Sales
onehotencoder__OCCUPATION_Service
onehotencoder__SECTOR_Construction
onehotencoder__SECTOR_Manufacturing
onehotencoder__SECTOR_Other
onehotencoder__MARR_Unmarried
onehotencoder__UNION_not_member
onehotencoder__SEX_male
onehotencoder__SOUTH_yes
standardscaler__EDUCATION
standardscaler__EXPERIENCE
standardscaler__AGE
Parameters
regressor regressor: object, default=None

Regressor object such as derived from
:class:`~sklearn.base.RegressorMixin`. This regressor will
automatically be cloned each time prior to fitting. If `regressor is
None`, :class:`~sklearn.linear_model.LinearRegression` is created and used.
RidgeCV(alpha...+09, 1.e+10]))
func func: function, default=None

Function to apply to `y` before passing to :meth:`fit`. Cannot be set
at the same time as `transformer`. If `func is None`, the function used will be
the identity function. If `func` is set, `inverse_func` also needs to be
provided. The function needs to return a 2-dimensional array.
<ufunc 'log10'>
inverse_func inverse_func: function, default=None

Function to apply to the prediction of the regressor. Cannot be set at
the same time as `transformer`. The inverse function is used to return
predictions to the same space of the original training labels. If
`inverse_func` is set, `func` also needs to be provided. The inverse
function needs to return a 2-dimensional array.
<ufunc 'exp10'>
transformer transformer: object, default=None

Estimator object such as derived from
:class:`~sklearn.base.TransformerMixin`. Cannot be set at the same time
as `func` and `inverse_func`. If `transformer is None` as well as
`func` and `inverse_func`, the transformer will be an identity
transformer. Note that the transformer will be cloned during fitting.
Also, the transformer is restricting `y` to be a numpy array.
None
check_inverse check_inverse: bool, default=True

Whether to check that `transform` followed by `inverse_transform`
or `func` followed by `inverse_func` leads to the original targets.
True
Fitted attributes
Name Type Value
n_features_in_ n_features_in_: int

Number of features seen during :term:`fit`. Only defined if the
underlying regressor exposes such an attribute when fit.

.. versionadded:: 0.24
int 19
regressor_ regressor_: object

Fitted regressor.
RidgeCV RidgeCV(alpha...+09, 1.e+10]))
transformer_ transformer_: object

Transformer used in :meth:`fit` and :meth:`predict`.
FunctionTransformer FunctionTrans...validate=True)
Parameters
alphas alphas: array-like of shape (n_alphas,), default=(0.1, 1.0, 10.0)

Array of alpha values to try.
Regularization strength; must be a positive float. Regularization
improves the conditioning of the problem and reduces the variance of
the estimates. Larger values specify stronger regularization.
Alpha corresponds to ``1 / (2C)`` in other linear models such as
:class:`~sklearn.linear_model.LogisticRegression` or
:class:`~sklearn.svm.LinearSVC`.
If using Leave-One-Out cross-validation, alphas must be strictly positive.

For an example on how regularization strength affects the model coefficients,
see :ref:`sphx_glr_auto_examples_linear_model_plot_ridge_coeffs.py`.
array([1.e-10...e+09, 1.e+10])
fit_intercept fit_intercept: bool, default=True

Whether to calculate the intercept for this model. If set
to false, no intercept will be used in calculations
(i.e. data is expected to be centered).
True
scoring scoring: str, callable, default=None

The scoring method to use for cross-validation. Options:

- str: see :ref:`scoring_string_names` for options.
- callable: a scorer callable object (e.g., function) with signature
``scorer(estimator, X, y)``. See :ref:`scoring_callable` for details.
- `None`: negative :ref:`mean squared error <mean_squared_error>` if cv is
None (i.e. when using leave-one-out cross-validation), or
:ref:`coefficient of determination <r2_score>` (:math:`R^2`) otherwise.
None
cv cv: int, cross-validation generator or an iterable, default=None

Determines the cross-validation splitting strategy.
Possible inputs for cv are:

- None, to use the efficient Leave-One-Out cross-validation
- integer, to specify the number of folds,
- :term:`CV splitter`,
- an iterable yielding (train, test) splits as arrays of indices.

For integer/None inputs, if ``y`` is binary or multiclass,
:class:`~sklearn.model_selection.StratifiedKFold` is used, else,
:class:`~sklearn.model_selection.KFold` is used.

Refer :ref:`User Guide <cross_validation>` for the various
cross-validation strategies that can be used here.
None
gcv_mode gcv_mode: {'auto', 'svd', 'eigen'}, default='auto'

Flag indicating which strategy to use when performing
Leave-One-Out Cross-Validation. Options are::

'auto' : same as 'eigen'
'svd' : use singular value decomposition of X when X is dense,
fallback to 'eigen' when X is sparse
'eigen' : use eigendecomposition of X X' when n_samples <= n_features
or X' X when n_features < n_samples

The 'auto' mode is the default and is intended to pick the cheaper
option depending on the shape and sparsity of the training data.
None
store_cv_results store_cv_results: bool, default=False

Flag indicating if the cross-validation values corresponding to
each alpha should be stored in the ``cv_results_`` attribute (see
below). This flag is only compatible with ``cv=None`` (i.e. using
Leave-One-Out Cross-Validation).

.. versionchanged:: 1.5
Parameter name changed from `store_cv_values` to `store_cv_results`.
False
alpha_per_target alpha_per_target: bool, default=False

Flag indicating whether to optimize the alpha value (picked from the
`alphas` parameter list) for each target separately (for multi-output
settings: multiple prediction targets). When set to `True`, after
fitting, the `alpha_` attribute will contain a value for each target.
When set to `False`, a single alpha is used for all targets.
This flag is only compatible with ``cv=None`` (i.e. using
Leave-One-Out Cross-Validation).

.. versionadded:: 0.24
False
Fitted attributes
Name Type Value
alpha_ alpha_: float or ndarray of shape (n_targets,)

Estimated regularization parameter, or, if ``alpha_per_target=True``,
the estimated regularization parameter for each target.
float 10
best_score_ best_score_: float or ndarray of shape (n_targets,)

Score of base estimator with best alpha, or, if
``alpha_per_target=True``, a score for each target.

.. versionadded:: 0.23
float64 -0.03611
coef_ coef_: ndarray of shape (n_features) or (n_targets, n_features)

Weight vector(s).
ndarray[float64](19,) [-0.01,-0.01, 0.02,..., 0.06, 0.02, 0.03]
intercept_ intercept_: float or ndarray of shape (n_targets,)

Independent term in decision function. Set to 0.0 if
``fit_intercept = False``.
float64 0.9579
n_features_in_ n_features_in_: int

Number of features seen during :term:`fit`.

.. versionadded:: 0.24
int 19


First we check which value of \(\alpha\) has been selected.

model[-1].regressor_.alpha_
10.0

Then we check the quality of the predictions.

mae_train = median_absolute_error(y_train, model.predict(X_train))
y_pred = model.predict(X_test)
mae_test = median_absolute_error(y_test, y_pred)
scores = {
    "MedAE on training set": f"{mae_train:.2f} $/hour",
    "MedAE on testing set": f"{mae_test:.2f} $/hour",
}

_, ax = plt.subplots(figsize=(5, 5))
display = PredictionErrorDisplay.from_predictions(
    y_test, y_pred, kind="actual_vs_predicted", ax=ax, scatter_kwargs={"alpha": 0.5}
)
ax.set_title("Ridge model, optimum regularization")
for name, score in scores.items():
    ax.plot([], [], " ", label=f"{name}: {score}")
ax.legend(loc="upper left")
plt.tight_layout()
Ridge model, optimum regularization

The ability to reproduce the data of the regularized model is similar to the one of the non-regularized model.

coefs = pd.DataFrame(
    model[-1].regressor_.coef_,
    columns=["Coefficients importance"],
    index=feature_names,
)
coefs.plot.barh(figsize=(9, 7))
plt.title("Ridge model, with regularization, normalized variables")
plt.xlabel("Raw coefficient values")
plt.axvline(x=0, color=".5")
plt.subplots_adjust(left=0.3)
Ridge model, with regularization, normalized variables

The coefficients are significantly different. AGE and EXPERIENCE coefficients are both positive but they now have less influence on the prediction.

The regularization reduces the influence of correlated variables on the model because the weight is shared between the two predictive variables, so neither alone would have strong weights.

On the other hand, the weights obtained with regularization are more stable (see the Ridge regression and classification User Guide section). This increased stability is visible from the plot, obtained from data perturbations, in a cross-validation. This plot can be compared with the previous one.

cv_model = cross_validate(
    model,
    X,
    y,
    cv=cv,
    return_estimator=True,
    n_jobs=2,
)
coefs = pd.DataFrame(
    [est[-1].regressor_.coef_ for est in cv_model["estimator"]], columns=feature_names
)
plt.xlabel("Age coefficient")
plt.ylabel("Experience coefficient")
plt.grid(True)
plt.xlim(-0.4, 0.5)
plt.ylim(-0.4, 0.5)
plt.scatter(coefs["AGE"], coefs["EXPERIENCE"])
_ = plt.title("Co-variations of coefficients for AGE and EXPERIENCE across folds")
Co-variations of coefficients for AGE and EXPERIENCE across folds

Linear models with sparse coefficients

Another possibility to take into account correlated variables in the dataset, is to estimate sparse coefficients. In some way we already did it manually when we dropped the AGE column in a previous ridge estimation.

Lasso models (see the Lasso User Guide section) estimates sparse coefficients. LassoCV applies cross validation in order to determine which value of the regularization parameter (alpha) is best suited for the model estimation.

from sklearn.linear_model import LassoCV

alphas = np.logspace(-10, 10, 21)  # alpha values to be chosen from by cross-validation
model = make_pipeline(
    preprocessor,
    TransformedTargetRegressor(
        regressor=LassoCV(alphas=alphas, max_iter=100_000),
        func=np.log10,
        inverse_func=sp.special.exp10,
    ),
)

_ = model.fit(X_train, y_train)

First we verify which value of \(\alpha\) has been selected.

model[-1].regressor_.alpha_
np.float64(0.001)

Then we check the quality of the predictions.

mae_train = median_absolute_error(y_train, model.predict(X_train))
y_pred = model.predict(X_test)
mae_test = median_absolute_error(y_test, y_pred)
scores = {
    "MedAE on training set": f"{mae_train:.2f} $/hour",
    "MedAE on testing set": f"{mae_test:.2f} $/hour",
}

_, ax = plt.subplots(figsize=(6, 6))
display = PredictionErrorDisplay.from_predictions(
    y_test, y_pred, kind="actual_vs_predicted", ax=ax, scatter_kwargs={"alpha": 0.5}
)
ax.set_title("Lasso model, optimum regularization")
for name, score in scores.items():
    ax.plot([], [], " ", label=f"{name}: {score}")
ax.legend(loc="upper left")
plt.tight_layout()
Lasso model, optimum regularization

For our dataset, again the model is not very predictive.

coefs = pd.DataFrame(
    model[-1].regressor_.coef_,
    columns=["Coefficients importance"],
    index=feature_names,
)
coefs.plot(kind="barh", figsize=(9, 7))
plt.title("Lasso model, optimum regularization, normalized variables")
plt.axvline(x=0, color=".5")
plt.subplots_adjust(left=0.3)
Lasso model, optimum regularization, normalized variables

A Lasso model identifies the correlation between AGE and EXPERIENCE and suppresses one of them for the sake of the prediction.

It is important to keep in mind that the coefficients that have been dropped may still be related to the outcome by themselves: the model chose to suppress them because they bring little or no additional information on top of the other features. Additionally, this selection is unstable for correlated features, and should be interpreted with caution.

Indeed, we can check the variability of the coefficients across folds.

cv_model = cross_validate(
    model,
    X,
    y,
    cv=cv,
    return_estimator=True,
    n_jobs=2,
)
coefs = pd.DataFrame(
    [est[-1].regressor_.coef_ for est in cv_model["estimator"]], columns=feature_names
)
plt.figure(figsize=(9, 7))
sns.stripplot(data=coefs, orient="h", palette="dark:k", alpha=0.5)
sns.boxplot(data=coefs, orient="h", color="cyan", saturation=0.5, whis=100)
plt.axvline(x=0, color=".5")
plt.title("Coefficient variability")
plt.subplots_adjust(left=0.3)
Coefficient variability

We observe that the AGE and EXPERIENCE coefficients are varying a lot depending of the fold.

Wrong causal interpretation

Policy makers might want to know the effect of education on wage to assess whether or not a certain policy designed to entice people to pursue more education would make economic sense. While Machine Learning models are great for measuring statistical associations, they are generally unable to infer causal effects.

It might be tempting to look at the coefficient of education on wage from our last model (or any model for that matter) and conclude that it captures the true effect of a change in the standardized education variable on wages.

Unfortunately there are likely unobserved confounding variables that either inflate or deflate that coefficient. A confounding variable is a variable that causes both EDUCATION and WAGE. One example of such variable is ability. Presumably, more able people are more likely to pursue education while at the same time being more likely to earn a higher hourly wage at any level of education. In this case, ability induces a positive Omitted Variable Bias (OVB) on the EDUCATION coefficient, thereby exaggerating the effect of education on wages.

See the Failure of Machine Learning to infer causal effects for a simulated case of ability OVB.

Lessons learned

  • Coefficients must be scaled to the same unit of measure to retrieve feature importance. Scaling them with the standard-deviation of the feature is a useful proxy.
  • Coefficients in multivariate linear models represent the dependency between a given feature and the target, conditional on the other features.
  • Correlated features induce instabilities in the coefficients of linear models and their effects cannot be well teased apart.
  • Different linear models respond differently to feature correlation and coefficients could significantly vary from one another.
  • Inspecting coefficients across the folds of a cross-validation loop gives an idea of their stability.
  • Interpreting causality is difficult when there are confounding effects. If the relationship between two variables is also affected by something unobserved, we should be careful when making conclusions about causality.

Total running time of the script: (0 minutes 16.545 seconds)

Launch binder
Launch JupyterLite

Download Jupyter notebook: plot_linear_model_coefficient_interpretation.ipynb

Download Python source code: plot_linear_model_coefficient_interpretation.py

Download zipped: plot_linear_model_coefficient_interpretation.zip

Related examples

Ridge coefficients as a function of the L2 Regularization

Failure of Machine Learning to infer causal effects

Plot Ridge coefficients as a function of the regularization

Effect of model regularization on training and test error

© 2007–2026 The scikit-learn developers
Licensed under the 3-clause BSD License.
https://scikit-learn.org/1.9/auto_examples/inspection/plot_linear_model_coefficient_interpretation.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API