Skip to content

Commit d55e27d

Browse files
authored
MAINT prepare release 0.14 (#1146)
1 parent 367fb90 commit d55e27d

17 files changed

+872
-1970
lines changed

README.rst

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,13 @@
2828
.. _Black: :target: https://github.com/psf/black
2929

3030
.. |PythonMinVersion| replace:: 3.10
31-
.. |NumPyMinVersion| replace:: 1.24.3
32-
.. |SciPyMinVersion| replace:: 1.10.1
33-
.. |ScikitLearnMinVersion| replace:: 1.3.2
31+
.. |NumPyMinVersion| replace:: 1.25.2
32+
.. |SciPyMinVersion| replace:: 1.11.4
33+
.. |ScikitLearnMinVersion| replace:: 1.4.2
3434
.. |MatplotlibMinVersion| replace:: 3.7.3
35-
.. |PandasMinVersion| replace:: 1.5.3
36-
.. |TensorflowMinVersion| replace:: 2.13.1
37-
.. |KerasMinVersion| replace:: 3.0.5
35+
.. |PandasMinVersion| replace:: 2.0.3
36+
.. |TensorflowMinVersion| replace:: 2.16.1
37+
.. |KerasMinVersion| replace:: 3.3.3
3838
.. |SeabornMinVersion| replace:: 0.12.2
3939
.. |PytestMinVersion| replace:: 7.2.2
4040

doc/install.rst

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@ Prerequisites
88
=============
99

1010
.. |PythonMinVersion| replace:: 3.10
11-
.. |NumPyMinVersion| replace:: 1.24.3
12-
.. |SciPyMinVersion| replace:: 1.10.1
13-
.. |ScikitLearnMinVersion| replace:: 1.3.2
11+
.. |NumPyMinVersion| replace:: 1.25.2
12+
.. |SciPyMinVersion| replace:: 1.11.4
13+
.. |ScikitLearnMinVersion| replace:: 1.4.2
1414
.. |MatplotlibMinVersion| replace:: 3.7.3
15-
.. |PandasMinVersion| replace:: 1.5.3
16-
.. |TensorflowMinVersion| replace:: 2.13.1
17-
.. |KerasMinVersion| replace:: 3.0.5
15+
.. |PandasMinVersion| replace:: 2.0.3
16+
.. |TensorflowMinVersion| replace:: 2.16.1
17+
.. |KerasMinVersion| replace:: 3.3.3
1818
.. |SeabornMinVersion| replace:: 0.12.2
1919
.. |PytestMinVersion| replace:: 7.2.2
2020

doc/whats_new/0.14.rst

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
.. _changes_0_14:
22

3-
Version 0.14.0 (Under development)
4-
==================================
3+
Version 0.14.0
4+
==============
55

6-
**TBD**
6+
**August 14, 2025**
77

88
Changelog
99
---------
@@ -21,5 +21,8 @@ Enhancements
2121
Compatibility
2222
.............
2323

24+
- Compatibility with scikit-learn 1.7
25+
:pr:`1137`, :pr:`1145`, :pr:`1146` by :user:`Guillaume Lemaitre <glemaitre>`.
26+
2427
Deprecations
2528
............

examples/model_selection/plot_instance_hardness_cv.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,15 @@
2828
from sklearn.datasets import make_blobs
2929

3030
X, y = make_blobs(n_samples=[950, 50], centers=((-3, 0), (3, 0)), random_state=10)
31-
plt.scatter(X[:, 0], X[:, 1], c=y)
31+
_ = plt.scatter(X[:, 0], X[:, 1], c=y)
3232

3333
# %%
3434
# To introduce instance hardness in our dataset, we add some hard to classify samples:
3535
X_hard, y_hard = make_blobs(
3636
n_samples=10, centers=((3, 0), (-3, 0)), cluster_std=1, random_state=10
3737
)
3838
X, y = np.vstack((X, X_hard)), np.hstack((y, y_hard))
39-
plt.scatter(X[:, 0], X[:, 1], c=y)
39+
_ = plt.scatter(X[:, 0], X[:, 1], c=y)
4040

4141
# %%
4242
# Compare cross validation scores using `StratifiedKFold` and `InstanceHardnessCV`
@@ -69,7 +69,7 @@
6969
results = {}
7070
for cv in (
7171
StratifiedKFold(n_splits=5, shuffle=True, random_state=10),
72-
InstanceHardnessCV(estimator=LogisticRegression(), n_splits=5, random_state=10),
72+
InstanceHardnessCV(estimator=LogisticRegression()),
7373
):
7474
result = cross_validate(
7575
logistic_regression,
@@ -83,7 +83,7 @@
8383

8484
# %%
8585
ax = results.plot.box(vert=False, whis=[0, 100])
86-
ax.set(
86+
_ = ax.set(
8787
xlabel="Average precision",
8888
title="Cross validation scores with different splitters",
8989
xlim=(0, 1),

imblearn/ensemble/_bagging.py

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,13 @@
1212
from sklearn.ensemble import BaggingClassifier
1313
from sklearn.tree import DecisionTreeClassifier
1414
from sklearn.utils._param_validation import HasMethods, Interval, StrOptions
15-
from sklearn.utils.fixes import parse_version
1615

1716
from ..pipeline import Pipeline
1817
from ..under_sampling import RandomUnderSampler
1918
from ..under_sampling.base import BaseUnderSampler
2019
from ..utils import Substitution, check_sampling_strategy, check_target_type
2120
from ..utils._docstring import _n_jobs_docstring, _random_state_docstring
22-
from ..utils._sklearn_compat import _fit_context, sklearn_version
23-
from ._common import _bagging_parameter_constraints
21+
from ..utils._sklearn_compat import _fit_context
2422

2523

2624
@Substitution(
@@ -224,11 +222,7 @@ class BalancedBaggingClassifier(BaggingClassifier):
224222
"""
225223

226224
# make a deepcopy to not modify the original dictionary
227-
if sklearn_version >= parse_version("1.4"):
228-
_parameter_constraints = copy.deepcopy(BaggingClassifier._parameter_constraints)
229-
else:
230-
_parameter_constraints = copy.deepcopy(_bagging_parameter_constraints)
231-
225+
_parameter_constraints = copy.deepcopy(BaggingClassifier._parameter_constraints)
232226
_parameter_constraints.update(
233227
{
234228
"sampling_strategy": [
@@ -241,9 +235,6 @@ class BalancedBaggingClassifier(BaggingClassifier):
241235
"sampler": [HasMethods(["fit_resample"]), None],
242236
}
243237
)
244-
# TODO: remove when minimum supported version of scikit-learn is 1.4
245-
if "base_estimator" in _parameter_constraints:
246-
del _parameter_constraints["base_estimator"]
247238

248239
def __init__(
249240
self,

imblearn/ensemble/_common.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,8 @@ def _estimator_has(attr):
1919
def check(self):
2020
if hasattr(self, "estimators_"):
2121
return hasattr(self.estimators_[0], attr)
22-
elif self.estimator is not None:
22+
else: # self.estimator is not None
2323
return hasattr(self.estimator, attr)
24-
else: # TODO(1.4): Remove when the base_estimator deprecation cycle ends
25-
return hasattr(self.base_estimator, attr)
2624

2725
return check
2826

@@ -45,11 +43,6 @@ def check(self):
4543
"n_jobs": [None, Integral],
4644
"random_state": ["random_state"],
4745
"verbose": ["verbose"],
48-
"base_estimator": [
49-
HasMethods(["fit", "predict"]),
50-
StrOptions({"deprecated"}),
51-
None,
52-
],
5346
}
5447

5548
_adaboost_classifier_parameter_constraints = {

imblearn/ensemble/_easy_ensemble.py

Lines changed: 1 addition & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,13 @@
55
# License: MIT
66

77
import copy
8-
import inspect
98
import numbers
109

1110
import numpy as np
1211
from sklearn.base import clone
1312
from sklearn.ensemble import AdaBoostClassifier, BaggingClassifier
14-
from sklearn.ensemble._bagging import _parallel_decision_function
15-
from sklearn.ensemble._base import _partition_estimators
1613
from sklearn.utils._param_validation import Interval, StrOptions
1714
from sklearn.utils.fixes import parse_version
18-
from sklearn.utils.metaestimators import available_if
19-
from sklearn.utils.parallel import Parallel, delayed
20-
from sklearn.utils.validation import check_is_fitted
2115

2216
from ..pipeline import Pipeline
2317
from ..under_sampling import RandomUnderSampler
@@ -28,9 +22,8 @@
2822
_fit_context,
2923
get_tags,
3024
sklearn_version,
31-
validate_data,
3225
)
33-
from ._common import _bagging_parameter_constraints, _estimator_has
26+
from ._common import _bagging_parameter_constraints
3427

3528
MAX_INT = np.iinfo(np.int32).max
3629

@@ -276,61 +269,6 @@ def _fit(self, X, y, max_samples=None, max_depth=None, sample_weight=None):
276269
# None.
277270
return super()._fit(X, y, self.max_samples)
278271

279-
# TODO: remove when minimum supported version of scikit-learn is 1.1
280-
@available_if(_estimator_has("decision_function"))
281-
def decision_function(self, X):
282-
"""Average of the decision functions of the base classifiers.
283-
284-
Parameters
285-
----------
286-
X : {array-like, sparse matrix} of shape (n_samples, n_features)
287-
The training input samples. Sparse matrices are accepted only if
288-
they are supported by the base estimator.
289-
290-
Returns
291-
-------
292-
score : ndarray of shape (n_samples, k)
293-
The decision function of the input samples. The columns correspond
294-
to the classes in sorted order, as they appear in the attribute
295-
``classes_``. Regression and binary classification are special
296-
cases with ``k == 1``, otherwise ``k==n_classes``.
297-
"""
298-
check_is_fitted(self)
299-
300-
# Check data
301-
X = validate_data(
302-
self,
303-
X=X,
304-
accept_sparse=["csr", "csc"],
305-
dtype=None,
306-
ensure_all_finite=(
307-
"allow_nan" if get_tags(self).input_tags.allow_nan else True
308-
),
309-
reset=False,
310-
)
311-
312-
# Parallel loop
313-
n_jobs, _, starts = _partition_estimators(self.n_estimators, self.n_jobs)
314-
315-
kwargs = {}
316-
if "params" in inspect.signature(_parallel_decision_function).parameters:
317-
kwargs["params"] = {}
318-
319-
all_decisions = Parallel(n_jobs=n_jobs, verbose=self.verbose)(
320-
delayed(_parallel_decision_function)(
321-
self.estimators_[starts[i] : starts[i + 1]],
322-
self.estimators_features_[starts[i] : starts[i + 1]],
323-
X,
324-
**kwargs,
325-
)
326-
for i in range(n_jobs)
327-
)
328-
329-
# Reduce
330-
decisions = sum(all_decisions) / self.n_estimators
331-
332-
return decisions
333-
334272
@property
335273
def base_estimator_(self):
336274
"""Attribute for older sklearn version compatibility."""

imblearn/ensemble/_forest.py

Lines changed: 15 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,9 @@ def _local_parallel_build_trees(
7474
"bootstrap": bootstrap,
7575
}
7676

77-
if sklearn_version >= parse_version("1.4"):
78-
# TODO: remove when the minimum supported version of scikit-learn will be 1.4
79-
# support for missing values
80-
params_parallel_build_trees["missing_values_in_feature_mask"] = (
81-
missing_values_in_feature_mask
82-
)
77+
params_parallel_build_trees["missing_values_in_feature_mask"] = (
78+
missing_values_in_feature_mask
79+
)
8380

8481
tree = _parallel_build_trees(**params_parallel_build_trees)
8582

@@ -469,20 +466,9 @@ def __init__(
469466
"min_impurity_decrease": min_impurity_decrease,
470467
"ccp_alpha": ccp_alpha,
471468
"max_samples": max_samples,
469+
"monotonic_cst": monotonic_cst,
472470
}
473-
# TODO: remove when the minimum supported version of scikit-learn will be 1.4
474-
if sklearn_version >= parse_version("1.4"):
475-
# use scikit-learn support for monotonic constraints
476-
params_random_forest["monotonic_cst"] = monotonic_cst
477-
else:
478-
if monotonic_cst is not None:
479-
raise ValueError(
480-
"Monotonic constraints are not supported for scikit-learn "
481-
"version < 1.4."
482-
)
483-
# create an attribute for compatibility with other scikit-learn tools such
484-
# as HTML representation.
485-
self.monotonic_cst = monotonic_cst
471+
486472
super().__init__(**params_random_forest)
487473

488474
self.sampling_strategy = sampling_strategy
@@ -548,37 +534,26 @@ def fit(self, X, y, sample_weight=None):
548534
if issparse(y):
549535
raise ValueError("sparse multilabel-indicator for y is not supported.")
550536

551-
# TODO: remove when the minimum supported version of scipy will be 1.4
552-
# Support for missing values
553-
if sklearn_version >= parse_version("1.4"):
554-
ensure_all_finite = False
555-
else:
556-
ensure_all_finite = True
557-
558537
X, y = validate_data(
559538
self,
560539
X=X,
561540
y=y,
562541
multi_output=True,
563542
accept_sparse="csc",
564543
dtype=DTYPE,
565-
ensure_all_finite=ensure_all_finite,
544+
ensure_all_finite=False,
566545
)
567546

568-
# TODO: remove when the minimum supported version of scikit-learn will be 1.4
569-
if sklearn_version >= parse_version("1.4"):
570-
# _compute_missing_values_in_feature_mask checks if X has missing values and
571-
# will raise an error if the underlying tree base estimator can't handle
572-
# missing values. Only the criterion is required to determine if the tree
573-
# supports missing values.
574-
estimator = type(self.estimator)(criterion=self.criterion)
575-
missing_values_in_feature_mask = (
576-
estimator._compute_missing_values_in_feature_mask(
577-
X, estimator_name=self.__class__.__name__
578-
)
547+
# _compute_missing_values_in_feature_mask checks if X has missing values and
548+
# will raise an error if the underlying tree base estimator can't handle
549+
# missing values. Only the criterion is required to determine if the tree
550+
# supports missing values.
551+
estimator = type(self.estimator)(criterion=self.criterion)
552+
missing_values_in_feature_mask = (
553+
estimator._compute_missing_values_in_feature_mask(
554+
X, estimator_name=self.__class__.__name__
579555
)
580-
else:
581-
missing_values_in_feature_mask = None
556+
)
582557

583558
if sample_weight is not None:
584559
sample_weight = _check_sample_weight(sample_weight, X)

imblearn/keras/_generator.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ def import_from_keras():
2020
if hasattr(keras.utils, "Sequence"):
2121
return (keras.utils.Sequence,), True
2222
else:
23-
return (keras.utils.data_utils.Sequence,), True
23+
return (keras.utils.PyDataset,), True
2424
except ImportError:
2525
return tuple(), False
2626

@@ -31,7 +31,7 @@ def import_from_tensforflow():
3131
if hasattr(keras.utils, "Sequence"):
3232
return (keras.utils.Sequence,), True
3333
else:
34-
return (keras.utils.data_utils.Sequence,), True
34+
return (keras.utils.PyDataset,), True
3535
except ImportError:
3636
return tuple(), False
3737

0 commit comments

Comments
 (0)