Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
ce53c81
ENH Add verbose option and corresponding tests for Pipeline.
Mar 9, 2017
c7bbb94
ENH Add verbose option and corresponding tests for FeatureUnion.
Mar 10, 2017
d1353fb
ENH Add _pretty_print method to print from Pipeline properly.
Mar 10, 2017
a981bdc
Add a changelog entry about verbosity of Pipeline.
Mar 10, 2017
3bddca1
Fix bug in pipeline transformer list in-place assignment;
petrushev Sep 1, 2017
ba083b8
Extend `make_pipeline` to support the `verbose` kwarg as well
petrushev Sep 1, 2017
5857d5e
Fix `whats new` to reflect the Pipeline verbose change
petrushev Sep 1, 2017
72f4d0e
Remove `_BasePipeline` resulting from a bad rebase
petrushev Sep 1, 2017
1b7245e
Revert `FeatureUnion.transformer_list` to list instead of sequence
petrushev Sep 1, 2017
3938adc
Extend `FeatureUnion` to support the `verbose` kwarg as well
petrushev Sep 1, 2017
efb4aac
Minor tidy-up
petrushev Sep 3, 2017
2e5b6cc
Add `message_with_time` helper
petrushev Sep 5, 2017
f60eb7b
Merge branch 'master' into verbose-pipeline
jnothman Jan 9, 2018
45f346b
Rewrite verbose pipeline logic
jnothman Jan 9, 2018
3950fb0
Test logging utils
jnothman Jan 9, 2018
6807457
Param docstring
jnothman Jan 9, 2018
badf235
pytest<3.3 does return namedtuples
jnothman Jan 16, 2018
7de5ed8
Don't forcefully adjust timing on Windows as joblib does
jnothman Jan 16, 2018
5dce7c1
Need some __future__
jnothman Feb 11, 2018
b8c56d8
Rm unused import
jnothman Feb 13, 2018
57edbfa
Merge branch 'master' into verbose-pipeline
jnothman Apr 2, 2018
2caef80
Merge branch 'master' into verbose-pipeline
jnothman May 25, 2018
b92da44
Merge remote-tracking branch 'upstream/master' into verbose-pipeline
thomasjpfan Jun 27, 2018
6997c8f
ENH: Adapts ColumnTransformer to pipeline funcs
thomasjpfan Jun 27, 2018
fb8c123
ENH: Adds verbose to ColumnTransformer
thomasjpfan Jun 27, 2018
b3d5c8e
DOC: Fix doctest
thomasjpfan Jun 27, 2018
3f658bd
TST: Adds test for _ERR_MSG_1DCOLUMN
thomasjpfan Jun 28, 2018
7ee5d1f
Merge remote-tracking branch 'upstream/master' into verbose-pipeline
thomasjpfan Jul 12, 2018
cd7c5b0
Merge remote-tracking branch 'upstream/master' into verbose-pipeline
thomasjpfan Jul 19, 2018
02edbc4
RFC: Simplify transform
thomasjpfan Jul 19, 2018
8df40b6
RFC: Removes unrelated test
thomasjpfan Jul 19, 2018
a5bb214
Merge remote-tracking branch 'upstream/master' into verbose-pipeline
thomasjpfan Jul 26, 2018
5ab53c9
STY: Fix
thomasjpfan Jul 26, 2018
ed78138
MRG: Fix
thomasjpfan Jul 26, 2018
de8143e
Merge remote-tracking branch 'upstream/master' into verbose-pipeline
thomasjpfan Aug 22, 2018
ebae161
DOC: Adds docstring
thomasjpfan Aug 22, 2018
ab5a971
BUG: n_jobs defaults to None
thomasjpfan Aug 22, 2018
6d87aac
BUG: Defaults n_jobs to None
thomasjpfan Aug 22, 2018
34f850e
Merge remote-tracking branch 'upstream/master' into verbose-pipeline
thomasjpfan Oct 8, 2018
6e501b2
STY: Removes formatting diffs
thomasjpfan Oct 8, 2018
4924d1a
DOC: Rewords comment
thomasjpfan Oct 8, 2018
87629ec
STY: Removes formatting
thomasjpfan Oct 8, 2018
c9d3f37
BUG: Fixes
thomasjpfan Oct 8, 2018
5e1dc79
RFC: Clearly states the max length to be 70
thomasjpfan Oct 8, 2018
d50eebe
DOC: Fix
thomasjpfan Oct 8, 2018
b7d629d
BUG: Fix
thomasjpfan Oct 8, 2018
4e6bc32
RFC: Removes import
thomasjpfan Oct 18, 2018
f67da30
Merge remote-tracking branch 'upstream/master' into verbose-pipeline
thomasjpfan Oct 18, 2018
8550d72
ENH: Address comments
thomasjpfan Oct 24, 2018
dae9a41
ENH: Uses timeit
thomasjpfan Oct 24, 2018
a778069
BUG: Fix
thomasjpfan Oct 24, 2018
c9ab7b9
RFC: Does not handle wrapping
thomasjpfan Oct 24, 2018
dc917de
RFC: Removes partial
thomasjpfan Oct 24, 2018
795e1d5
DOC: Moves what_new message up
thomasjpfan Oct 24, 2018
1f240fa
RFC: Renames to message_clsname
thomasjpfan Oct 24, 2018
4edff5d
RFC: Convert back to original order
thomasjpfan Oct 24, 2018
b1f8331
RFC: Rename to return_transform
thomasjpfan Oct 24, 2018
e349e7a
RFC: Filters parameter_grid for test_verbose
thomasjpfan Oct 24, 2018
e9edcd2
RFC: Uses empty string as default
thomasjpfan Oct 24, 2018
03b499e
Merge remote-tracking branch 'upstream/master' into verbose-pipeline
thomasjpfan Jan 3, 2019
f7b2881
TST: Adds verbose to pprint tests
thomasjpfan Jan 4, 2019
52f876e
TST: Replace total with elapsed
thomasjpfan Jan 4, 2019
6804511
ENH: Uses total instead of elapsed
thomasjpfan Jan 4, 2019
bde21f5
TST: Fix
thomasjpfan Jan 4, 2019
9144ab9
Merge remote-tracking branch 'upstream/master' into verbose-pipeline
thomasjpfan Feb 25, 2019
10c82e2
Merge remote-tracking branch 'upstream/master' into verbose-pipeline
thomasjpfan Mar 12, 2019
8b17a8c
CLN Address comments
thomasjpfan Mar 12, 2019
495ecd7
CLN Address comments
thomasjpfan Mar 12, 2019
2cb6bba
BLD Trigger CI
thomasjpfan Mar 12, 2019
64fc3eb
BUG Fix doctest error
thomasjpfan Mar 13, 2019
234e6c6
Merge remote-tracking branch 'upstream/master' into verbose-pipeline
thomasjpfan Mar 13, 2019
a5e7a94
STY Flake8
thomasjpfan Mar 13, 2019
538b81d
Merge remote-tracking branch 'upstream/master' into verbose-pipeline
thomasjpfan Apr 10, 2019
3a42ba5
CLN Address comments
thomasjpfan Apr 12, 2019
0c6ed63
DOC Clears up verbose doc
thomasjpfan Apr 13, 2019
37e1f64
ENH Proper handling of None and passthrough
thomasjpfan Apr 14, 2019
94ed049
STY
thomasjpfan Apr 14, 2019
4759cb4
Merge remote-tracking branch 'upstream/master' into verbose-pipeline
thomasjpfan Apr 15, 2019
94e3bd5
CLN Uses the word processing
thomasjpfan Apr 18, 2019
7675454
CLN Address comments
thomasjpfan Apr 20, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 14 additions & 10 deletions doc/modules/compose.rst
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ is an estimator object::
>>> pipe # doctest: +NORMALIZE_WHITESPACE, +ELLIPSIS
Pipeline(memory=None,
steps=[('reduce_dim', PCA(copy=True,...)),
('clf', SVC(C=1.0,...))])
('clf', SVC(C=1.0,...))], verbose=False)

The utility function :func:`make_pipeline` is a shorthand
for constructing pipelines;
Expand All @@ -75,7 +75,8 @@ filling in the names automatically::
steps=[('binarizer', Binarizer(copy=True, threshold=0.0)),
('multinomialnb', MultinomialNB(alpha=1.0,
class_prior=None,
fit_prior=True))])
fit_prior=True))],
verbose=False)

Accessing steps
...............
Expand Down Expand Up @@ -106,9 +107,9 @@ permitted). This is convenient for performing only some of the transformations
(or their inverse):

>>> pipe[:1] # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
Pipeline(memory=None, steps=[('reduce_dim', PCA(copy=True, ...))])
Pipeline(memory=None, steps=[('reduce_dim', PCA(copy=True, ...))],...)
>>> pipe[-1:] # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
Pipeline(memory=None, steps=[('clf', SVC(C=1.0, ...))])
Pipeline(memory=None, steps=[('clf', SVC(C=1.0, ...))],...)

Nested parameters
.................
Expand All @@ -119,7 +120,8 @@ Parameters of the estimators in the pipeline can be accessed using the
>>> pipe.set_params(clf__C=10) # doctest: +NORMALIZE_WHITESPACE, +ELLIPSIS
Pipeline(memory=None,
steps=[('reduce_dim', PCA(copy=True, iterated_power='auto',...)),
('clf', SVC(C=10, cache_size=200, class_weight=None,...))])
('clf', SVC(C=10, cache_size=200, class_weight=None,...))],
verbose=False)

This is particularly important for doing grid searches::

Expand Down Expand Up @@ -202,7 +204,7 @@ object::
>>> pipe # doctest: +NORMALIZE_WHITESPACE, +ELLIPSIS
Pipeline(...,
steps=[('reduce_dim', PCA(copy=True,...)),
('clf', SVC(C=1.0,...))])
('clf', SVC(C=1.0,...))], verbose=False)
>>> # Clear the cache directory when you don't need it anymore
>>> rmtree(cachedir)

Expand All @@ -219,7 +221,8 @@ object::
>>> pipe.fit(digits.data, digits.target)
... # doctest: +NORMALIZE_WHITESPACE, +ELLIPSIS
Pipeline(memory=None,
steps=[('reduce_dim', PCA(...)), ('clf', SVC(...))])
steps=[('reduce_dim', PCA(...)), ('clf', SVC(...))],
verbose=False)
>>> # The pca instance can be inspected directly
>>> print(pca1.components_) # doctest: +NORMALIZE_WHITESPACE, +ELLIPSIS
[[-1.77484909e-19 ... 4.07058917e-18]]
Expand All @@ -241,7 +244,8 @@ object::
>>> cached_pipe.fit(digits.data, digits.target)
... # doctest: +NORMALIZE_WHITESPACE, +ELLIPSIS
Pipeline(memory=...,
steps=[('reduce_dim', PCA(...)), ('clf', SVC(...))])
steps=[('reduce_dim', PCA(...)), ('clf', SVC(...))],
verbose=False)
>>> print(cached_pipe.named_steps['reduce_dim'].components_)
... # doctest: +NORMALIZE_WHITESPACE, +ELLIPSIS
[[-1.77484909e-19 ... 4.07058917e-18]]
Expand Down Expand Up @@ -376,7 +380,7 @@ and ``value`` is an estimator object::
FeatureUnion(n_jobs=None,
transformer_list=[('linear_pca', PCA(copy=True,...)),
('kernel_pca', KernelPCA(alpha=1.0,...))],
transformer_weights=None)
transformer_weights=None, verbose=False)


Like pipelines, feature unions have a shorthand constructor called
Expand All @@ -391,7 +395,7 @@ and ignored by setting to ``'drop'``::
FeatureUnion(n_jobs=None,
transformer_list=[('linear_pca', PCA(copy=True,...)),
('kernel_pca', 'drop')],
transformer_weights=None)
transformer_weights=None, verbose=False)

.. topic:: Examples:

Expand Down
7 changes: 7 additions & 0 deletions doc/whats_new/v0.21.rst
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,13 @@ Support for Python 3.4 and below has been officially dropped.
therefore ``len(pipeline)`` returns the number of steps in the pipeline.
:issue:`13439` by :user:`Lakshya KD <LakshKD>`.

- |Feature| Added optional parameter ``verbose`` in :class:`pipeline.Pipeline`,
:class:`compose.ColumnTransformer` and :class:`pipeline.FeatureUnion`
and corresponding ``make_`` helpers for showing progress and timing of
each step. :issue:`11364` by :user:`Baze Petrushev <petrushev>`,
:user:`Karan Desai <karandesai-96>`, `Joel Nothman`_, and
:user:`Thomas Fan <thomasjpfan>`.

:mod:`sklearn.preprocessing`
............................

Expand Down
2 changes: 1 addition & 1 deletion examples/compose/plot_column_transformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ def transform(self, posts):

# Use a SVC classifier on the combined features
('svc', LinearSVC()),
])
], verbose=True)

# limit the list of categories to make running this example faster.
categories = ['alt.atheism', 'talk.religion.misc']
Expand Down
44 changes: 36 additions & 8 deletions sklearn/compose/_column_transformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@ class ColumnTransformer(_BaseComposition, TransformerMixin):
transformer is multiplied by these weights. Keys are transformer names,
values the weights.

verbose : boolean, optional(default=False)
If True, the time elapsed while fitting each transformer will be
printed as it is completed.

Attributes
----------
transformers_ : list
Expand Down Expand Up @@ -160,13 +164,19 @@ class ColumnTransformer(_BaseComposition, TransformerMixin):
"""
_required_parameters = ['transformers']

def __init__(self, transformers, remainder='drop', sparse_threshold=0.3,
n_jobs=None, transformer_weights=None):
def __init__(self,
transformers,
remainder='drop',
sparse_threshold=0.3,
n_jobs=None,
transformer_weights=None,
verbose=False):
self.transformers = transformers
self.remainder = remainder
self.sparse_threshold = sparse_threshold
self.n_jobs = n_jobs
self.transformer_weights = transformer_weights
self.verbose = verbose

@property
def _transformers(self):
Expand Down Expand Up @@ -377,6 +387,11 @@ def _validate_output(self, result):
"The output of the '{0}' transformer should be 2D (scipy "
"matrix, array, or pandas DataFrame).".format(name))

def _log_message(self, name, idx, total):
if not self.verbose:
return None
return '(%d of %d) Processing %s' % (idx, total, name)

def _fit_transform(self, X, y, func, fitted=False):
"""
Private function to fit and/or transform on demand.
Expand All @@ -385,12 +400,19 @@ def _fit_transform(self, X, y, func, fitted=False):
on the passed function.
``fitted=True`` ensures the fitted transformers are used.
"""
transformers = list(
self._iter(fitted=fitted, replace_strings=True))
try:
return Parallel(n_jobs=self.n_jobs)(
delayed(func)(clone(trans) if not fitted else trans,
_get_column(X, column), y, weight)
for _, trans, column, weight in self._iter(
fitted=fitted, replace_strings=True))
delayed(func)(
transformer=clone(trans) if not fitted else trans,
X=_get_column(X, column),
y=y,
weight=weight,
message_clsname='ColumnTransformer',
message=self._log_message(name, idx, len(transformers)))
for idx, (name, trans, column, weight) in enumerate(
self._iter(fitted=fitted, replace_strings=True), 1))
except ValueError as e:
if "Expected 2D array, got 1D array instead" in str(e):
raise ValueError(_ERR_MSG_1DCOLUMN)
Expand Down Expand Up @@ -775,6 +797,10 @@ def make_column_transformer(*transformers, **kwargs):
``-1`` means using all processors. See :term:`Glossary <n_jobs>`
for more details.

verbose : boolean, optional(default=False)
If True, the time elapsed while fitting each transformer will be
printed as it is completed.

Returns
-------
ct : ColumnTransformer
Expand All @@ -800,18 +826,20 @@ def make_column_transformer(*transformers, **kwargs):
['numerical_column']),
('onehotencoder',
OneHotEncoder(...),
['categorical_column'])])
['categorical_column'])], verbose=False)

"""
# transformer_weights keyword is not passed through because the user
# would need to know the automatically generated names of the transformers
n_jobs = kwargs.pop('n_jobs', None)
remainder = kwargs.pop('remainder', 'drop')
sparse_threshold = kwargs.pop('sparse_threshold', 0.3)
verbose = kwargs.pop('verbose', False)
if kwargs:
raise TypeError('Unknown keyword arguments: "{}"'
.format(list(kwargs.keys())[0]))
transformer_list = _get_transformer_list(transformers)
return ColumnTransformer(transformer_list, n_jobs=n_jobs,
remainder=remainder,
sparse_threshold=sparse_threshold)
sparse_threshold=sparse_threshold,
verbose=verbose)
63 changes: 59 additions & 4 deletions sklearn/compose/tests/test_column_transformer.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
Test the ColumnTransformer.
"""
import re

import numpy as np
from scipy import sparse
Expand Down Expand Up @@ -596,7 +597,8 @@ def test_column_transformer_get_set_params():
'trans2__with_mean': True,
'trans2__with_std': True,
'transformers': ct.transformers,
'transformer_weights': None}
'transformer_weights': None,
'verbose': False}

assert_dict_equal(ct.get_params(), exp)

Expand All @@ -613,7 +615,8 @@ def test_column_transformer_get_set_params():
'trans2__with_mean': True,
'trans2__with_std': True,
'transformers': ct.transformers,
'transformer_weights': None}
'transformer_weights': None,
'verbose': False}

assert_dict_equal(ct.get_params(), exp)

Expand Down Expand Up @@ -944,7 +947,8 @@ def test_column_transformer_get_set_params_with_remainder():
'trans1__with_mean': True,
'trans1__with_std': True,
'transformers': ct.transformers,
'transformer_weights': None}
'transformer_weights': None,
'verbose': False}

assert ct.get_params() == exp

Expand All @@ -960,7 +964,8 @@ def test_column_transformer_get_set_params_with_remainder():
'sparse_threshold': 0.3,
'trans1': 'passthrough',
'transformers': ct.transformers,
'transformer_weights': None}
'transformer_weights': None,
'verbose': False}

assert ct.get_params() == exp

Expand All @@ -981,6 +986,56 @@ def test_column_transformer_no_estimators():
assert ct.transformers_[-1][2] == [0, 1, 2]


@pytest.mark.parametrize(
['est', 'pattern'],
[(ColumnTransformer([('trans1', Trans(), [0]), ('trans2', Trans(), [1])],
remainder=DoubleTrans()),
(r'\[ColumnTransformer\].*\(1 of 3\) Processing trans1.* total=.*\n'
r'\[ColumnTransformer\].*\(2 of 3\) Processing trans2.* total=.*\n'
r'\[ColumnTransformer\].*\(3 of 3\) Processing remainder.* total=.*\n$'
)),
(ColumnTransformer([('trans1', Trans(), [0]), ('trans2', Trans(), [1])],
remainder='passthrough'),
(r'\[ColumnTransformer\].*\(1 of 3\) Processing trans1.* total=.*\n'
r'\[ColumnTransformer\].*\(2 of 3\) Processing trans2.* total=.*\n'
r'\[ColumnTransformer\].*\(3 of 3\) Processing remainder.* total=.*\n$'
)),
(ColumnTransformer([('trans1', Trans(), [0]), ('trans2', 'drop', [1])],
remainder='passthrough'),
(r'\[ColumnTransformer\].*\(1 of 2\) Processing trans1.* total=.*\n'
r'\[ColumnTransformer\].*\(2 of 2\) Processing remainder.* total=.*\n$'
)),
(ColumnTransformer([('trans1', Trans(), [0]),
('trans2', 'passthrough', [1])],
remainder='passthrough'),
(r'\[ColumnTransformer\].*\(1 of 3\) Processing trans1.* total=.*\n'
r'\[ColumnTransformer\].*\(2 of 3\) Processing trans2.* total=.*\n'
r'\[ColumnTransformer\].*\(3 of 3\) Processing remainder.* total=.*\n$'
)),
(ColumnTransformer([('trans1', Trans(), [0])], remainder='passthrough'),
(r'\[ColumnTransformer\].*\(1 of 2\) Processing trans1.* total=.*\n'
r'\[ColumnTransformer\].*\(2 of 2\) Processing remainder.* total=.*\n$'
)),
(ColumnTransformer([('trans1', Trans(), [0]), ('trans2', Trans(), [1])],
remainder='drop'),
(r'\[ColumnTransformer\].*\(1 of 2\) Processing trans1.* total=.*\n'
r'\[ColumnTransformer\].*\(2 of 2\) Processing trans2.* total=.*\n$')),
(ColumnTransformer([('trans1', Trans(), [0])], remainder='drop'),
(r'\[ColumnTransformer\].*\(1 of 1\) Processing trans1.* total=.*\n$'))])
@pytest.mark.parametrize('method', ['fit', 'fit_transform'])
def test_column_transformer_verbose(est, pattern, method, capsys):
X_array = np.array([[0, 1, 2], [2, 4, 6], [8, 6, 4]]).T

func = getattr(est, method)
est.set_params(verbose=False)
func(X_array)
assert not capsys.readouterr().out, 'Got output for verbose=False'

est.set_params(verbose=True)
func(X_array)
assert re.match(pattern, capsys.readouterr()[0])


def test_column_transformer_no_estimators_set_params():
ct = ColumnTransformer([]).set_params(n_jobs=2)
assert ct.n_jobs == 2
Expand Down
7 changes: 3 additions & 4 deletions sklearn/model_selection/_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@
import scipy.sparse as sp

from ..base import is_classifier, clone
from ..utils import indexable, check_random_state, safe_indexing
from ..utils import (indexable, check_random_state, safe_indexing,
_message_with_time)
from ..utils.validation import _is_arraylike, _num_samples
from ..utils.metaestimators import _safe_split
from ..utils._joblib import Parallel, delayed
from ..utils._joblib import logger
from ..metrics.scorer import check_scoring, _check_multimetric_scoring
from ..exceptions import FitFailedWarning
from ._split import check_cv
Expand Down Expand Up @@ -572,8 +572,7 @@ def _fit_and_score(estimator, X, y, scorer, train, test, verbose,

if verbose > 1:
total_time = score_time + fit_time
end_msg = "%s, total=%s" % (msg, logger.short_format_time(total_time))
print("[CV] %s %s" % ((64 - len(end_msg)) * '.', end_msg))
print(_message_with_time('CV', msg, total_time))

ret = [train_scores, test_scores] if return_train_score else [test_scores]

Expand Down
2 changes: 1 addition & 1 deletion sklearn/model_selection/tests/test_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ def fit(self, X, Y=None, sample_weight=None, class_prior=None,
raise ValueError('X cannot be d')
if sample_weight is not None:
assert sample_weight.shape[0] == X.shape[0], (
'MockClassifier extra fit_param '
'MockClassifier extra fit_param '
'sample_weight.shape[0] is {0}, should be {1}'
.format(sample_weight.shape[0], X.shape[0]))
if class_prior is not None:
Expand Down
Loading