Upload edit\Qwen3-TTS-test\.venv\Lib\site-packages\sklearn\ensemble\tests\test_voting.py with huggingface_hub
Browse files
edit//Qwen3-TTS-test//.venv//Lib//site-packages//sklearn//ensemble//tests//test_voting.py
ADDED
|
@@ -0,0 +1,795 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Testing for the VotingClassifier and VotingRegressor"""
|
| 2 |
+
|
| 3 |
+
import re
|
| 4 |
+
|
| 5 |
+
import numpy as np
|
| 6 |
+
import pytest
|
| 7 |
+
|
| 8 |
+
from sklearn import config_context, datasets
|
| 9 |
+
from sklearn.base import BaseEstimator, ClassifierMixin, clone
|
| 10 |
+
from sklearn.calibration import CalibratedClassifierCV
|
| 11 |
+
from sklearn.datasets import make_multilabel_classification
|
| 12 |
+
from sklearn.dummy import DummyRegressor
|
| 13 |
+
from sklearn.ensemble import (
|
| 14 |
+
RandomForestClassifier,
|
| 15 |
+
RandomForestRegressor,
|
| 16 |
+
VotingClassifier,
|
| 17 |
+
VotingRegressor,
|
| 18 |
+
)
|
| 19 |
+
from sklearn.exceptions import NotFittedError
|
| 20 |
+
from sklearn.linear_model import LinearRegression, LogisticRegression
|
| 21 |
+
from sklearn.model_selection import GridSearchCV, cross_val_score, train_test_split
|
| 22 |
+
from sklearn.multiclass import OneVsRestClassifier
|
| 23 |
+
from sklearn.naive_bayes import GaussianNB
|
| 24 |
+
from sklearn.neighbors import KNeighborsClassifier
|
| 25 |
+
from sklearn.preprocessing import StandardScaler
|
| 26 |
+
from sklearn.svm import SVC
|
| 27 |
+
from sklearn.tests.metadata_routing_common import (
|
| 28 |
+
ConsumingClassifier,
|
| 29 |
+
ConsumingRegressor,
|
| 30 |
+
_Registry,
|
| 31 |
+
check_recorded_metadata,
|
| 32 |
+
)
|
| 33 |
+
from sklearn.tree import DecisionTreeClassifier, DecisionTreeRegressor
|
| 34 |
+
from sklearn.utils._testing import (
|
| 35 |
+
assert_almost_equal,
|
| 36 |
+
assert_array_almost_equal,
|
| 37 |
+
assert_array_equal,
|
| 38 |
+
)
|
| 39 |
+
|
| 40 |
+
# Load datasets
|
| 41 |
+
iris = datasets.load_iris()
|
| 42 |
+
X, y = iris.data[:, 1:3], iris.target
|
| 43 |
+
# Scaled to solve ConvergenceWarning throw by Logistic Regression
|
| 44 |
+
X_scaled = StandardScaler().fit_transform(X)
|
| 45 |
+
|
| 46 |
+
X_r, y_r = datasets.load_diabetes(return_X_y=True)
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
@pytest.mark.parametrize(
|
| 50 |
+
"params, err_msg",
|
| 51 |
+
[
|
| 52 |
+
(
|
| 53 |
+
{"estimators": []},
|
| 54 |
+
"Invalid 'estimators' attribute, 'estimators' should be a non-empty list",
|
| 55 |
+
),
|
| 56 |
+
(
|
| 57 |
+
{"estimators": [LogisticRegression()]},
|
| 58 |
+
"Invalid 'estimators' attribute, 'estimators' should be a non-empty list",
|
| 59 |
+
),
|
| 60 |
+
(
|
| 61 |
+
{"estimators": [(213, LogisticRegression())]},
|
| 62 |
+
"Invalid 'estimators' attribute, 'estimators' should be a non-empty list",
|
| 63 |
+
),
|
| 64 |
+
(
|
| 65 |
+
{"estimators": [("lr", LogisticRegression())], "weights": [1, 2]},
|
| 66 |
+
"Number of `estimators` and weights must be equal",
|
| 67 |
+
),
|
| 68 |
+
],
|
| 69 |
+
)
|
| 70 |
+
def test_voting_classifier_estimator_init(params, err_msg):
|
| 71 |
+
ensemble = VotingClassifier(**params)
|
| 72 |
+
with pytest.raises(ValueError, match=err_msg):
|
| 73 |
+
ensemble.fit(X, y)
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
def test_predictproba_hardvoting():
|
| 77 |
+
eclf = VotingClassifier(
|
| 78 |
+
estimators=[("lr1", LogisticRegression()), ("lr2", LogisticRegression())],
|
| 79 |
+
voting="hard",
|
| 80 |
+
)
|
| 81 |
+
|
| 82 |
+
inner_msg = "predict_proba is not available when voting='hard'"
|
| 83 |
+
outer_msg = "'VotingClassifier' has no attribute 'predict_proba'"
|
| 84 |
+
with pytest.raises(AttributeError, match=outer_msg) as exec_info:
|
| 85 |
+
eclf.predict_proba
|
| 86 |
+
assert isinstance(exec_info.value.__cause__, AttributeError)
|
| 87 |
+
assert inner_msg in str(exec_info.value.__cause__)
|
| 88 |
+
|
| 89 |
+
assert not hasattr(eclf, "predict_proba")
|
| 90 |
+
eclf.fit(X_scaled, y)
|
| 91 |
+
assert not hasattr(eclf, "predict_proba")
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
def test_notfitted():
|
| 95 |
+
eclf = VotingClassifier(
|
| 96 |
+
estimators=[("lr1", LogisticRegression()), ("lr2", LogisticRegression())],
|
| 97 |
+
voting="soft",
|
| 98 |
+
)
|
| 99 |
+
ereg = VotingRegressor([("dr", DummyRegressor())])
|
| 100 |
+
msg = (
|
| 101 |
+
"This %s instance is not fitted yet. Call 'fit'"
|
| 102 |
+
" with appropriate arguments before using this estimator."
|
| 103 |
+
)
|
| 104 |
+
with pytest.raises(NotFittedError, match=msg % "VotingClassifier"):
|
| 105 |
+
eclf.predict(X)
|
| 106 |
+
with pytest.raises(NotFittedError, match=msg % "VotingClassifier"):
|
| 107 |
+
eclf.predict_proba(X)
|
| 108 |
+
with pytest.raises(NotFittedError, match=msg % "VotingClassifier"):
|
| 109 |
+
eclf.transform(X)
|
| 110 |
+
with pytest.raises(NotFittedError, match=msg % "VotingRegressor"):
|
| 111 |
+
ereg.predict(X_r)
|
| 112 |
+
with pytest.raises(NotFittedError, match=msg % "VotingRegressor"):
|
| 113 |
+
ereg.transform(X_r)
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
def test_majority_label_iris(global_random_seed):
|
| 117 |
+
"""Check classification by majority label on dataset iris."""
|
| 118 |
+
clf1 = LogisticRegression(random_state=global_random_seed)
|
| 119 |
+
clf2 = RandomForestClassifier(n_estimators=10, random_state=global_random_seed)
|
| 120 |
+
clf3 = GaussianNB()
|
| 121 |
+
eclf = VotingClassifier(
|
| 122 |
+
estimators=[("lr", clf1), ("rf", clf2), ("gnb", clf3)], voting="hard"
|
| 123 |
+
)
|
| 124 |
+
scores = cross_val_score(eclf, X, y, scoring="accuracy")
|
| 125 |
+
|
| 126 |
+
assert scores.mean() >= 0.9
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
def test_tie_situation():
|
| 130 |
+
"""Check voting classifier selects smaller class label in tie situation."""
|
| 131 |
+
clf1 = LogisticRegression(random_state=123)
|
| 132 |
+
clf2 = RandomForestClassifier(random_state=123)
|
| 133 |
+
eclf = VotingClassifier(estimators=[("lr", clf1), ("rf", clf2)], voting="hard")
|
| 134 |
+
assert clf1.fit(X, y).predict(X)[52] == 2
|
| 135 |
+
assert clf2.fit(X, y).predict(X)[52] == 1
|
| 136 |
+
assert eclf.fit(X, y).predict(X)[52] == 1
|
| 137 |
+
|
| 138 |
+
|
| 139 |
+
def test_weights_iris(global_random_seed):
|
| 140 |
+
"""Check classification by average probabilities on dataset iris."""
|
| 141 |
+
clf1 = LogisticRegression(random_state=global_random_seed)
|
| 142 |
+
clf2 = RandomForestClassifier(n_estimators=10, random_state=global_random_seed)
|
| 143 |
+
clf3 = GaussianNB()
|
| 144 |
+
eclf = VotingClassifier(
|
| 145 |
+
estimators=[("lr", clf1), ("rf", clf2), ("gnb", clf3)],
|
| 146 |
+
voting="soft",
|
| 147 |
+
weights=[1, 2, 10],
|
| 148 |
+
)
|
| 149 |
+
scores = cross_val_score(eclf, X_scaled, y, scoring="accuracy")
|
| 150 |
+
assert scores.mean() >= 0.9
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
def test_weights_regressor():
|
| 154 |
+
"""Check weighted average regression prediction on diabetes dataset."""
|
| 155 |
+
reg1 = DummyRegressor(strategy="mean")
|
| 156 |
+
reg2 = DummyRegressor(strategy="median")
|
| 157 |
+
reg3 = DummyRegressor(strategy="quantile", quantile=0.2)
|
| 158 |
+
ereg = VotingRegressor(
|
| 159 |
+
[("mean", reg1), ("median", reg2), ("quantile", reg3)], weights=[1, 2, 10]
|
| 160 |
+
)
|
| 161 |
+
|
| 162 |
+
X_r_train, X_r_test, y_r_train, y_r_test = train_test_split(
|
| 163 |
+
X_r, y_r, test_size=0.25
|
| 164 |
+
)
|
| 165 |
+
|
| 166 |
+
reg1_pred = reg1.fit(X_r_train, y_r_train).predict(X_r_test)
|
| 167 |
+
reg2_pred = reg2.fit(X_r_train, y_r_train).predict(X_r_test)
|
| 168 |
+
reg3_pred = reg3.fit(X_r_train, y_r_train).predict(X_r_test)
|
| 169 |
+
ereg_pred = ereg.fit(X_r_train, y_r_train).predict(X_r_test)
|
| 170 |
+
|
| 171 |
+
avg = np.average(
|
| 172 |
+
np.asarray([reg1_pred, reg2_pred, reg3_pred]), axis=0, weights=[1, 2, 10]
|
| 173 |
+
)
|
| 174 |
+
assert_almost_equal(ereg_pred, avg, decimal=2)
|
| 175 |
+
|
| 176 |
+
ereg_weights_none = VotingRegressor(
|
| 177 |
+
[("mean", reg1), ("median", reg2), ("quantile", reg3)], weights=None
|
| 178 |
+
)
|
| 179 |
+
ereg_weights_equal = VotingRegressor(
|
| 180 |
+
[("mean", reg1), ("median", reg2), ("quantile", reg3)], weights=[1, 1, 1]
|
| 181 |
+
)
|
| 182 |
+
ereg_weights_none.fit(X_r_train, y_r_train)
|
| 183 |
+
ereg_weights_equal.fit(X_r_train, y_r_train)
|
| 184 |
+
ereg_none_pred = ereg_weights_none.predict(X_r_test)
|
| 185 |
+
ereg_equal_pred = ereg_weights_equal.predict(X_r_test)
|
| 186 |
+
assert_almost_equal(ereg_none_pred, ereg_equal_pred, decimal=2)
|
| 187 |
+
|
| 188 |
+
|
| 189 |
+
def test_predict_on_toy_problem(global_random_seed):
|
| 190 |
+
"""Manually check predicted class labels for toy dataset."""
|
| 191 |
+
clf1 = LogisticRegression(random_state=global_random_seed)
|
| 192 |
+
clf2 = RandomForestClassifier(n_estimators=10, random_state=global_random_seed)
|
| 193 |
+
clf3 = GaussianNB()
|
| 194 |
+
|
| 195 |
+
X = np.array(
|
| 196 |
+
[[-1.1, -1.5], [-1.2, -1.4], [-3.4, -2.2], [1.1, 1.2], [2.1, 1.4], [3.1, 2.3]]
|
| 197 |
+
)
|
| 198 |
+
|
| 199 |
+
y = np.array([1, 1, 1, 2, 2, 2])
|
| 200 |
+
|
| 201 |
+
assert_array_equal(clf1.fit(X, y).predict(X), [1, 1, 1, 2, 2, 2])
|
| 202 |
+
assert_array_equal(clf2.fit(X, y).predict(X), [1, 1, 1, 2, 2, 2])
|
| 203 |
+
assert_array_equal(clf3.fit(X, y).predict(X), [1, 1, 1, 2, 2, 2])
|
| 204 |
+
|
| 205 |
+
eclf = VotingClassifier(
|
| 206 |
+
estimators=[("lr", clf1), ("rf", clf2), ("gnb", clf3)],
|
| 207 |
+
voting="hard",
|
| 208 |
+
weights=[1, 1, 1],
|
| 209 |
+
)
|
| 210 |
+
assert_array_equal(eclf.fit(X, y).predict(X), [1, 1, 1, 2, 2, 2])
|
| 211 |
+
|
| 212 |
+
eclf = VotingClassifier(
|
| 213 |
+
estimators=[("lr", clf1), ("rf", clf2), ("gnb", clf3)],
|
| 214 |
+
voting="soft",
|
| 215 |
+
weights=[1, 1, 1],
|
| 216 |
+
)
|
| 217 |
+
assert_array_equal(eclf.fit(X, y).predict(X), [1, 1, 1, 2, 2, 2])
|
| 218 |
+
|
| 219 |
+
|
| 220 |
+
def test_predict_proba_on_toy_problem():
|
| 221 |
+
"""Calculate predicted probabilities on toy dataset."""
|
| 222 |
+
clf1 = LogisticRegression(random_state=123)
|
| 223 |
+
clf2 = RandomForestClassifier(random_state=123)
|
| 224 |
+
clf3 = GaussianNB()
|
| 225 |
+
X = np.array([[-1.1, -1.5], [-1.2, -1.4], [-3.4, -2.2], [1.1, 1.2]])
|
| 226 |
+
y = np.array([1, 1, 2, 2])
|
| 227 |
+
|
| 228 |
+
clf1_res = np.array(
|
| 229 |
+
[
|
| 230 |
+
[0.59790391, 0.40209609],
|
| 231 |
+
[0.57622162, 0.42377838],
|
| 232 |
+
[0.50728456, 0.49271544],
|
| 233 |
+
[0.40241774, 0.59758226],
|
| 234 |
+
]
|
| 235 |
+
)
|
| 236 |
+
|
| 237 |
+
clf2_res = np.array([[0.8, 0.2], [0.8, 0.2], [0.2, 0.8], [0.3, 0.7]])
|
| 238 |
+
|
| 239 |
+
clf3_res = np.array(
|
| 240 |
+
[[0.9985082, 0.0014918], [0.99845843, 0.00154157], [0.0, 1.0], [0.0, 1.0]]
|
| 241 |
+
)
|
| 242 |
+
|
| 243 |
+
t00 = (2 * clf1_res[0][0] + clf2_res[0][0] + clf3_res[0][0]) / 4
|
| 244 |
+
t11 = (2 * clf1_res[1][1] + clf2_res[1][1] + clf3_res[1][1]) / 4
|
| 245 |
+
t21 = (2 * clf1_res[2][1] + clf2_res[2][1] + clf3_res[2][1]) / 4
|
| 246 |
+
t31 = (2 * clf1_res[3][1] + clf2_res[3][1] + clf3_res[3][1]) / 4
|
| 247 |
+
|
| 248 |
+
eclf = VotingClassifier(
|
| 249 |
+
estimators=[("lr", clf1), ("rf", clf2), ("gnb", clf3)],
|
| 250 |
+
voting="soft",
|
| 251 |
+
weights=[2, 1, 1],
|
| 252 |
+
)
|
| 253 |
+
eclf_res = eclf.fit(X, y).predict_proba(X)
|
| 254 |
+
|
| 255 |
+
assert_almost_equal(t00, eclf_res[0][0], decimal=1)
|
| 256 |
+
assert_almost_equal(t11, eclf_res[1][1], decimal=1)
|
| 257 |
+
assert_almost_equal(t21, eclf_res[2][1], decimal=1)
|
| 258 |
+
assert_almost_equal(t31, eclf_res[3][1], decimal=1)
|
| 259 |
+
|
| 260 |
+
inner_msg = "predict_proba is not available when voting='hard'"
|
| 261 |
+
outer_msg = "'VotingClassifier' has no attribute 'predict_proba'"
|
| 262 |
+
with pytest.raises(AttributeError, match=outer_msg) as exec_info:
|
| 263 |
+
eclf = VotingClassifier(
|
| 264 |
+
estimators=[("lr", clf1), ("rf", clf2), ("gnb", clf3)], voting="hard"
|
| 265 |
+
)
|
| 266 |
+
eclf.fit(X, y).predict_proba(X)
|
| 267 |
+
|
| 268 |
+
assert isinstance(exec_info.value.__cause__, AttributeError)
|
| 269 |
+
assert inner_msg in str(exec_info.value.__cause__)
|
| 270 |
+
|
| 271 |
+
|
| 272 |
+
def test_multilabel():
|
| 273 |
+
"""Check if error is raised for multilabel classification."""
|
| 274 |
+
X, y = make_multilabel_classification(
|
| 275 |
+
n_classes=2, n_labels=1, allow_unlabeled=False, random_state=123
|
| 276 |
+
)
|
| 277 |
+
clf = OneVsRestClassifier(SVC(kernel="linear"))
|
| 278 |
+
|
| 279 |
+
eclf = VotingClassifier(estimators=[("ovr", clf)], voting="hard")
|
| 280 |
+
|
| 281 |
+
try:
|
| 282 |
+
eclf.fit(X, y)
|
| 283 |
+
except NotImplementedError:
|
| 284 |
+
return
|
| 285 |
+
|
| 286 |
+
|
| 287 |
+
def test_gridsearch():
|
| 288 |
+
"""Check GridSearch support."""
|
| 289 |
+
clf1 = LogisticRegression(random_state=1)
|
| 290 |
+
clf2 = RandomForestClassifier(random_state=1, n_estimators=3)
|
| 291 |
+
clf3 = GaussianNB()
|
| 292 |
+
eclf = VotingClassifier(
|
| 293 |
+
estimators=[("lr", clf1), ("rf", clf2), ("gnb", clf3)], voting="soft"
|
| 294 |
+
)
|
| 295 |
+
|
| 296 |
+
params = {
|
| 297 |
+
"lr__C": [1.0, 100.0],
|
| 298 |
+
"voting": ["soft", "hard"],
|
| 299 |
+
"weights": [[0.5, 0.5, 0.5], [1.0, 0.5, 0.5]],
|
| 300 |
+
}
|
| 301 |
+
|
| 302 |
+
grid = GridSearchCV(estimator=eclf, param_grid=params, cv=2)
|
| 303 |
+
grid.fit(X_scaled, y)
|
| 304 |
+
|
| 305 |
+
|
| 306 |
+
def test_parallel_fit(global_random_seed):
|
| 307 |
+
"""Check parallel backend of VotingClassifier on toy dataset."""
|
| 308 |
+
clf1 = LogisticRegression(random_state=global_random_seed)
|
| 309 |
+
clf2 = RandomForestClassifier(n_estimators=10, random_state=global_random_seed)
|
| 310 |
+
clf3 = GaussianNB()
|
| 311 |
+
X = np.array([[-1.1, -1.5], [-1.2, -1.4], [-3.4, -2.2], [1.1, 1.2]])
|
| 312 |
+
y = np.array([1, 1, 2, 2])
|
| 313 |
+
|
| 314 |
+
eclf1 = VotingClassifier(
|
| 315 |
+
estimators=[("lr", clf1), ("rf", clf2), ("gnb", clf3)], voting="soft", n_jobs=1
|
| 316 |
+
).fit(X, y)
|
| 317 |
+
eclf2 = VotingClassifier(
|
| 318 |
+
estimators=[("lr", clf1), ("rf", clf2), ("gnb", clf3)], voting="soft", n_jobs=2
|
| 319 |
+
).fit(X, y)
|
| 320 |
+
|
| 321 |
+
assert_array_equal(eclf1.predict(X), eclf2.predict(X))
|
| 322 |
+
assert_array_almost_equal(eclf1.predict_proba(X), eclf2.predict_proba(X))
|
| 323 |
+
|
| 324 |
+
|
| 325 |
+
def test_sample_weight(global_random_seed):
|
| 326 |
+
"""Tests sample_weight parameter of VotingClassifier"""
|
| 327 |
+
clf1 = LogisticRegression(random_state=global_random_seed)
|
| 328 |
+
clf2 = RandomForestClassifier(n_estimators=10, random_state=global_random_seed)
|
| 329 |
+
clf3 = CalibratedClassifierCV(SVC(random_state=global_random_seed), ensemble=False)
|
| 330 |
+
eclf1 = VotingClassifier(
|
| 331 |
+
estimators=[("lr", clf1), ("rf", clf2), ("svc", clf3)], voting="soft"
|
| 332 |
+
).fit(X_scaled, y, sample_weight=np.ones((len(y),)))
|
| 333 |
+
eclf2 = VotingClassifier(
|
| 334 |
+
estimators=[("lr", clf1), ("rf", clf2), ("svc", clf3)], voting="soft"
|
| 335 |
+
).fit(X_scaled, y)
|
| 336 |
+
assert_array_equal(eclf1.predict(X_scaled), eclf2.predict(X_scaled))
|
| 337 |
+
assert_array_almost_equal(
|
| 338 |
+
eclf1.predict_proba(X_scaled), eclf2.predict_proba(X_scaled)
|
| 339 |
+
)
|
| 340 |
+
sample_weight = np.random.RandomState(global_random_seed).uniform(size=(len(y),))
|
| 341 |
+
eclf3 = VotingClassifier(estimators=[("lr", clf1)], voting="soft")
|
| 342 |
+
eclf3.fit(X_scaled, y, sample_weight=sample_weight)
|
| 343 |
+
clf1.fit(X_scaled, y, sample_weight)
|
| 344 |
+
assert_array_equal(eclf3.predict(X_scaled), clf1.predict(X_scaled))
|
| 345 |
+
assert_array_almost_equal(
|
| 346 |
+
eclf3.predict_proba(X_scaled), clf1.predict_proba(X_scaled)
|
| 347 |
+
)
|
| 348 |
+
|
| 349 |
+
# check that an error is raised and indicative if sample_weight is not
|
| 350 |
+
# supported.
|
| 351 |
+
clf4 = KNeighborsClassifier()
|
| 352 |
+
eclf3 = VotingClassifier(
|
| 353 |
+
estimators=[("lr", clf1), ("svc", clf3), ("knn", clf4)], voting="soft"
|
| 354 |
+
)
|
| 355 |
+
msg = "Underlying estimator KNeighborsClassifier does not support sample weights."
|
| 356 |
+
with pytest.raises(TypeError, match=msg):
|
| 357 |
+
eclf3.fit(X_scaled, y, sample_weight=sample_weight)
|
| 358 |
+
|
| 359 |
+
# check that _fit_single_estimator will raise the right error
|
| 360 |
+
# it should raise the original error if this is not linked to sample_weight
|
| 361 |
+
class ClassifierErrorFit(ClassifierMixin, BaseEstimator):
|
| 362 |
+
def fit(self, X_scaled, y, sample_weight):
|
| 363 |
+
raise TypeError("Error unrelated to sample_weight.")
|
| 364 |
+
|
| 365 |
+
clf = ClassifierErrorFit()
|
| 366 |
+
with pytest.raises(TypeError, match="Error unrelated to sample_weight"):
|
| 367 |
+
clf.fit(X_scaled, y, sample_weight=sample_weight)
|
| 368 |
+
|
| 369 |
+
|
| 370 |
+
def test_sample_weight_kwargs():
|
| 371 |
+
"""Check that VotingClassifier passes sample_weight as kwargs"""
|
| 372 |
+
|
| 373 |
+
class MockClassifier(ClassifierMixin, BaseEstimator):
|
| 374 |
+
"""Mock Classifier to check that sample_weight is received as kwargs"""
|
| 375 |
+
|
| 376 |
+
def fit(self, X, y, *args, **sample_weight):
|
| 377 |
+
assert "sample_weight" in sample_weight
|
| 378 |
+
|
| 379 |
+
clf = MockClassifier()
|
| 380 |
+
eclf = VotingClassifier(estimators=[("mock", clf)], voting="soft")
|
| 381 |
+
|
| 382 |
+
# Should not raise an error.
|
| 383 |
+
eclf.fit(X, y, sample_weight=np.ones((len(y),)))
|
| 384 |
+
|
| 385 |
+
|
| 386 |
+
def test_voting_classifier_set_params(global_random_seed):
|
| 387 |
+
# check equivalence in the output when setting underlying estimators
|
| 388 |
+
clf1 = LogisticRegression(random_state=global_random_seed)
|
| 389 |
+
clf2 = RandomForestClassifier(
|
| 390 |
+
n_estimators=10, random_state=global_random_seed, max_depth=None
|
| 391 |
+
)
|
| 392 |
+
clf3 = GaussianNB()
|
| 393 |
+
|
| 394 |
+
eclf1 = VotingClassifier(
|
| 395 |
+
[("lr", clf1), ("rf", clf2)], voting="soft", weights=[1, 2]
|
| 396 |
+
).fit(X_scaled, y)
|
| 397 |
+
eclf2 = VotingClassifier(
|
| 398 |
+
[("lr", clf1), ("nb", clf3)], voting="soft", weights=[1, 2]
|
| 399 |
+
)
|
| 400 |
+
eclf2.set_params(nb=clf2).fit(X_scaled, y)
|
| 401 |
+
|
| 402 |
+
assert_array_equal(eclf1.predict(X_scaled), eclf2.predict(X_scaled))
|
| 403 |
+
assert_array_almost_equal(
|
| 404 |
+
eclf1.predict_proba(X_scaled), eclf2.predict_proba(X_scaled)
|
| 405 |
+
)
|
| 406 |
+
assert eclf2.estimators[0][1].get_params() == clf1.get_params()
|
| 407 |
+
assert eclf2.estimators[1][1].get_params() == clf2.get_params()
|
| 408 |
+
|
| 409 |
+
|
| 410 |
+
def test_set_estimator_drop():
|
| 411 |
+
# VotingClassifier set_params should be able to set estimators as drop
|
| 412 |
+
# Test predict
|
| 413 |
+
clf1 = LogisticRegression(random_state=123)
|
| 414 |
+
clf2 = RandomForestClassifier(n_estimators=10, random_state=123)
|
| 415 |
+
clf3 = GaussianNB()
|
| 416 |
+
eclf1 = VotingClassifier(
|
| 417 |
+
estimators=[("lr", clf1), ("rf", clf2), ("nb", clf3)],
|
| 418 |
+
voting="hard",
|
| 419 |
+
weights=[1, 0, 0.5],
|
| 420 |
+
).fit(X, y)
|
| 421 |
+
|
| 422 |
+
eclf2 = VotingClassifier(
|
| 423 |
+
estimators=[("lr", clf1), ("rf", clf2), ("nb", clf3)],
|
| 424 |
+
voting="hard",
|
| 425 |
+
weights=[1, 1, 0.5],
|
| 426 |
+
)
|
| 427 |
+
eclf2.set_params(rf="drop").fit(X, y)
|
| 428 |
+
|
| 429 |
+
assert_array_equal(eclf1.predict(X), eclf2.predict(X))
|
| 430 |
+
|
| 431 |
+
assert dict(eclf2.estimators)["rf"] == "drop"
|
| 432 |
+
assert len(eclf2.estimators_) == 2
|
| 433 |
+
assert all(
|
| 434 |
+
isinstance(est, (LogisticRegression, GaussianNB)) for est in eclf2.estimators_
|
| 435 |
+
)
|
| 436 |
+
assert eclf2.get_params()["rf"] == "drop"
|
| 437 |
+
|
| 438 |
+
eclf1.set_params(voting="soft").fit(X, y)
|
| 439 |
+
eclf2.set_params(voting="soft").fit(X, y)
|
| 440 |
+
|
| 441 |
+
assert_array_equal(eclf1.predict(X), eclf2.predict(X))
|
| 442 |
+
assert_array_almost_equal(eclf1.predict_proba(X), eclf2.predict_proba(X))
|
| 443 |
+
msg = "All estimators are dropped. At least one is required"
|
| 444 |
+
with pytest.raises(ValueError, match=msg):
|
| 445 |
+
eclf2.set_params(lr="drop", rf="drop", nb="drop").fit(X, y)
|
| 446 |
+
|
| 447 |
+
# Test soft voting transform
|
| 448 |
+
X1 = np.array([[1], [2]])
|
| 449 |
+
y1 = np.array([1, 2])
|
| 450 |
+
eclf1 = VotingClassifier(
|
| 451 |
+
estimators=[("rf", clf2), ("nb", clf3)],
|
| 452 |
+
voting="soft",
|
| 453 |
+
weights=[0, 0.5],
|
| 454 |
+
flatten_transform=False,
|
| 455 |
+
).fit(X1, y1)
|
| 456 |
+
|
| 457 |
+
eclf2 = VotingClassifier(
|
| 458 |
+
estimators=[("rf", clf2), ("nb", clf3)],
|
| 459 |
+
voting="soft",
|
| 460 |
+
weights=[1, 0.5],
|
| 461 |
+
flatten_transform=False,
|
| 462 |
+
)
|
| 463 |
+
eclf2.set_params(rf="drop").fit(X1, y1)
|
| 464 |
+
assert_array_almost_equal(
|
| 465 |
+
eclf1.transform(X1),
|
| 466 |
+
np.array([[[0.7, 0.3], [0.3, 0.7]], [[1.0, 0.0], [0.0, 1.0]]]),
|
| 467 |
+
)
|
| 468 |
+
assert_array_almost_equal(eclf2.transform(X1), np.array([[[1.0, 0.0], [0.0, 1.0]]]))
|
| 469 |
+
eclf1.set_params(voting="hard")
|
| 470 |
+
eclf2.set_params(voting="hard")
|
| 471 |
+
assert_array_equal(eclf1.transform(X1), np.array([[0, 0], [1, 1]]))
|
| 472 |
+
assert_array_equal(eclf2.transform(X1), np.array([[0], [1]]))
|
| 473 |
+
|
| 474 |
+
|
| 475 |
+
def test_estimator_weights_format(global_random_seed):
|
| 476 |
+
# Test estimator weights inputs as list and array
|
| 477 |
+
clf1 = LogisticRegression(random_state=global_random_seed)
|
| 478 |
+
clf2 = RandomForestClassifier(n_estimators=10, random_state=global_random_seed)
|
| 479 |
+
eclf1 = VotingClassifier(
|
| 480 |
+
estimators=[("lr", clf1), ("rf", clf2)], weights=[1, 2], voting="soft"
|
| 481 |
+
)
|
| 482 |
+
eclf2 = VotingClassifier(
|
| 483 |
+
estimators=[("lr", clf1), ("rf", clf2)], weights=np.array((1, 2)), voting="soft"
|
| 484 |
+
)
|
| 485 |
+
eclf1.fit(X_scaled, y)
|
| 486 |
+
eclf2.fit(X_scaled, y)
|
| 487 |
+
assert_array_almost_equal(
|
| 488 |
+
eclf1.predict_proba(X_scaled), eclf2.predict_proba(X_scaled)
|
| 489 |
+
)
|
| 490 |
+
|
| 491 |
+
|
| 492 |
+
def test_transform(global_random_seed):
|
| 493 |
+
"""Check transform method of VotingClassifier on toy dataset."""
|
| 494 |
+
clf1 = LogisticRegression(random_state=global_random_seed)
|
| 495 |
+
clf2 = RandomForestClassifier(n_estimators=10, random_state=global_random_seed)
|
| 496 |
+
clf3 = GaussianNB()
|
| 497 |
+
X = np.array([[-1.1, -1.5], [-1.2, -1.4], [-3.4, -2.2], [1.1, 1.2]])
|
| 498 |
+
y = np.array([1, 1, 2, 2])
|
| 499 |
+
|
| 500 |
+
eclf1 = VotingClassifier(
|
| 501 |
+
estimators=[("lr", clf1), ("rf", clf2), ("gnb", clf3)], voting="soft"
|
| 502 |
+
).fit(X, y)
|
| 503 |
+
eclf2 = VotingClassifier(
|
| 504 |
+
estimators=[("lr", clf1), ("rf", clf2), ("gnb", clf3)],
|
| 505 |
+
voting="soft",
|
| 506 |
+
flatten_transform=True,
|
| 507 |
+
).fit(X, y)
|
| 508 |
+
eclf3 = VotingClassifier(
|
| 509 |
+
estimators=[("lr", clf1), ("rf", clf2), ("gnb", clf3)],
|
| 510 |
+
voting="soft",
|
| 511 |
+
flatten_transform=False,
|
| 512 |
+
).fit(X, y)
|
| 513 |
+
|
| 514 |
+
assert_array_equal(eclf1.transform(X).shape, (4, 6))
|
| 515 |
+
assert_array_equal(eclf2.transform(X).shape, (4, 6))
|
| 516 |
+
assert_array_equal(eclf3.transform(X).shape, (3, 4, 2))
|
| 517 |
+
assert_array_almost_equal(eclf1.transform(X), eclf2.transform(X))
|
| 518 |
+
assert_array_almost_equal(
|
| 519 |
+
eclf3.transform(X).swapaxes(0, 1).reshape((4, 6)), eclf2.transform(X)
|
| 520 |
+
)
|
| 521 |
+
|
| 522 |
+
|
| 523 |
+
@pytest.mark.parametrize(
|
| 524 |
+
"X, y, voter",
|
| 525 |
+
[
|
| 526 |
+
(
|
| 527 |
+
X,
|
| 528 |
+
y,
|
| 529 |
+
VotingClassifier(
|
| 530 |
+
[
|
| 531 |
+
("lr", LogisticRegression()),
|
| 532 |
+
("rf", RandomForestClassifier(n_estimators=5)),
|
| 533 |
+
]
|
| 534 |
+
),
|
| 535 |
+
),
|
| 536 |
+
(
|
| 537 |
+
X_r,
|
| 538 |
+
y_r,
|
| 539 |
+
VotingRegressor(
|
| 540 |
+
[
|
| 541 |
+
("lr", LinearRegression()),
|
| 542 |
+
("rf", RandomForestRegressor(n_estimators=5)),
|
| 543 |
+
]
|
| 544 |
+
),
|
| 545 |
+
),
|
| 546 |
+
],
|
| 547 |
+
)
|
| 548 |
+
def test_none_estimator_with_weights(X, y, voter):
|
| 549 |
+
# check that an estimator can be set to 'drop' and passing some weight
|
| 550 |
+
# regression test for
|
| 551 |
+
# https://github.com/scikit-learn/scikit-learn/issues/13777
|
| 552 |
+
voter = clone(voter)
|
| 553 |
+
# Scaled to solve ConvergenceWarning throw by Logistic Regression
|
| 554 |
+
X_scaled = StandardScaler().fit_transform(X)
|
| 555 |
+
voter.fit(X_scaled, y, sample_weight=np.ones(y.shape))
|
| 556 |
+
voter.set_params(lr="drop")
|
| 557 |
+
voter.fit(X_scaled, y, sample_weight=np.ones(y.shape))
|
| 558 |
+
y_pred = voter.predict(X_scaled)
|
| 559 |
+
assert y_pred.shape == y.shape
|
| 560 |
+
|
| 561 |
+
|
| 562 |
+
@pytest.mark.parametrize(
|
| 563 |
+
"est",
|
| 564 |
+
[
|
| 565 |
+
VotingRegressor(
|
| 566 |
+
estimators=[
|
| 567 |
+
("lr", LinearRegression()),
|
| 568 |
+
("tree", DecisionTreeRegressor(random_state=0)),
|
| 569 |
+
]
|
| 570 |
+
),
|
| 571 |
+
VotingClassifier(
|
| 572 |
+
estimators=[
|
| 573 |
+
("lr", LogisticRegression(random_state=0)),
|
| 574 |
+
("tree", DecisionTreeClassifier(random_state=0)),
|
| 575 |
+
]
|
| 576 |
+
),
|
| 577 |
+
],
|
| 578 |
+
ids=["VotingRegressor", "VotingClassifier"],
|
| 579 |
+
)
|
| 580 |
+
def test_n_features_in(est):
|
| 581 |
+
est = clone(est)
|
| 582 |
+
X = [[1, 2], [3, 4], [5, 6]]
|
| 583 |
+
y = [0, 1, 2]
|
| 584 |
+
|
| 585 |
+
assert not hasattr(est, "n_features_in_")
|
| 586 |
+
est.fit(X, y)
|
| 587 |
+
assert est.n_features_in_ == 2
|
| 588 |
+
|
| 589 |
+
|
| 590 |
+
@pytest.mark.parametrize(
|
| 591 |
+
"estimator",
|
| 592 |
+
[
|
| 593 |
+
VotingRegressor(
|
| 594 |
+
estimators=[
|
| 595 |
+
("lr", LinearRegression()),
|
| 596 |
+
("rf", RandomForestRegressor(random_state=123)),
|
| 597 |
+
],
|
| 598 |
+
verbose=True,
|
| 599 |
+
),
|
| 600 |
+
VotingClassifier(
|
| 601 |
+
estimators=[
|
| 602 |
+
("lr", LogisticRegression(random_state=123)),
|
| 603 |
+
("rf", RandomForestClassifier(random_state=123)),
|
| 604 |
+
],
|
| 605 |
+
verbose=True,
|
| 606 |
+
),
|
| 607 |
+
],
|
| 608 |
+
)
|
| 609 |
+
def test_voting_verbose(estimator, capsys):
|
| 610 |
+
X = np.array([[-1.1, -1.5], [-1.2, -1.4], [-3.4, -2.2], [1.1, 1.2]])
|
| 611 |
+
y = np.array([1, 1, 2, 2])
|
| 612 |
+
|
| 613 |
+
pattern = (
|
| 614 |
+
r"\[Voting\].*\(1 of 2\) Processing lr, total=.*\n"
|
| 615 |
+
r"\[Voting\].*\(2 of 2\) Processing rf, total=.*\n$"
|
| 616 |
+
)
|
| 617 |
+
clone(estimator).fit(X, y)
|
| 618 |
+
assert re.match(pattern, capsys.readouterr()[0])
|
| 619 |
+
|
| 620 |
+
|
| 621 |
+
def test_get_features_names_out_regressor():
|
| 622 |
+
"""Check get_feature_names_out output for regressor."""
|
| 623 |
+
|
| 624 |
+
X = [[1, 2], [3, 4], [5, 6]]
|
| 625 |
+
y = [0, 1, 2]
|
| 626 |
+
|
| 627 |
+
voting = VotingRegressor(
|
| 628 |
+
estimators=[
|
| 629 |
+
("lr", LinearRegression()),
|
| 630 |
+
("tree", DecisionTreeRegressor(random_state=0)),
|
| 631 |
+
("ignore", "drop"),
|
| 632 |
+
]
|
| 633 |
+
)
|
| 634 |
+
voting.fit(X, y)
|
| 635 |
+
|
| 636 |
+
names_out = voting.get_feature_names_out()
|
| 637 |
+
expected_names = ["votingregressor_lr", "votingregressor_tree"]
|
| 638 |
+
assert_array_equal(names_out, expected_names)
|
| 639 |
+
|
| 640 |
+
|
| 641 |
+
@pytest.mark.parametrize(
|
| 642 |
+
"kwargs, expected_names",
|
| 643 |
+
[
|
| 644 |
+
(
|
| 645 |
+
{"voting": "soft", "flatten_transform": True},
|
| 646 |
+
[
|
| 647 |
+
"votingclassifier_lr0",
|
| 648 |
+
"votingclassifier_lr1",
|
| 649 |
+
"votingclassifier_lr2",
|
| 650 |
+
"votingclassifier_tree0",
|
| 651 |
+
"votingclassifier_tree1",
|
| 652 |
+
"votingclassifier_tree2",
|
| 653 |
+
],
|
| 654 |
+
),
|
| 655 |
+
({"voting": "hard"}, ["votingclassifier_lr", "votingclassifier_tree"]),
|
| 656 |
+
],
|
| 657 |
+
)
|
| 658 |
+
def test_get_features_names_out_classifier(kwargs, expected_names):
|
| 659 |
+
"""Check get_feature_names_out for classifier for different settings."""
|
| 660 |
+
X = [[1, 2], [3, 4], [5, 6], [1, 1.2]]
|
| 661 |
+
y = [0, 1, 2, 0]
|
| 662 |
+
|
| 663 |
+
voting = VotingClassifier(
|
| 664 |
+
estimators=[
|
| 665 |
+
("lr", LogisticRegression(random_state=0)),
|
| 666 |
+
("tree", DecisionTreeClassifier(random_state=0)),
|
| 667 |
+
],
|
| 668 |
+
**kwargs,
|
| 669 |
+
)
|
| 670 |
+
voting.fit(X, y)
|
| 671 |
+
X_trans = voting.transform(X)
|
| 672 |
+
names_out = voting.get_feature_names_out()
|
| 673 |
+
|
| 674 |
+
assert X_trans.shape[1] == len(expected_names)
|
| 675 |
+
assert_array_equal(names_out, expected_names)
|
| 676 |
+
|
| 677 |
+
|
| 678 |
+
def test_get_features_names_out_classifier_error():
|
| 679 |
+
"""Check that error is raised when voting="soft" and flatten_transform=False."""
|
| 680 |
+
X = [[1, 2], [3, 4], [5, 6]]
|
| 681 |
+
y = [0, 1, 2]
|
| 682 |
+
|
| 683 |
+
voting = VotingClassifier(
|
| 684 |
+
estimators=[
|
| 685 |
+
("lr", LogisticRegression(random_state=0)),
|
| 686 |
+
("tree", DecisionTreeClassifier(random_state=0)),
|
| 687 |
+
],
|
| 688 |
+
voting="soft",
|
| 689 |
+
flatten_transform=False,
|
| 690 |
+
)
|
| 691 |
+
voting.fit(X, y)
|
| 692 |
+
|
| 693 |
+
msg = (
|
| 694 |
+
"get_feature_names_out is not supported when `voting='soft'` and "
|
| 695 |
+
"`flatten_transform=False`"
|
| 696 |
+
)
|
| 697 |
+
with pytest.raises(ValueError, match=msg):
|
| 698 |
+
voting.get_feature_names_out()
|
| 699 |
+
|
| 700 |
+
|
| 701 |
+
# Metadata Routing Tests
|
| 702 |
+
# ======================
|
| 703 |
+
|
| 704 |
+
|
| 705 |
+
@pytest.mark.parametrize(
|
| 706 |
+
"Estimator, Child",
|
| 707 |
+
[(VotingClassifier, ConsumingClassifier), (VotingRegressor, ConsumingRegressor)],
|
| 708 |
+
)
|
| 709 |
+
def test_routing_passed_metadata_not_supported(Estimator, Child):
|
| 710 |
+
"""Test that the right error message is raised when metadata is passed while
|
| 711 |
+
not supported when `enable_metadata_routing=False`."""
|
| 712 |
+
|
| 713 |
+
X = np.array([[0, 1], [2, 2], [4, 6]])
|
| 714 |
+
y = [1, 2, 3]
|
| 715 |
+
|
| 716 |
+
with pytest.raises(
|
| 717 |
+
ValueError, match="is only supported if enable_metadata_routing=True"
|
| 718 |
+
):
|
| 719 |
+
Estimator(["clf", Child()]).fit(X, y, sample_weight=[1, 1, 1], metadata="a")
|
| 720 |
+
|
| 721 |
+
|
| 722 |
+
@pytest.mark.parametrize(
|
| 723 |
+
"Estimator, Child",
|
| 724 |
+
[(VotingClassifier, ConsumingClassifier), (VotingRegressor, ConsumingRegressor)],
|
| 725 |
+
)
|
| 726 |
+
@config_context(enable_metadata_routing=True)
|
| 727 |
+
def test_get_metadata_routing_without_fit(Estimator, Child):
|
| 728 |
+
# Test that metadata_routing() doesn't raise when called before fit.
|
| 729 |
+
est = Estimator([("sub_est", Child())])
|
| 730 |
+
est.get_metadata_routing()
|
| 731 |
+
|
| 732 |
+
|
| 733 |
+
@pytest.mark.parametrize(
|
| 734 |
+
"Estimator, Child",
|
| 735 |
+
[(VotingClassifier, ConsumingClassifier), (VotingRegressor, ConsumingRegressor)],
|
| 736 |
+
)
|
| 737 |
+
@pytest.mark.parametrize("prop", ["sample_weight", "metadata"])
|
| 738 |
+
@config_context(enable_metadata_routing=True)
|
| 739 |
+
def test_metadata_routing_for_voting_estimators(Estimator, Child, prop):
|
| 740 |
+
"""Test that metadata is routed correctly for Voting*."""
|
| 741 |
+
X = np.array([[0, 1], [2, 2], [4, 6]])
|
| 742 |
+
y = [1, 2, 3]
|
| 743 |
+
sample_weight, metadata = [1, 1, 1], "a"
|
| 744 |
+
|
| 745 |
+
est = Estimator(
|
| 746 |
+
[
|
| 747 |
+
(
|
| 748 |
+
"sub_est1",
|
| 749 |
+
Child(registry=_Registry()).set_fit_request(**{prop: True}),
|
| 750 |
+
),
|
| 751 |
+
(
|
| 752 |
+
"sub_est2",
|
| 753 |
+
Child(registry=_Registry()).set_fit_request(**{prop: True}),
|
| 754 |
+
),
|
| 755 |
+
]
|
| 756 |
+
)
|
| 757 |
+
|
| 758 |
+
est.fit(X, y, **{prop: sample_weight if prop == "sample_weight" else metadata})
|
| 759 |
+
|
| 760 |
+
for estimator in est.estimators:
|
| 761 |
+
if prop == "sample_weight":
|
| 762 |
+
kwargs = {prop: sample_weight}
|
| 763 |
+
else:
|
| 764 |
+
kwargs = {prop: metadata}
|
| 765 |
+
# access sub-estimator in (name, est) with estimator[1]
|
| 766 |
+
registry = estimator[1].registry
|
| 767 |
+
assert len(registry)
|
| 768 |
+
for sub_est in registry:
|
| 769 |
+
check_recorded_metadata(obj=sub_est, method="fit", parent="fit", **kwargs)
|
| 770 |
+
|
| 771 |
+
|
| 772 |
+
@pytest.mark.parametrize(
|
| 773 |
+
"Estimator, Child",
|
| 774 |
+
[(VotingClassifier, ConsumingClassifier), (VotingRegressor, ConsumingRegressor)],
|
| 775 |
+
)
|
| 776 |
+
@config_context(enable_metadata_routing=True)
|
| 777 |
+
def test_metadata_routing_error_for_voting_estimators(Estimator, Child):
|
| 778 |
+
"""Test that the right error is raised when metadata is not requested."""
|
| 779 |
+
X = np.array([[0, 1], [2, 2], [4, 6]])
|
| 780 |
+
y = [1, 2, 3]
|
| 781 |
+
sample_weight, metadata = [1, 1, 1], "a"
|
| 782 |
+
|
| 783 |
+
est = Estimator([("sub_est", Child())])
|
| 784 |
+
|
| 785 |
+
error_message = (
|
| 786 |
+
"[sample_weight, metadata] are passed but are not explicitly set as requested"
|
| 787 |
+
f" or not requested for {Child.__name__}.fit"
|
| 788 |
+
)
|
| 789 |
+
|
| 790 |
+
with pytest.raises(ValueError, match=re.escape(error_message)):
|
| 791 |
+
est.fit(X, y, sample_weight=sample_weight, metadata=metadata)
|
| 792 |
+
|
| 793 |
+
|
| 794 |
+
# End of Metadata Routing Tests
|
| 795 |
+
# =============================
|