Skip to content

Commit de97df0

Browse files
authored
chore: Compatibility scikit-learn 1.9.0 (#1169)
1 parent 6cdb18e commit de97df0

7 files changed

Lines changed: 6447 additions & 5580 deletions

File tree

imblearn/ensemble/_forest.py

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -586,7 +586,12 @@ def fit(self, X, y, sample_weight=None):
586586

587587
self.n_outputs_ = y.shape[1]
588588

589-
y_encoded, expanded_class_weight = self._validate_y_class_weight(y)
589+
if sklearn_version >= parse_version("1.9"):
590+
y_encoded, expanded_class_weight = self._validate_y_class_weight(
591+
y, sample_weight
592+
)
593+
else:
594+
y_encoded, expanded_class_weight = self._validate_y_class_weight(y)
590595

591596
if getattr(y, "dtype", None) != DOUBLE or not y.flags.contiguous:
592597
y_encoded = np.ascontiguousarray(y_encoded, dtype=DOUBLE)
@@ -610,9 +615,16 @@ def fit(self, X, y, sample_weight=None):
610615
sample_weight = expanded_class_weight
611616

612617
# Get bootstrap sample size
613-
n_samples_bootstrap = _get_n_samples_bootstrap(
614-
n_samples=X.shape[0], max_samples=self.max_samples
615-
)
618+
if sklearn_version >= parse_version("1.9"):
619+
n_samples_bootstrap = _get_n_samples_bootstrap(
620+
n_samples=X.shape[0],
621+
max_samples=self.max_samples,
622+
sample_weight=sample_weight,
623+
)
624+
else:
625+
n_samples_bootstrap = _get_n_samples_bootstrap(
626+
n_samples=X.shape[0], max_samples=self.max_samples
627+
)
616628

617629
# Check parameters
618630
self._validate_estimator()
@@ -781,13 +793,26 @@ def _compute_oob_predictions(self, X, y):
781793
y_resample = y[sampler.sample_indices_]
782794

783795
n_sample_subset = y_resample.shape[0]
784-
n_samples_bootstrap = _get_n_samples_bootstrap(
785-
n_sample_subset, self.max_samples
786-
)
796+
if sklearn_version >= parse_version("1.9"):
797+
n_samples_bootstrap = _get_n_samples_bootstrap(
798+
n_sample_subset, self.max_samples, sample_weight=None
799+
)
800+
else:
801+
n_samples_bootstrap = _get_n_samples_bootstrap(
802+
n_sample_subset, self.max_samples
803+
)
787804

788-
unsampled_indices = _generate_unsampled_indices(
789-
estimator.random_state, n_sample_subset, n_samples_bootstrap
790-
)
805+
if sklearn_version >= parse_version("1.9"):
806+
unsampled_indices = _generate_unsampled_indices(
807+
estimator.random_state,
808+
n_sample_subset,
809+
n_samples_bootstrap,
810+
sample_weight=None,
811+
)
812+
else:
813+
unsampled_indices = _generate_unsampled_indices(
814+
estimator.random_state, n_sample_subset, n_samples_bootstrap
815+
)
791816

792817
y_pred = self._get_oob_predictions(
793818
estimator, X_resample[unsampled_indices, :]

imblearn/metrics/tests/test_classification.py

Lines changed: 42 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77

88
import numpy as np
99
import pytest
10-
from sklearn import datasets, svm
10+
from sklearn import datasets
11+
from sklearn.linear_model import LogisticRegression
1112
from sklearn.metrics import (
1213
accuracy_score,
1314
average_precision_score,
@@ -43,7 +44,7 @@
4344

4445

4546
def make_prediction(dataset=None, binary=False):
46-
"""Make some classification predictions on a toy dataset using a SVC
47+
"""Make some classification predictions on a toy dataset using a classifier.
4748
If binary is True restrict to a binary classification problem instead of a
4849
multiclass classification problem
4950
"""
@@ -72,7 +73,7 @@ def make_prediction(dataset=None, binary=False):
7273
X = np.c_[X, rng.randn(n_samples, 200 * n_features)]
7374

7475
# run classifier, get class probabilities and label predictions
75-
clf = svm.SVC(kernel="linear", probability=True, random_state=0)
76+
clf = LogisticRegression()
7677
probas_pred = clf.fit(X[:half], y[:half]).predict_proba(X[half:])
7778

7879
if binary:
@@ -275,10 +276,10 @@ def test_geometric_mean_sample_weight(
275276
@pytest.mark.parametrize(
276277
"average, expected_gmean",
277278
[
278-
("multiclass", 0.41),
279-
(None, [0.85, 0.29, 0.7]),
280-
("macro", 0.68),
281-
("weighted", 0.65),
279+
("multiclass", 0.36),
280+
(None, [0.82, 0.24, 0.72]),
281+
("macro", 0.67),
282+
("weighted", 0.64),
282283
],
283284
)
284285
def test_geometric_mean_score_prediction(average, expected_gmean):
@@ -309,10 +310,10 @@ def test_classification_report_imbalanced_multiclass():
309310

310311
# print classification report with class names
311312
expected_report = (
312-
"pre rec spe f1 geo iba sup setosa 0.83 0.79 0.92 "
313-
"0.81 0.85 0.72 24 versicolor 0.33 0.10 0.86 0.15 "
314-
"0.29 0.08 31 virginica 0.42 0.90 0.55 0.57 0.70 "
315-
"0.51 20 avg / total 0.51 0.53 0.80 0.47 0.58 0.40 75"
313+
"pre rec spe f1 geo iba sup setosa 0.70 0.79 0.84 "
314+
"0.75 0.82 0.66 24 versicolor 0.29 0.06 0.89 0.11 "
315+
"0.24 0.05 31 virginica 0.44 0.90 0.58 0.59 0.72 "
316+
"0.54 20 avg / total 0.46 0.52 0.79 0.44 0.55 0.38 75"
316317
)
317318

318319
report = classification_report_imbalanced(
@@ -324,10 +325,10 @@ def test_classification_report_imbalanced_multiclass():
324325
assert _format_report(report) == expected_report
325326
# print classification report with label detection
326327
expected_report = (
327-
"pre rec spe f1 geo iba sup 0 0.83 0.79 0.92 0.81 "
328-
"0.85 0.72 24 1 0.33 0.10 0.86 0.15 0.29 0.08 31 "
329-
"2 0.42 0.90 0.55 0.57 0.70 0.51 20 avg / total "
330-
"0.51 0.53 0.80 0.47 0.58 0.40 75"
328+
"pre rec spe f1 geo iba sup 0 0.70 0.79 0.84 0.75 "
329+
"0.82 0.66 24 1 0.29 0.06 0.89 0.11 0.24 0.05 31 "
330+
"2 0.44 0.90 0.58 0.59 0.72 0.54 20 avg / total "
331+
"0.46 0.52 0.79 0.44 0.55 0.38 75"
331332
)
332333

333334
report = classification_report_imbalanced(y_true, y_pred)
@@ -340,12 +341,12 @@ def test_classification_report_imbalanced_multiclass_with_digits():
340341

341342
# print classification report with class names
342343
expected_report = (
343-
"pre rec spe f1 geo iba sup setosa 0.82609 0.79167 "
344-
"0.92157 0.80851 0.85415 0.72010 24 versicolor "
345-
"0.33333 0.09677 0.86364 0.15000 0.28910 0.07717 "
346-
"31 virginica 0.41860 0.90000 0.54545 0.57143 0.70065 "
347-
"0.50831 20 avg / total 0.51375 0.53333 0.79733 "
348-
"0.47310 0.57966 0.39788 75"
344+
"pre rec spe f1 geo iba sup setosa 0.70370 0.79167 "
345+
"0.84314 0.74510 0.81700 0.66405 24 versicolor "
346+
"0.28571 0.06452 0.88636 0.10526 0.23913 0.05249 "
347+
"31 virginica 0.43902 0.90000 0.58182 0.59016 0.72363 "
348+
"0.54030 20 avg / total 0.46035 0.52000 0.79132 "
349+
"0.43932 0.55325 0.37827 75"
349350
)
350351
report = classification_report_imbalanced(
351352
y_true,
@@ -357,10 +358,10 @@ def test_classification_report_imbalanced_multiclass_with_digits():
357358
assert _format_report(report) == expected_report
358359
# print classification report with label detection
359360
expected_report = (
360-
"pre rec spe f1 geo iba sup 0 0.83 0.79 0.92 0.81 "
361-
"0.85 0.72 24 1 0.33 0.10 0.86 0.15 0.29 0.08 31 "
362-
"2 0.42 0.90 0.55 0.57 0.70 0.51 20 avg / total 0.51 "
363-
"0.53 0.80 0.47 0.58 0.40 75"
361+
"pre rec spe f1 geo iba sup 0 0.70 0.79 0.84 0.75 "
362+
"0.82 0.66 24 1 0.29 0.06 0.89 0.11 0.24 0.05 31 "
363+
"2 0.44 0.90 0.58 0.59 0.72 0.54 20 avg / total 0.46 "
364+
"0.52 0.79 0.44 0.55 0.38 75"
364365
)
365366
report = classification_report_imbalanced(y_true, y_pred)
366367
assert _format_report(report) == expected_report
@@ -373,19 +374,19 @@ def test_classification_report_imbalanced_multiclass_with_string_label():
373374
y_pred = np.array(["blue", "green", "red"])[y_pred]
374375

375376
expected_report = (
376-
"pre rec spe f1 geo iba sup blue 0.83 0.79 0.92 0.81 "
377-
"0.85 0.72 24 green 0.33 0.10 0.86 0.15 0.29 0.08 31 "
378-
"red 0.42 0.90 0.55 0.57 0.70 0.51 20 avg / total "
379-
"0.51 0.53 0.80 0.47 0.58 0.40 75"
377+
"pre rec spe f1 geo iba sup blue 0.70 0.79 0.84 0.75 "
378+
"0.82 0.66 24 green 0.29 0.06 0.89 0.11 0.24 0.05 31 "
379+
"red 0.44 0.90 0.58 0.59 0.72 0.54 20 avg / total "
380+
"0.46 0.52 0.79 0.44 0.55 0.38 75"
380381
)
381382
report = classification_report_imbalanced(y_true, y_pred)
382383
assert _format_report(report) == expected_report
383384

384385
expected_report = (
385-
"pre rec spe f1 geo iba sup a 0.83 0.79 0.92 0.81 0.85 "
386-
"0.72 24 b 0.33 0.10 0.86 0.15 0.29 0.08 31 c 0.42 "
387-
"0.90 0.55 0.57 0.70 0.51 20 avg / total 0.51 0.53 "
388-
"0.80 0.47 0.58 0.40 75"
386+
"pre rec spe f1 geo iba sup a 0.70 0.79 0.84 0.75 0.82 "
387+
"0.66 24 b 0.29 0.06 0.89 0.11 0.24 0.05 31 c 0.44 "
388+
"0.90 0.58 0.59 0.72 0.54 20 avg / total 0.46 0.52 "
389+
"0.79 0.44 0.55 0.38 75"
389390
)
390391
report = classification_report_imbalanced(
391392
y_true, y_pred, target_names=["a", "b", "c"]
@@ -401,10 +402,10 @@ def test_classification_report_imbalanced_multiclass_with_unicode_label():
401402
y_pred = labels[y_pred]
402403

403404
expected_report = (
404-
"pre rec spe f1 geo iba sup blue¢ 0.83 0.79 0.92 0.81 "
405-
"0.85 0.72 24 green¢ 0.33 0.10 0.86 0.15 0.29 0.08 31 "
406-
"red¢ 0.42 0.90 0.55 0.57 0.70 0.51 20 avg / total "
407-
"0.51 0.53 0.80 0.47 0.58 0.40 75"
405+
"pre rec spe f1 geo iba sup blue¢ 0.70 0.79 0.84 0.75 "
406+
"0.82 0.66 24 green¢ 0.29 0.06 0.89 0.11 0.24 0.05 31 "
407+
"red¢ 0.44 0.90 0.58 0.59 0.72 0.54 20 avg / total "
408+
"0.46 0.52 0.79 0.44 0.55 0.38 75"
408409
)
409410
report = classification_report_imbalanced(y_true, y_pred)
410411
assert _format_report(report) == expected_report
@@ -418,10 +419,10 @@ def test_classification_report_imbalanced_multiclass_with_long_string_label():
418419
y_pred = labels[y_pred]
419420

420421
expected_report = (
421-
"pre rec spe f1 geo iba sup blue 0.83 0.79 0.92 0.81 "
422-
"0.85 0.72 24 greengreengreengreengreen 0.33 0.10 "
423-
"0.86 0.15 0.29 0.08 31 red 0.42 0.90 0.55 0.57 0.70 "
424-
"0.51 20 avg / total 0.51 0.53 0.80 0.47 0.58 0.40 75"
422+
"pre rec spe f1 geo iba sup blue 0.70 0.79 0.84 0.75 "
423+
"0.82 0.66 24 greengreengreengreengreen 0.29 0.06 "
424+
"0.89 0.11 0.24 0.05 31 red 0.44 0.90 0.58 0.59 0.72 "
425+
"0.54 20 avg / total 0.46 0.52 0.79 0.44 0.55 0.38 75"
425426
)
426427

427428
report = classification_report_imbalanced(y_true, y_pred)

imblearn/over_sampling/tests/test_random_over_sampler.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,4 +308,4 @@ def test_random_over_sampler_full_nat():
308308
assert X_res.shape == (4, 2)
309309
assert y_res.shape == (4,)
310310

311-
assert X_res["col_timedelta"].dtype == "timedelta64[ns]"
311+
assert X_res["col_timedelta"].dtype.kind == "m" # timedelta

imblearn/tests/test_pipeline.py

Lines changed: 23 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -350,10 +350,10 @@ def test_pipeline_methods_pca_svm():
350350
iris = load_iris()
351351
X = iris.data
352352
y = iris.target
353-
# Test with PCA + SVC
354-
clf = SVC(gamma="scale", probability=True, random_state=0)
353+
# Test with PCA + LogisticRegression
354+
clf = LogisticRegression()
355355
pca = PCA(svd_solver="full", n_components="mle", whiten=True)
356-
pipe = Pipeline([("pca", pca), ("svc", clf)])
356+
pipe = Pipeline([("pca", pca), ("clf", clf)])
357357
pipe.fit(X, y)
358358
pipe.predict(X)
359359
pipe.predict_proba(X)
@@ -370,15 +370,10 @@ def test_pipeline_methods_preprocessing_svm():
370370
n_classes = len(np.unique(y))
371371
scaler = StandardScaler()
372372
pca = PCA(n_components=2, svd_solver="randomized", whiten=True)
373-
clf = SVC(
374-
gamma="scale",
375-
probability=True,
376-
random_state=0,
377-
decision_function_shape="ovr",
378-
)
373+
clf = LogisticRegression()
379374

380375
for preprocessing in [scaler, pca]:
381-
pipe = Pipeline([("preprocess", preprocessing), ("svc", clf)])
376+
pipe = Pipeline([("preprocess", preprocessing), ("clf", clf)])
382377
pipe.fit(X, y)
383378

384379
# check shapes of various prediction functions
@@ -671,11 +666,11 @@ def test_pipeline_memory_transformer():
671666
cachedir = mkdtemp()
672667
try:
673668
memory = Memory(cachedir, verbose=10)
674-
# Test with Transformer + SVC
675-
clf = SVC(gamma="scale", probability=True, random_state=0)
669+
# Test with Transformer + LogisticRegression
670+
clf = LogisticRegression()
676671
transf = DummyTransf()
677-
pipe = Pipeline([("transf", clone(transf)), ("svc", clf)])
678-
cached_pipe = Pipeline([("transf", transf), ("svc", clf)], memory=memory)
672+
pipe = Pipeline([("transf", clone(transf)), ("clf", clf)])
673+
cached_pipe = Pipeline([("transf", transf), ("clf", clf)], memory=memory)
679674

680675
# Memoize the transformer at the first fit
681676
cached_pipe.fit(X, y)
@@ -707,10 +702,10 @@ def test_pipeline_memory_transformer():
707702
assert cached_pipe.named_steps["transf"].timestamp_ == expected_ts
708703
# Create a new pipeline with cloned estimators
709704
# Check that even changing the name step does not affect the cache hit
710-
clf_2 = SVC(gamma="scale", probability=True, random_state=0)
705+
clf_2 = LogisticRegression(random_state=0)
711706
transf_2 = DummyTransf()
712707
cached_pipe_2 = Pipeline(
713-
[("transf_2", transf_2), ("svc", clf_2)], memory=memory
708+
[("transf_2", transf_2), ("clf", clf_2)], memory=memory
714709
)
715710
cached_pipe_2.fit(X, y)
716711

@@ -746,11 +741,11 @@ def test_pipeline_memory_sampler():
746741
cachedir = mkdtemp()
747742
try:
748743
memory = Memory(cachedir, verbose=10)
749-
# Test with Transformer + SVC
750-
clf = SVC(gamma="scale", probability=True, random_state=0)
744+
# Test with Sampler + LogisticRegression
745+
clf = LogisticRegression()
751746
transf = DummySampler()
752-
pipe = Pipeline([("transf", clone(transf)), ("svc", clf)])
753-
cached_pipe = Pipeline([("transf", transf), ("svc", clf)], memory=memory)
747+
pipe = Pipeline([("transf", clone(transf)), ("clf", clf)])
748+
cached_pipe = Pipeline([("transf", transf), ("clf", clf)], memory=memory)
754749

755750
# Memoize the transformer at the first fit
756751
cached_pipe.fit(X, y)
@@ -782,10 +777,10 @@ def test_pipeline_memory_sampler():
782777
assert cached_pipe.named_steps["transf"].timestamp_ == expected_ts
783778
# Create a new pipeline with cloned estimators
784779
# Check that even changing the name step does not affect the cache hit
785-
clf_2 = SVC(gamma="scale", probability=True, random_state=0)
780+
clf_2 = LogisticRegression(random_state=0)
786781
transf_2 = DummySampler()
787782
cached_pipe_2 = Pipeline(
788-
[("transf_2", transf_2), ("svc", clf_2)], memory=memory
783+
[("transf_2", transf_2), ("clf", clf_2)], memory=memory
789784
)
790785
cached_pipe_2.fit(X, y)
791786

@@ -820,11 +815,11 @@ def test_pipeline_methods_pca_rus_svm():
820815
random_state=0,
821816
)
822817

823-
# Test with PCA + SVC
824-
clf = SVC(gamma="scale", probability=True, random_state=0)
818+
# Test with PCA + LogisticRegression
819+
clf = LogisticRegression()
825820
pca = PCA()
826821
rus = RandomUnderSampler(random_state=0)
827-
pipe = Pipeline([("pca", pca), ("rus", rus), ("svc", clf)])
822+
pipe = Pipeline([("pca", pca), ("rus", rus), ("clf", clf)])
828823
pipe.fit(X, y)
829824
pipe.predict(X)
830825
pipe.predict_proba(X)
@@ -847,11 +842,11 @@ def test_pipeline_methods_rus_pca_svm():
847842
random_state=0,
848843
)
849844

850-
# Test with PCA + SVC
851-
clf = SVC(gamma="scale", probability=True, random_state=0)
845+
# Test with PCA + LogisticRegression
846+
clf = LogisticRegression()
852847
pca = PCA()
853848
rus = RandomUnderSampler(random_state=0)
854-
pipe = Pipeline([("rus", rus), ("pca", pca), ("svc", clf)])
849+
pipe = Pipeline([("rus", rus), ("pca", pca), ("clf", clf)])
855850
pipe.fit(X, y)
856851
pipe.predict(X)
857852
pipe.predict_proba(X)

imblearn/under_sampling/_prototype_selection/tests/test_random_under_sampler.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,4 +183,4 @@ def test_random_under_sampler_full_nat():
183183
assert X_res.shape == (2, 2)
184184
assert y_res.shape == (2,)
185185

186-
assert X_res["col_timedelta"].dtype == "timedelta64[ns]"
186+
assert X_res["col_timedelta"].dtype.kind == "m" # timedelta

imblearn/utils/_validation.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ def _transfrom_one(self, array, props):
7878
for col in ret.columns:
7979
if (
8080
ret[col].isnull().all()
81-
and ret[col].dtype == "datetime64[ns]"
82-
and props["dtypes"][col] == "timedelta64[ns]"
81+
and ret[col].dtype.kind == "M" # datetime64
82+
and props["dtypes"][col].kind == "m" # timedelta64
8383
):
8484
ret[col] = pd.to_timedelta(["NaT"] * len(ret[col]))
8585
# try again

0 commit comments

Comments
 (0)