|
12 | 12 | # License: BSD 3 clause |
13 | 13 |
|
14 | 14 | import numpy as np |
| 15 | +import warnings |
15 | 16 |
|
16 | 17 | from ..base import ClassifierMixin |
17 | 18 | from ..base import TransformerMixin |
@@ -61,6 +62,13 @@ class VotingClassifier(_BaseComposition, ClassifierMixin, TransformerMixin): |
61 | 62 | The number of jobs to run in parallel for ``fit``. |
62 | 63 | If -1, then the number of jobs is set to the number of cores. |
63 | 64 |
|
| 65 | + flatten_transform : bool, optional (default=None) |
| 66 | + Affects shape of transform output only when voting='soft' |
| 67 | + If voting='soft' and flatten_transform=True, transform method returns |
| 68 | + matrix with shape (n_samples, n_classifiers * n_classes). If |
| 69 | + flatten_transform=False, it returns |
| 70 | + (n_classifiers, n_samples, n_classes). |
| 71 | +
|
64 | 72 | Attributes |
65 | 73 | ---------- |
66 | 74 | estimators_ : list of classifiers |
@@ -94,18 +102,23 @@ class VotingClassifier(_BaseComposition, ClassifierMixin, TransformerMixin): |
94 | 102 | [1 1 1 2 2 2] |
95 | 103 | >>> eclf3 = VotingClassifier(estimators=[ |
96 | 104 | ... ('lr', clf1), ('rf', clf2), ('gnb', clf3)], |
97 | | - ... voting='soft', weights=[2,1,1]) |
| 105 | + ... voting='soft', weights=[2,1,1], |
| 106 | + ... flatten_transform=True) |
98 | 107 | >>> eclf3 = eclf3.fit(X, y) |
99 | 108 | >>> print(eclf3.predict(X)) |
100 | 109 | [1 1 1 2 2 2] |
| 110 | + >>> print(eclf3.transform(X).shape) |
| 111 | + (6, 6) |
101 | 112 | >>> |
102 | 113 | """ |
103 | 114 |
|
104 | | - def __init__(self, estimators, voting='hard', weights=None, n_jobs=1): |
| 115 | + def __init__(self, estimators, voting='hard', weights=None, n_jobs=1, |
| 116 | + flatten_transform=None): |
105 | 117 | self.estimators = estimators |
106 | 118 | self.voting = voting |
107 | 119 | self.weights = weights |
108 | 120 | self.n_jobs = n_jobs |
| 121 | + self.flatten_transform = flatten_transform |
109 | 122 |
|
110 | 123 | @property |
111 | 124 | def named_estimators(self): |
@@ -163,6 +176,7 @@ def fit(self, X, y, sample_weight=None): |
163 | 176 | if n_isnone == len(self.estimators): |
164 | 177 | raise ValueError('All estimators are None. At least one is ' |
165 | 178 | 'required to be a classifier!') |
| 179 | + |
166 | 180 | self.le_ = LabelEncoder().fit(y) |
167 | 181 | self.classes_ = self.le_.classes_ |
168 | 182 | self.estimators_ = [] |
@@ -256,16 +270,30 @@ def transform(self, X): |
256 | 270 |
|
257 | 271 | Returns |
258 | 272 | ------- |
259 | | - If `voting='soft'`: |
260 | | - array-like = [n_classifiers, n_samples, n_classes] |
| 273 | + If `voting='soft'` and `flatten_transform=True`: |
| 274 | + array-like = (n_classifiers, n_samples * n_classes) |
| 275 | + otherwise array-like = (n_classifiers, n_samples, n_classes) |
261 | 276 | Class probabilities calculated by each classifier. |
262 | 277 | If `voting='hard'`: |
263 | 278 | array-like = [n_samples, n_classifiers] |
264 | 279 | Class labels predicted by each classifier. |
265 | 280 | """ |
266 | 281 | check_is_fitted(self, 'estimators_') |
| 282 | + |
267 | 283 | if self.voting == 'soft': |
268 | | - return self._collect_probas(X) |
| 284 | + probas = self._collect_probas(X) |
| 285 | + if self.flatten_transform is None: |
| 286 | + warnings.warn("'flatten_transform' default value will be " |
| 287 | + "changed to True in 0.21." |
| 288 | + "To silence this warning you may" |
| 289 | + " explicitly set flatten_transform=False.", |
| 290 | + DeprecationWarning) |
| 291 | + return probas |
| 292 | + elif not self.flatten_transform: |
| 293 | + return probas |
| 294 | + else: |
| 295 | + return np.hstack(probas) |
| 296 | + |
269 | 297 | else: |
270 | 298 | return self._predict(X) |
271 | 299 |
|
|
0 commit comments