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

Skip to content

Commit cbfcfe1

Browse files
cmarmothomasjpfan
authored andcommitted
ENH Add verbose option to VotingClassifier and VotingRegre… (scikit-learn#16069)
1 parent 31befed commit cbfcfe1

4 files changed

Lines changed: 65 additions & 9 deletions

File tree

doc/whats_new/v0.23.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,16 @@ Changelog
7171
`ValueError` for arguments `n_classes < 1` OR `length < 1`.
7272
:pr:`16006` by :user:`Rushabh Vasani <rushabh-v>`.
7373

74+
:mod:`sklearn.ensemble`
75+
.................................
76+
77+
- |API| Added boolean `verbose` flag to classes:
78+
:class:`ensemble.VotingClassifier` and :class:`ensemble.VotingRegressor`.
79+
:pr:`15991` by :user:`Sam Bail <spbail>`,
80+
:user:`Hanna Bruce MacDonald <hannahbrucemacdonald>`,
81+
:user:`Reshama Shaikh <reshamas>`, and
82+
:user:`Chiara Marmo <cmarmo>`.
83+
7484
:mod:`sklearn.feature_extraction`
7585
.................................
7686

sklearn/ensemble/_base.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,13 @@
1515
from ..base import is_classifier, is_regressor
1616
from ..base import BaseEstimator
1717
from ..base import MetaEstimatorMixin
18-
from ..utils import Bunch
18+
from ..utils import Bunch, _print_elapsed_time
1919
from ..utils import check_random_state
2020
from ..utils.metaestimators import _BaseComposition
2121

2222

23-
def _parallel_fit_estimator(estimator, X, y, sample_weight=None):
23+
def _parallel_fit_estimator(estimator, X, y, sample_weight=None,
24+
message_clsname=None, message=None):
2425
"""Private function used to fit an estimator within a job."""
2526
if sample_weight is not None:
2627
try:
@@ -33,7 +34,8 @@ def _parallel_fit_estimator(estimator, X, y, sample_weight=None):
3334
) from exc
3435
raise
3536
else:
36-
estimator.fit(X, y)
37+
with _print_elapsed_time(message_clsname, message):
38+
estimator.fit(X, y)
3739
return estimator
3840

3941

sklearn/ensemble/_voting.py

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ class _BaseVoting(TransformerMixin, _BaseHeterogeneousEnsemble):
3939
instead.
4040
"""
4141

42+
def _log_message(self, name, idx, total):
43+
if not self.verbose:
44+
return None
45+
return '(%d of %d) Processing %s' % (idx, total, name)
46+
4247
@property
4348
def _weights_not_none(self):
4449
"""Get the weights of not `None` estimators."""
@@ -63,9 +68,14 @@ def fit(self, X, y, sample_weight=None):
6368
% (len(self.weights), len(self.estimators)))
6469

6570
self.estimators_ = Parallel(n_jobs=self.n_jobs)(
66-
delayed(_parallel_fit_estimator)(clone(clf), X, y,
67-
sample_weight=sample_weight)
68-
for clf in clfs if clf not in (None, 'drop')
71+
delayed(_parallel_fit_estimator)(
72+
clone(clf), X, y,
73+
sample_weight=sample_weight,
74+
message_clsname='Voting',
75+
message=self._log_message(names[idx],
76+
idx + 1, len(clfs))
77+
)
78+
for idx, clf in enumerate(clfs) if clf not in (None, 'drop')
6979
)
7080

7181
self.named_estimators_ = Bunch()
@@ -122,6 +132,10 @@ class VotingClassifier(ClassifierMixin, _BaseVoting):
122132
flatten_transform=False, it returns
123133
(n_classifiers, n_samples, n_classes).
124134
135+
verbose : bool, default=False
136+
If True, the time elapsed while fitting will be printed as it
137+
is completed.
138+
125139
Attributes
126140
----------
127141
estimators_ : list of classifiers
@@ -176,13 +190,14 @@ class VotingClassifier(ClassifierMixin, _BaseVoting):
176190
(6, 6)
177191
"""
178192

179-
def __init__(self, estimators, voting='hard', weights=None, n_jobs=None,
180-
flatten_transform=True):
193+
def __init__(self, estimators, voting='hard', weights=None,
194+
n_jobs=None, flatten_transform=True, verbose=False):
181195
super().__init__(estimators=estimators)
182196
self.voting = voting
183197
self.weights = weights
184198
self.n_jobs = n_jobs
185199
self.flatten_transform = flatten_transform
200+
self.verbose = verbose
186201

187202
def fit(self, X, y, sample_weight=None):
188203
"""Fit the estimators.
@@ -346,6 +361,10 @@ class VotingRegressor(RegressorMixin, _BaseVoting):
346361
``-1`` means using all processors. See :term:`Glossary <n_jobs>`
347362
for more details.
348363
364+
verbose : bool, default=False
365+
If True, the time elapsed while fitting will be printed as it
366+
is completed.
367+
349368
Attributes
350369
----------
351370
estimators_ : list of regressors
@@ -376,10 +395,11 @@ class VotingRegressor(RegressorMixin, _BaseVoting):
376395
[ 3.3 5.7 11.8 19.7 28. 40.3]
377396
"""
378397

379-
def __init__(self, estimators, weights=None, n_jobs=None):
398+
def __init__(self, estimators, weights=None, n_jobs=None, verbose=False):
380399
super().__init__(estimators=estimators)
381400
self.weights = weights
382401
self.n_jobs = n_jobs
402+
self.verbose = verbose
383403

384404
def fit(self, X, y, sample_weight=None):
385405
"""Fit the estimators.

sklearn/ensemble/tests/test_voting.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Testing for the VotingClassifier and VotingRegressor"""
22

33
import pytest
4+
import re
45
import numpy as np
56

67
from sklearn.utils._testing import assert_almost_equal, assert_array_equal
@@ -513,6 +514,29 @@ def test_check_estimators_voting_estimator(estimator):
513514
check_no_attributes_set_in_init(estimator.__class__.__name__, estimator)
514515

515516

517+
@pytest.mark.parametrize(
518+
"estimator",
519+
[VotingRegressor(
520+
estimators=[('lr', LinearRegression()),
521+
('rf', RandomForestRegressor(random_state=123))],
522+
verbose=True),
523+
VotingClassifier(
524+
estimators=[('lr', LogisticRegression(random_state=123)),
525+
('rf', RandomForestClassifier(random_state=123))],
526+
verbose=True)]
527+
)
528+
def test_voting_verbose(estimator, capsys):
529+
530+
X = np.array([[-1.1, -1.5], [-1.2, -1.4], [-3.4, -2.2], [1.1, 1.2]])
531+
y = np.array([1, 1, 2, 2])
532+
533+
pattern = (r'\[Voting\].*\(1 of 2\) Processing lr, total=.*\n'
534+
r'\[Voting\].*\(2 of 2\) Processing rf, total=.*\n$')
535+
536+
estimator.fit(X, y)
537+
assert re.match(pattern, capsys.readouterr()[0])
538+
539+
516540
# TODO: Remove in 0.24 when None is removed in Voting*
517541
@pytest.mark.parametrize(
518542
"Voter, BaseEstimator",

0 commit comments

Comments
 (0)