From 24925dc03c663a32f19b53bdbab3d73576c8dd1d Mon Sep 17 00:00:00 2001 From: Guillaume Lemaitre Date: Sun, 6 Oct 2024 18:26:33 +0200 Subject: [PATCH 1/7] update badge Azure -> GHA --- README.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index b15863bb3..fdccdbc4e 100644 --- a/README.rst +++ b/README.rst @@ -4,10 +4,10 @@ .. _scikit-learn-contrib: https://github.com/scikit-learn-contrib -|Azure|_ |Codecov|_ |CircleCI|_ |PythonVersion|_ |Pypi|_ |Gitter|_ |Black|_ +|GitHubActions|_ |Codecov|_ |CircleCI|_ |PythonVersion|_ |Pypi|_ |Gitter|_ |Black|_ -.. |Azure| image:: https://dev.azure.com/imbalanced-learn/imbalanced-learn/_apis/build/status/scikit-learn-contrib.imbalanced-learn?branchName=master -.. _Azure: https://dev.azure.com/imbalanced-learn/imbalanced-learn/_build +.. |GitHubActions| image:: https://github.com/scikit-learn-contrib/imbalanced-learn/actions/workflows/tests.yml/badge.svg +.. _GitHubActions: https://github.com/scikit-learn-contrib/imbalanced-learn/actions/workflows/tests.yml .. |Codecov| image:: https://codecov.io/gh/scikit-learn-contrib/imbalanced-learn/branch/master/graph/badge.svg .. _Codecov: https://codecov.io/gh/scikit-learn-contrib/imbalanced-learn From dbfbd6e6d0434c9fffb55592856f18897e3a4a0e Mon Sep 17 00:00:00 2001 From: Guillaume Lemaitre Date: Sun, 6 Oct 2024 18:52:39 +0200 Subject: [PATCH 2/7] remove dead code --- imblearn/base.py | 9 +- imblearn/ensemble/_bagging.py | 19 +--- imblearn/ensemble/_easy_ensemble.py | 19 +--- imblearn/ensemble/_forest.py | 16 +-- .../_smote/tests/test_smote_nc.py | 10 +- imblearn/tests/test_docstring_parameters.py | 18 ++-- imblearn/tests/test_pipeline.py | 3 - imblearn/utils/_available_if.py | 99 ------------------- imblearn/utils/_param_validation.py | 4 +- imblearn/utils/estimator_checks.py | 16 +-- 10 files changed, 21 insertions(+), 192 deletions(-) delete mode 100644 imblearn/utils/_available_if.py diff --git a/imblearn/base.py b/imblearn/base.py index 0b2d94e84..889587c42 100644 --- a/imblearn/base.py +++ b/imblearn/base.py @@ -8,14 +8,7 @@ import numpy as np import sklearn -from sklearn.base import BaseEstimator - -try: - # scikit-learn >= 1.2 - from sklearn.base import OneToOneFeatureMixin -except ImportError: - from sklearn.base import _OneToOneFeatureMixin as OneToOneFeatureMixin - +from sklearn.base import BaseEstimator, OneToOneFeatureMixin from sklearn.preprocessing import label_binarize from sklearn.utils.fixes import parse_version from sklearn.utils.multiclass import check_classification_targets diff --git a/imblearn/ensemble/_bagging.py b/imblearn/ensemble/_bagging.py index f75d954a9..b05ce2e30 100644 --- a/imblearn/ensemble/_bagging.py +++ b/imblearn/ensemble/_bagging.py @@ -14,24 +14,17 @@ from sklearn.ensemble import BaggingClassifier from sklearn.ensemble._bagging import _parallel_decision_function from sklearn.ensemble._base import _partition_estimators -from sklearn.exceptions import NotFittedError from sklearn.tree import DecisionTreeClassifier from sklearn.utils.fixes import parse_version +from sklearn.utils.metaestimators import available_if +from sklearn.utils.parallel import Parallel, delayed from sklearn.utils.validation import check_is_fitted -try: - # scikit-learn >= 1.2 - from sklearn.utils.parallel import Parallel, delayed -except (ImportError, ModuleNotFoundError): - from joblib import Parallel - from sklearn.utils.fixes import delayed - from ..base import _ParamsValidationMixin from ..pipeline import Pipeline from ..under_sampling import RandomUnderSampler from ..under_sampling.base import BaseUnderSampler from ..utils import Substitution, check_sampling_strategy, check_target_type -from ..utils._available_if import available_if from ..utils._docstring import _n_jobs_docstring, _random_state_docstring from ..utils._param_validation import HasMethods, Interval, StrOptions from ..utils.fixes import _fit_context @@ -443,14 +436,6 @@ def base_estimator_(self): error = AttributeError( f"{self.__class__.__name__} object has no attribute 'base_estimator_'." ) - if sklearn_version < parse_version("1.2"): - # The base class require to have the attribute defined. For scikit-learn - # > 1.2, we are going to raise an error. - try: - check_is_fitted(self) - return self.estimator_ - except NotFittedError: - raise error raise error def _more_tags(self): diff --git a/imblearn/ensemble/_easy_ensemble.py b/imblearn/ensemble/_easy_ensemble.py index fe393c8b0..0825e9fa1 100644 --- a/imblearn/ensemble/_easy_ensemble.py +++ b/imblearn/ensemble/_easy_ensemble.py @@ -14,24 +14,17 @@ from sklearn.ensemble import AdaBoostClassifier, BaggingClassifier from sklearn.ensemble._bagging import _parallel_decision_function from sklearn.ensemble._base import _partition_estimators -from sklearn.exceptions import NotFittedError from sklearn.utils._tags import _safe_tags from sklearn.utils.fixes import parse_version +from sklearn.utils.metaestimators import available_if +from sklearn.utils.parallel import Parallel, delayed from sklearn.utils.validation import check_is_fitted -try: - # scikit-learn >= 1.2 - from sklearn.utils.parallel import Parallel, delayed -except (ImportError, ModuleNotFoundError): - from joblib import Parallel - from sklearn.utils.fixes import delayed - from ..base import _ParamsValidationMixin from ..pipeline import Pipeline from ..under_sampling import RandomUnderSampler from ..under_sampling.base import BaseUnderSampler from ..utils import Substitution, check_sampling_strategy, check_target_type -from ..utils._available_if import available_if from ..utils._docstring import _n_jobs_docstring, _random_state_docstring from ..utils._param_validation import Interval, StrOptions from ..utils.fixes import _fit_context @@ -357,14 +350,6 @@ def base_estimator_(self): error = AttributeError( f"{self.__class__.__name__} object has no attribute 'base_estimator_'." ) - if sklearn_version < parse_version("1.2"): - # The base class require to have the attribute defined. For scikit-learn - # > 1.2, we are going to raise an error. - try: - check_is_fitted(self) - return self.estimator_ - except NotFittedError: - raise error raise error def _get_estimator(self): diff --git a/imblearn/ensemble/_forest.py b/imblearn/ensemble/_forest.py index 9d72be8cf..77e45afce 100644 --- a/imblearn/ensemble/_forest.py +++ b/imblearn/ensemble/_forest.py @@ -25,15 +25,9 @@ from sklearn.utils import _safe_indexing, check_random_state from sklearn.utils.fixes import parse_version from sklearn.utils.multiclass import type_of_target +from sklearn.utils.parallel import Parallel, delayed from sklearn.utils.validation import _check_sample_weight -try: - # scikit-learn >= 1.2 - from sklearn.utils.parallel import Parallel, delayed -except (ImportError, ModuleNotFoundError): - from joblib import Parallel - from sklearn.utils.fixes import delayed - from ..base import _ParamsValidationMixin from ..pipeline import make_pipeline from ..under_sampling import RandomUnderSampler @@ -80,6 +74,7 @@ def _local_parallel_build_trees( "verbose": verbose, "class_weight": class_weight, "n_samples_bootstrap": n_samples_bootstrap, + "bootstrap": bootstrap, } if parse_version(sklearn_version.base_version) >= parse_version("1.4"): @@ -89,13 +84,6 @@ def _local_parallel_build_trees( missing_values_in_feature_mask ) - # TODO: remove when the minimum supported version of scikit-learn will be 1.1 - # change of signature in scikit-learn 1.1 - if parse_version(sklearn_version.base_version) >= parse_version("1.1"): - params_parallel_build_trees["bootstrap"] = bootstrap - else: - params_parallel_build_trees["forest"] = forest - tree = _parallel_build_trees(**params_parallel_build_trees) return sampler, tree diff --git a/imblearn/over_sampling/_smote/tests/test_smote_nc.py b/imblearn/over_sampling/_smote/tests/test_smote_nc.py index 1314ea98b..dce2c143a 100644 --- a/imblearn/over_sampling/_smote/tests/test_smote_nc.py +++ b/imblearn/over_sampling/_smote/tests/test_smote_nc.py @@ -272,21 +272,17 @@ def test_smote_nc_with_null_median_std(): def test_smotenc_categorical_encoder(): """Check that we can pass our own categorical encoder.""" - # TODO: only use `sparse_output` when sklearn >= 1.2 - param = "sparse" if sklearn_version < parse_version("1.2") else "sparse_output" - X, y, categorical_features = data_heterogneous_unordered() smote = SMOTENC(categorical_features=categorical_features, random_state=0) smote.fit_resample(X, y) - assert getattr(smote.categorical_encoder_, param) is True + assert getattr(smote.categorical_encoder_, "sparse_output") is True - encoder = OneHotEncoder() - encoder.set_params(**{param: False}) + encoder = OneHotEncoder(sparse_output=False) smote.set_params(categorical_encoder=encoder).fit_resample(X, y) assert smote.categorical_encoder is encoder assert smote.categorical_encoder_ is not encoder - assert getattr(smote.categorical_encoder_, param) is False + assert getattr(smote.categorical_encoder_, "sparse_output") is False # TODO(0.13): remove this test diff --git a/imblearn/tests/test_docstring_parameters.py b/imblearn/tests/test_docstring_parameters.py index 1bd6ecf51..03b66b3cb 100644 --- a/imblearn/tests/test_docstring_parameters.py +++ b/imblearn/tests/test_docstring_parameters.py @@ -16,18 +16,12 @@ check_docstring_parameters, ignore_warnings, ) -from sklearn.utils.estimator_checks import _enforce_estimator_tags_y - -try: - from sklearn.utils.estimator_checks import _enforce_estimator_tags_x -except ImportError: - # scikit-learn >= 1.2 - from sklearn.utils.estimator_checks import ( - _enforce_estimator_tags_X as _enforce_estimator_tags_x, - ) - from sklearn.utils.deprecation import _is_deprecated -from sklearn.utils.estimator_checks import _construct_instance +from sklearn.utils.estimator_checks import ( + _construct_instance, + _enforce_estimator_tags_X, + _enforce_estimator_tags_y, +) import imblearn from imblearn.base import is_sampler @@ -197,7 +191,7 @@ def test_fit_docstring_attributes(name, Estimator): ) y = _enforce_estimator_tags_y(est, y) - X = _enforce_estimator_tags_x(est, X) + X = _enforce_estimator_tags_X(est, X) if "oob_score" in est.get_params(): est.set_params(bootstrap=True, oob_score=True) diff --git a/imblearn/tests/test_pipeline.py b/imblearn/tests/test_pipeline.py index d89e03a11..3af63e2a4 100644 --- a/imblearn/tests/test_pipeline.py +++ b/imblearn/tests/test_pipeline.py @@ -1338,9 +1338,6 @@ def test_pipeline_param_validation(): check_param_validation("Pipeline", model) -@pytest.mark.skipif( - sklearn_version < parse_version("1.2"), reason="requires scikit-learn >= 1.2" -) def test_pipeline_with_set_output(): pd = pytest.importorskip("pandas") X, y = load_iris(return_X_y=True, as_frame=True) diff --git a/imblearn/utils/_available_if.py b/imblearn/utils/_available_if.py deleted file mode 100644 index bca75e735..000000000 --- a/imblearn/utils/_available_if.py +++ /dev/null @@ -1,99 +0,0 @@ -"""This is a copy of sklearn/utils/_available_if.py. It can be removed when -we support scikit-learn >= 1.1. -""" -# mypy: ignore-errors - -from functools import update_wrapper, wraps -from types import MethodType - -import sklearn -from sklearn.utils.fixes import parse_version - -sklearn_version = parse_version(sklearn.__version__) - -if sklearn_version < parse_version("1.1"): - - class _AvailableIfDescriptor: - """Implements a conditional property using the descriptor protocol. - - Using this class to create a decorator will raise an ``AttributeError`` - if check(self) returns a falsey value. Note that if check raises an error - this will also result in hasattr returning false. - - See https://docs.python.org/3/howto/descriptor.html for an explanation of - descriptors. - """ - - def __init__(self, fn, check, attribute_name): - self.fn = fn - self.check = check - self.attribute_name = attribute_name - - # update the docstring of the descriptor - update_wrapper(self, fn) - - def __get__(self, obj, owner=None): - attr_err = AttributeError( - f"This {owner.__name__!r} has no attribute {self.attribute_name!r}" - ) - if obj is not None: - # delegate only on instances, not the classes. - # this is to allow access to the docstrings. - if not self.check(obj): - raise attr_err - out = MethodType(self.fn, obj) - - else: - # This makes it possible to use the decorated method as an - # unbound method, for instance when monkeypatching. - @wraps(self.fn) - def out(*args, **kwargs): - if not self.check(args[0]): - raise attr_err - return self.fn(*args, **kwargs) - - return out - - def available_if(check): - """An attribute that is available only if check returns a truthy value. - - Parameters - ---------- - check : callable - When passed the object with the decorated method, this should return - a truthy value if the attribute is available, and either return False - or raise an AttributeError if not available. - - Returns - ------- - callable - Callable makes the decorated method available if `check` returns - a truthy value, otherwise the decorated method is unavailable. - - Examples - -------- - >>> from sklearn.utils.metaestimators import available_if - >>> class HelloIfEven: - ... def __init__(self, x): - ... self.x = x - ... - ... def _x_is_even(self): - ... return self.x % 2 == 0 - ... - ... @available_if(_x_is_even) - ... def say_hello(self): - ... print("Hello") - ... - >>> obj = HelloIfEven(1) - >>> hasattr(obj, "say_hello") - False - >>> obj.x = 2 - >>> hasattr(obj, "say_hello") - True - >>> obj.say_hello() - Hello - """ - return lambda fn: _AvailableIfDescriptor(fn, check, attribute_name=fn.__name__) - -else: - from sklearn.utils.metaestimators import available_if # noqa diff --git a/imblearn/utils/_param_validation.py b/imblearn/utils/_param_validation.py index 3ccabf2eb..bf74ff811 100644 --- a/imblearn/utils/_param_validation.py +++ b/imblearn/utils/_param_validation.py @@ -1,6 +1,4 @@ -"""This is a copy of sklearn/utils/_param_validation.py. It can be removed when -we support scikit-learn >= 1.2. -""" +"""This is a copy of sklearn/utils/_param_validation.py.""" # mypy: ignore-errors import functools import math diff --git a/imblearn/utils/estimator_checks.py b/imblearn/utils/estimator_checks.py index 04d886111..6e8a61b21 100644 --- a/imblearn/utils/estimator_checks.py +++ b/imblearn/utils/estimator_checks.py @@ -35,19 +35,11 @@ set_random_state, ) from sklearn.utils.estimator_checks import ( + _enforce_estimator_tags_X, _enforce_estimator_tags_y, _get_check_estimator_ids, _maybe_mark_xfail, ) - -try: - from sklearn.utils.estimator_checks import _enforce_estimator_tags_x -except ImportError: - # scikit-learn >= 1.2 - from sklearn.utils.estimator_checks import ( - _enforce_estimator_tags_X as _enforce_estimator_tags_x, - ) - from sklearn.utils.fixes import parse_version from sklearn.utils.multiclass import type_of_target @@ -602,7 +594,7 @@ def check_dataframe_column_names_consistency(name, estimator_orig): X_orig = rng.normal(size=(150, 8)) - X_orig = _enforce_estimator_tags_x(estimator, X_orig) + X_orig = _enforce_estimator_tags_X(estimator, X_orig) n_samples, n_features = X_orig.shape names = np.array([f"col_{i}" for i in range(n_features)]) @@ -756,7 +748,7 @@ def check_sampler_get_feature_names_out(name, sampler_orig): X = StandardScaler().fit_transform(X) sampler = clone(sampler_orig) - X = _enforce_estimator_tags_x(sampler, X) + X = _enforce_estimator_tags_X(sampler, X) n_features = X.shape[1] set_random_state(sampler) @@ -804,7 +796,7 @@ def check_sampler_get_feature_names_out_pandas(name, sampler_orig): X = StandardScaler().fit_transform(X) sampler = clone(sampler_orig) - X = _enforce_estimator_tags_x(sampler, X) + X = _enforce_estimator_tags_X(sampler, X) n_features = X.shape[1] set_random_state(sampler) From e8213874b881ad9b8622de66c2af71291444fb4c Mon Sep 17 00:00:00 2001 From: Guillaume Lemaitre Date: Sun, 6 Oct 2024 18:59:56 +0200 Subject: [PATCH 3/7] remove dead code --- imblearn/base.py | 4 ---- imblearn/ensemble/tests/test_bagging.py | 3 --- imblearn/ensemble/tests/test_easy_ensemble.py | 3 --- imblearn/ensemble/tests/test_weight_boosting.py | 4 ---- imblearn/over_sampling/_smote/tests/test_smote_nc.py | 4 ---- imblearn/tests/test_pipeline.py | 4 ---- imblearn/utils/_param_validation.py | 2 +- imblearn/utils/fixes.py | 12 ------------ 8 files changed, 1 insertion(+), 35 deletions(-) diff --git a/imblearn/base.py b/imblearn/base.py index 889587c42..6e3954532 100644 --- a/imblearn/base.py +++ b/imblearn/base.py @@ -7,18 +7,14 @@ from abc import ABCMeta, abstractmethod import numpy as np -import sklearn from sklearn.base import BaseEstimator, OneToOneFeatureMixin from sklearn.preprocessing import label_binarize -from sklearn.utils.fixes import parse_version from sklearn.utils.multiclass import check_classification_targets from .utils import check_sampling_strategy, check_target_type from .utils._param_validation import validate_parameter_constraints from .utils._validation import ArraysTransformer -sklearn_version = parse_version(sklearn.__version__) - class _ParamsValidationMixin: """Mixin class to validate parameters.""" diff --git a/imblearn/ensemble/tests/test_bagging.py b/imblearn/ensemble/tests/test_bagging.py index 382597183..921c88816 100644 --- a/imblearn/ensemble/tests/test_bagging.py +++ b/imblearn/ensemble/tests/test_bagging.py @@ -7,7 +7,6 @@ import numpy as np import pytest -import sklearn from sklearn.cluster import KMeans from sklearn.datasets import load_iris, make_classification, make_hastie_10_2 from sklearn.dummy import DummyClassifier @@ -22,7 +21,6 @@ assert_array_almost_equal, assert_array_equal, ) -from sklearn.utils.fixes import parse_version from imblearn import FunctionSampler from imblearn.datasets import make_imbalance @@ -31,7 +29,6 @@ from imblearn.pipeline import make_pipeline from imblearn.under_sampling import ClusterCentroids, RandomUnderSampler -sklearn_version = parse_version(sklearn.__version__) iris = load_iris() diff --git a/imblearn/ensemble/tests/test_easy_ensemble.py b/imblearn/ensemble/tests/test_easy_ensemble.py index 7dc04414a..b216cd184 100644 --- a/imblearn/ensemble/tests/test_easy_ensemble.py +++ b/imblearn/ensemble/tests/test_easy_ensemble.py @@ -5,20 +5,17 @@ import numpy as np import pytest -import sklearn from sklearn.datasets import load_iris, make_hastie_10_2 from sklearn.ensemble import AdaBoostClassifier from sklearn.feature_selection import SelectKBest from sklearn.model_selection import GridSearchCV, train_test_split from sklearn.utils._testing import assert_allclose, assert_array_equal -from sklearn.utils.fixes import parse_version from imblearn.datasets import make_imbalance from imblearn.ensemble import EasyEnsembleClassifier from imblearn.pipeline import make_pipeline from imblearn.under_sampling import RandomUnderSampler -sklearn_version = parse_version(sklearn.__version__) iris = load_iris() # Generate a global dataset to use diff --git a/imblearn/ensemble/tests/test_weight_boosting.py b/imblearn/ensemble/tests/test_weight_boosting.py index ad3dbca04..8096a2b16 100644 --- a/imblearn/ensemble/tests/test_weight_boosting.py +++ b/imblearn/ensemble/tests/test_weight_boosting.py @@ -1,15 +1,11 @@ import numpy as np import pytest -import sklearn from sklearn.datasets import make_classification from sklearn.model_selection import train_test_split from sklearn.utils._testing import assert_array_equal -from sklearn.utils.fixes import parse_version from imblearn.ensemble import RUSBoostClassifier -sklearn_version = parse_version(sklearn.__version__) - @pytest.fixture def imbalanced_dataset(): diff --git a/imblearn/over_sampling/_smote/tests/test_smote_nc.py b/imblearn/over_sampling/_smote/tests/test_smote_nc.py index dce2c143a..2d1e2b3f0 100644 --- a/imblearn/over_sampling/_smote/tests/test_smote_nc.py +++ b/imblearn/over_sampling/_smote/tests/test_smote_nc.py @@ -8,12 +8,10 @@ import numpy as np import pytest -import sklearn from scipy import sparse from sklearn.datasets import make_classification from sklearn.preprocessing import OneHotEncoder from sklearn.utils._testing import assert_allclose, assert_array_equal -from sklearn.utils.fixes import parse_version from imblearn.over_sampling import SMOTENC from imblearn.utils.estimator_checks import ( @@ -21,8 +19,6 @@ check_param_validation, ) -sklearn_version = parse_version(sklearn.__version__) - def data_heterogneous_ordered(): rng = np.random.RandomState(42) diff --git a/imblearn/tests/test_pipeline.py b/imblearn/tests/test_pipeline.py index 3af63e2a4..ed90b263c 100644 --- a/imblearn/tests/test_pipeline.py +++ b/imblearn/tests/test_pipeline.py @@ -13,7 +13,6 @@ import numpy as np import pytest -import sklearn from joblib import Memory from pytest import raises from sklearn.base import BaseEstimator, clone @@ -31,7 +30,6 @@ assert_array_almost_equal, assert_array_equal, ) -from sklearn.utils.fixes import parse_version from imblearn.datasets import make_imbalance from imblearn.pipeline import Pipeline, make_pipeline @@ -39,8 +37,6 @@ from imblearn.under_sampling import RandomUnderSampler from imblearn.utils.estimator_checks import check_param_validation -sklearn_version = parse_version(sklearn.__version__) - JUNK_FOOD_DOCS = ( "the pizza pizza beer copyright", "the pizza burger beer copyright", diff --git a/imblearn/utils/_param_validation.py b/imblearn/utils/_param_validation.py index bf74ff811..c8941876e 100644 --- a/imblearn/utils/_param_validation.py +++ b/imblearn/utils/_param_validation.py @@ -13,9 +13,9 @@ import sklearn from scipy.sparse import csr_matrix, issparse from sklearn.utils.fixes import parse_version +from sklearn.utils.validation import _is_arraylike_not_scalar from .._config import config_context, get_config -from ..utils.fixes import _is_arraylike_not_scalar sklearn_version = parse_version(sklearn.__version__) diff --git a/imblearn/utils/fixes.py b/imblearn/utils/fixes.py index 023d8a152..9e4852566 100644 --- a/imblearn/utils/fixes.py +++ b/imblearn/utils/fixes.py @@ -7,7 +7,6 @@ import functools import sys -import numpy as np import scipy import scipy.stats import sklearn @@ -26,17 +25,6 @@ def _mode(a, axis=0): return scipy.stats.mode(a, axis=axis) -# TODO: Remove when scikit-learn 1.1 is the minimum supported version -if sklearn_version >= parse_version("1.1"): - from sklearn.utils.validation import _is_arraylike_not_scalar -else: - from sklearn.utils.validation import _is_arraylike - - def _is_arraylike_not_scalar(array): - """Return True if array is array-like and not a scalar""" - return _is_arraylike(array) and not np.isscalar(array) - - # TODO: remove when scikit-learn minimum version is 1.3 if sklearn_version < parse_version("1.3"): From 493e619a563b1d08e84c4fdbb6bf7d898fdc1fc0 Mon Sep 17 00:00:00 2001 From: Guillaume Lemaitre Date: Sun, 6 Oct 2024 22:32:32 +0200 Subject: [PATCH 4/7] iter --- README.rst | 1 + imblearn/_min_dependencies.py | 61 -------- imblearn/ensemble/_bagging.py | 23 --- imblearn/ensemble/_easy_ensemble.py | 8 -- imblearn/ensemble/_forest.py | 36 +---- imblearn/ensemble/tests/test_bagging.py | 8 -- imblearn/ensemble/tests/test_easy_ensemble.py | 8 -- imblearn/ensemble/tests/test_forest.py | 12 +- imblearn/utils/estimator_checks.py | 40 ++---- imblearn/utils/tests/test_min_dependencies.py | 42 +++++- pixi.lock | 131 +++++++++--------- pyproject.toml | 2 + 12 files changed, 118 insertions(+), 254 deletions(-) delete mode 100644 imblearn/_min_dependencies.py diff --git a/README.rst b/README.rst index fdccdbc4e..f68c1473b 100644 --- a/README.rst +++ b/README.rst @@ -66,6 +66,7 @@ Dependencies - NumPy (>= |NumPyMinVersion|) - SciPy (>= |SciPyMinVersion|) - Scikit-learn (>= |ScikitLearnMinVersion|) +- Pytest (>= |PytestMinVersion|) Additionally, `imbalanced-learn` requires the following optional dependencies: diff --git a/imblearn/_min_dependencies.py b/imblearn/_min_dependencies.py deleted file mode 100644 index f1eac4fd8..000000000 --- a/imblearn/_min_dependencies.py +++ /dev/null @@ -1,61 +0,0 @@ -"""All minimum dependencies for imbalanced-learn.""" -import argparse - -NUMPY_MIN_VERSION = "1.24.3" -SCIPY_MIN_VERSION = "1.10.1" -PANDAS_MIN_VERSION = "1.5.3" -SKLEARN_MIN_VERSION = "1.2.2" -TENSORFLOW_MIN_VERSION = "2.13.1" -KERAS_MIN_VERSION = "3.0.5" -JOBLIB_MIN_VERSION = "1.1.1" -THREADPOOLCTL_MIN_VERSION = "2.0.0" -PYTEST_MIN_VERSION = "7.2.2" - -# 'build' and 'install' is included to have structured metadata for CI. -# It will NOT be included in setup's extras_require -# The values are (version_spec, comma separated tags) -dependent_packages = { - "numpy": (NUMPY_MIN_VERSION, "install"), - "scipy": (SCIPY_MIN_VERSION, "install"), - "scikit-learn": (SKLEARN_MIN_VERSION, "install"), - "joblib": (JOBLIB_MIN_VERSION, "install"), - "threadpoolctl": (THREADPOOLCTL_MIN_VERSION, "install"), - "pandas": (PANDAS_MIN_VERSION, "optional, docs, examples, tests"), - "tensorflow": (TENSORFLOW_MIN_VERSION, "optional, docs, examples, tests"), - "keras": (KERAS_MIN_VERSION, "optional, docs, examples, tests"), - "matplotlib": ("3.7.3", "docs, examples"), - "seaborn": ("0.12.2", "docs, examples"), - "memory_profiler": ("0.61.0", "docs"), - "numpydoc": ("1.5.0", "docs, tests"), - "pytest": (PYTEST_MIN_VERSION, "tests"), - "pytest-cov": ("4.1.0", "tests"), - "pytest-xdist": ("3.5.0", "tests"), - "black": ("23.3.0", "tests"), - "ruff": ("0.4.8", "tests"), - "mypy": ("1.3.0", "tests"), - "sphinx": ("8.0.2", "docs"), - "sphinx-gallery": ("0.13.0", "docs"), - "sphinx-copybutton": ("0.5.2", "docs"), - "sphinxcontrib-bibtex": ("2.4.1", "docs"), - "pydata-sphinx-theme": ("0.15.4", "docs"), - "sphinx-design": ("0.6.1", "docs"), -} - - -# create inverse mapping for setuptools -tag_to_packages: dict = { - extra: [] for extra in ["install", "optional", "docs", "examples", "tests"] -} -for package, (min_version, extras) in dependent_packages.items(): - for extra in extras.split(", "): - tag_to_packages[extra].append("{}>={}".format(package, min_version)) - - -# Used by CI to get the min dependencies -if __name__ == "__main__": - parser = argparse.ArgumentParser(description="Get min dependencies for a package") - - parser.add_argument("package", choices=dependent_packages) - args = parser.parse_args() - min_version = dependent_packages[args.package][0] - print(min_version) diff --git a/imblearn/ensemble/_bagging.py b/imblearn/ensemble/_bagging.py index b05ce2e30..79559cd2c 100644 --- a/imblearn/ensemble/_bagging.py +++ b/imblearn/ensemble/_bagging.py @@ -6,7 +6,6 @@ import copy import numbers -import warnings import numpy as np import sklearn @@ -121,14 +120,6 @@ class BalancedBaggingClassifier(_ParamsValidationMixin, BaggingClassifier): .. versionadded:: 0.10 - n_features_ : int - The number of features when `fit` is performed. - - .. deprecated:: 1.0 - `n_features_` is deprecated in `scikit-learn` 1.0 and will be removed - in version 1.2. When the minimum version of `scikit-learn` supported - by `imbalanced-learn` will reach 1.2, this attribute will be removed. - estimators_ : list of estimators The collection of fitted base estimators. @@ -331,20 +322,6 @@ def _validate_estimator(self, default=DecisionTreeClassifier()): [("sampler", self.sampler_), ("classifier", estimator)] ) - # TODO: remove when supporting scikit-learn>=1.2 - @property - def n_features_(self): - """Number of features when ``fit`` is performed.""" - warnings.warn( - ( - "`n_features_` was deprecated in scikit-learn 1.0. This attribute will " - "not be accessible when the minimum supported version of scikit-learn " - "is 1.2." - ), - FutureWarning, - ) - return self.n_features_in_ - @_fit_context(prefer_skip_nested_validation=False) def fit(self, X, y): """Build a Bagging ensemble of estimators from the training set (X, y). diff --git a/imblearn/ensemble/_easy_ensemble.py b/imblearn/ensemble/_easy_ensemble.py index 0825e9fa1..aec7f6837 100644 --- a/imblearn/ensemble/_easy_ensemble.py +++ b/imblearn/ensemble/_easy_ensemble.py @@ -100,14 +100,6 @@ class EasyEnsembleClassifier(_ParamsValidationMixin, BaggingClassifier): n_classes_ : int or list The number of classes. - n_features_ : int - The number of features when `fit` is performed. - - .. deprecated:: 1.0 - `n_features_` is deprecated in `scikit-learn` 1.0 and will be removed - in version 1.2. When the minimum version of `scikit-learn` supported - by `imbalanced-learn` will reach 1.2, this attribute will be removed. - n_features_in_ : int Number of features in the input dataset. diff --git a/imblearn/ensemble/_forest.py b/imblearn/ensemble/_forest.py index 77e45afce..587db01d8 100644 --- a/imblearn/ensemble/_forest.py +++ b/imblearn/ensemble/_forest.py @@ -343,14 +343,6 @@ class labels (multi-output problem). The number of classes (single output problem), or a list containing the number of classes for each output (multi-output problem). - n_features_ : int - The number of features when `fit` is performed. - - .. deprecated:: 1.0 - `n_features_` is deprecated in `scikit-learn` 1.0 and will be removed - in version 1.2. When the minimum version of `scikit-learn` supported - by `imbalanced-learn` will reach 1.2, this attribute will be removed. - n_features_in_ : int Number of features in the input dataset. @@ -502,13 +494,8 @@ def __init__( def _validate_estimator(self, default=DecisionTreeClassifier()): """Check the estimator and the n_estimator attribute, set the `estimator_` attribute.""" - if hasattr(self, "estimator"): - base_estimator = self.estimator - else: - base_estimator = self.base_estimator - - if base_estimator is not None: - self.estimator_ = clone(base_estimator) + if self.estimator is not None: + self.estimator_ = clone(self.estimator) else: self.estimator_ = clone(default) @@ -893,22 +880,5 @@ def _compute_oob_predictions(self, X, y): return oob_pred - # TODO: remove when supporting scikit-learn>=1.2 - @property - def n_features_(self): - """Number of features when ``fit`` is performed.""" - warn( - ( - "`n_features_` was deprecated in scikit-learn 1.0. This attribute will " - "not be accessible when the minimum supported version of scikit-learn " - "is 1.2." - ), - FutureWarning, - ) - return self.n_features_in_ - def _more_tags(self): - return { - "multioutput": False, - "multilabel": False, - } + return {"multioutput": False, "multilabel": False} diff --git a/imblearn/ensemble/tests/test_bagging.py b/imblearn/ensemble/tests/test_bagging.py index 921c88816..640007757 100644 --- a/imblearn/ensemble/tests/test_bagging.py +++ b/imblearn/ensemble/tests/test_bagging.py @@ -581,11 +581,3 @@ def roughly_balanced_bagging(X, y, replace=False): for estimator in rbb.estimators_: class_counts = estimator[-1].class_counts_ assert (class_counts[0] / class_counts[1]) > 0.78 - - -def test_balanced_bagging_classifier_n_features(): - """Check that we raise a FutureWarning when accessing `n_features_`.""" - X, y = load_iris(return_X_y=True) - estimator = BalancedBaggingClassifier().fit(X, y) - with pytest.warns(FutureWarning, match="`n_features_` was deprecated"): - estimator.n_features_ diff --git a/imblearn/ensemble/tests/test_easy_ensemble.py b/imblearn/ensemble/tests/test_easy_ensemble.py index b216cd184..6fe50500f 100644 --- a/imblearn/ensemble/tests/test_easy_ensemble.py +++ b/imblearn/ensemble/tests/test_easy_ensemble.py @@ -222,11 +222,3 @@ def test_easy_ensemble_classifier_grid_search(): cv=5, ) grid_search.fit(X, y) - - -def test_easy_ensemble_classifier_n_features(): - """Check that we raise a FutureWarning when accessing `n_features_`.""" - X, y = load_iris(return_X_y=True) - estimator = EasyEnsembleClassifier().fit(X, y) - with pytest.warns(FutureWarning, match="`n_features_` was deprecated"): - estimator.n_features_ diff --git a/imblearn/ensemble/tests/test_forest.py b/imblearn/ensemble/tests/test_forest.py index 3719568e5..792824531 100644 --- a/imblearn/ensemble/tests/test_forest.py +++ b/imblearn/ensemble/tests/test_forest.py @@ -1,7 +1,7 @@ import numpy as np import pytest import sklearn -from sklearn.datasets import load_iris, make_classification +from sklearn.datasets import make_classification from sklearn.model_selection import GridSearchCV, train_test_split from sklearn.utils._testing import assert_allclose, assert_array_equal from sklearn.utils.fixes import parse_version @@ -219,16 +219,6 @@ def test_balanced_random_forest_oob_binomial(ratio): assert np.abs(erf.oob_score_ - 0.5) < 0.1 -def test_balanced_bagging_classifier_n_features(): - """Check that we raise a FutureWarning when accessing `n_features_`.""" - X, y = load_iris(return_X_y=True) - estimator = BalancedRandomForestClassifier( - sampling_strategy="all", replacement=True, bootstrap=False - ).fit(X, y) - with pytest.warns(FutureWarning, match="`n_features_` was deprecated"): - estimator.n_features_ - - # TODO: remove in 0.13 def test_balanced_random_forest_change_behaviour(imbalanced_dataset): """Check that we raise a change of behaviour for the parameters `sampling_strategy` diff --git a/imblearn/utils/estimator_checks.py b/imblearn/utils/estimator_checks.py index 6e8a61b21..271df753b 100644 --- a/imblearn/utils/estimator_checks.py +++ b/imblearn/utils/estimator_checks.py @@ -73,10 +73,7 @@ def _set_checking_parameters(estimator): if "n_estimators" in params: estimator.set_params(n_estimators=min(5, estimator.n_estimators)) if name == "ClusterCentroids": - if sklearn_version < parse_version("1.1"): - algorithm = "full" - else: - algorithm = "lloyd" + algorithm = "lloyd" estimator.set_params( voting="soft", estimator=KMeans(random_state=0, algorithm=algorithm, n_init=1), @@ -689,33 +686,14 @@ def check_dataframe_column_names_consistency(name, estimator_orig): X_bad = pd.DataFrame(X, columns=invalid_name) for name, method in check_methods: - if sklearn_version >= parse_version("1.2"): - expected_msg = re.escape( - "The feature names should match those that were passed during fit." - f"\n{additional_message}" - ) - with raises( - ValueError, match=expected_msg, err_msg=f"{name} did not raise" - ): - method(X_bad) - else: - expected_msg = re.escape( - "The feature names should match those that were passed " - "during fit. Starting version 1.2, an error will be raised.\n" - f"{additional_message}" - ) - with warnings.catch_warnings(): - warnings.filterwarnings( - "error", - category=FutureWarning, - module="sklearn", - ) - with raises( - FutureWarning, - match=expected_msg, - err_msg=f"{name} did not raise", - ): - method(X_bad) + expected_msg = re.escape( + "The feature names should match those that were passed during fit." + f"\n{additional_message}" + ) + with raises( + ValueError, match=expected_msg, err_msg=f"{name} did not raise" + ): + method(X_bad) # partial_fit checks on second call # Do not call partial fit if early_stopping is on diff --git a/imblearn/utils/tests/test_min_dependencies.py b/imblearn/utils/tests/test_min_dependencies.py index cd537030c..7a9acd397 100644 --- a/imblearn/utils/tests/test_min_dependencies.py +++ b/imblearn/utils/tests/test_min_dependencies.py @@ -1,14 +1,16 @@ """Tests for the minimum dependencies in the README.rst file.""" + import os import platform import re from pathlib import Path import pytest -from sklearn.utils.fixes import parse_version +import tomllib +from packaging.requirements import Requirement +from packaging.version import parse import imblearn -from imblearn._min_dependencies import dependent_packages @pytest.mark.skipif( @@ -17,7 +19,35 @@ def test_min_dependencies_readme(): # Test that the minimum dependencies in the README.rst file are # consistent with the minimum dependencies defined at the file: - # imblearn/_min_dependencies.py + # pyproject.toml + + pyproject_path = Path(imblearn.__path__[0]).parents[0] / "pyproject.toml" + with open(pyproject_path, "rb") as f: + pyproject_data = tomllib.load(f) + + def process_requirements(requirements): + result = {} + for req in requirements: + req = Requirement(req) + for specifier in req.specifier: + if specifier.operator == ">=": + result[req.name] = parse(specifier.version) + return result + + min_dependencies = process_requirements( + [f"python{pyproject_data['project']['requires-python']}"] + ) + min_dependencies.update( + process_requirements(pyproject_data["project"]["dependencies"]) + ) + + markers = ["docs", "optional", "tensorflow", "keras", "tests"] + for marker_name in markers: + min_dependencies.update( + process_requirements( + pyproject_data["project"]["optional-dependencies"][marker_name] + ) + ) pattern = re.compile( r"(\.\. \|)" @@ -44,8 +74,8 @@ def test_min_dependencies_readme(): package, version = matched.group(2), matched.group(5) package = package.lower() - if package in dependent_packages: - version = parse_version(version) - min_version = parse_version(dependent_packages[package][0]) + if package in min_dependencies: + version = parse(version) + min_version = min_dependencies[package] assert version == min_version, f"{package} has a mismatched version" diff --git a/pixi.lock b/pixi.lock index 9a7715de1..f3b64c5e6 100644 --- a/pixi.lock +++ b/pixi.lock @@ -2770,7 +2770,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang13-19.1.0-default_h9c6a7e4_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-h4637d8d_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.10.1-hbbe4b11_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.21-h4bc722e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.22-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.123-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20191231-he28a2e2_2.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_1.conda @@ -2807,7 +2807,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.0-h0841786_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.1.0-hc0a3c3a_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.1.0-h4852527_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.0-h6565414_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.0-he137b08_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.4.0-hd590300_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda @@ -3090,7 +3090,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.9.0-22_osx64_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcurl-8.10.1-h58e7537_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-19.1.1-hf95d169_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-64/libdeflate-1.21-hfdf4475_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libdeflate-1.22-h00291cd_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libedit-3.1.20191231-h0678c8f_2.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/osx-64/libev-4.33-h10d778d_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.6.3-hac325c4_0.conda @@ -3108,7 +3108,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/libsodium-1.0.20-hfdf4475_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.46.1-h4b8f8c9_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libssh2-1.11.0-hd019ec5_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-64/libtiff-4.7.0-h5f227bf_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libtiff-4.7.0-h583c2ba_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libwebp-base-1.4.0-h10d778d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libxcb-1.17.0-hf1f96e2_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.1-hd23fc13_2.conda @@ -3359,7 +3359,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.9.0-24_osxarm64_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.10.1-h13a7ad3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-19.1.1-ha82da77_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.21-h99b78c6_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.22-hd74edd7_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20191231-hc8eb9b7_2.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h93a5062_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.6.3-hf9b8971_0.conda @@ -3377,7 +3377,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsodium-1.0.20-h99b78c6_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.46.1-hc14010f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.0-h7a5bd25_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.0-h9c1d414_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.0-hfce79cd_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.4.0-h93a5062_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxcb-1.17.0-hdb1d25a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda @@ -3608,7 +3608,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang13-19.1.0-default_h9c6a7e4_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-h4637d8d_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.10.1-hbbe4b11_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.21-h4bc722e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.22-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.123-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20191231-he28a2e2_2.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_1.conda @@ -3644,7 +3644,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.0-h0841786_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.1.0-hc0a3c3a_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.1.0-h4852527_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.0-h6565414_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.0-he137b08_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.4.0-hd590300_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda @@ -3831,7 +3831,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.9.0-22_osx64_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcurl-8.10.1-h58e7537_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-19.1.1-hf95d169_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-64/libdeflate-1.21-hfdf4475_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libdeflate-1.22-h00291cd_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libedit-3.1.20191231-h0678c8f_2.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/osx-64/libev-4.33-h10d778d_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.6.3-hac325c4_0.conda @@ -3848,7 +3848,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/libre2-11-2023.09.01-h81f5012_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.46.1-h4b8f8c9_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libssh2-1.11.0-hd019ec5_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-64/libtiff-4.7.0-h5f227bf_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libtiff-4.7.0-h583c2ba_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libwebp-base-1.4.0-h10d778d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libxcb-1.17.0-hf1f96e2_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.1-hd23fc13_2.conda @@ -4001,7 +4001,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.9.0-24_osxarm64_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.10.1-h13a7ad3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-19.1.1-ha82da77_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.21-h99b78c6_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.22-hd74edd7_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20191231-hc8eb9b7_2.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h93a5062_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.6.3-hf9b8971_0.conda @@ -4018,7 +4018,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2023.09.01-h7b2c953_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.46.1-hc14010f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.0-h7a5bd25_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.0-h9c1d414_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.0-hfce79cd_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.4.0-h93a5062_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxcb-1.17.0-hdb1d25a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda @@ -8413,7 +8413,7 @@ packages: name: imbalanced-learn version: 0.13.0.dev0 path: . - sha256: 4b3127262d1e08d7e06274f80b8dc1dd8f7b94cb2529aa9f5bea1a07eebf9d6b + sha256: 6c5d43d5dc04feb801c87f245b32d35e26a70b8dafe7d9a2a8603abdb8f2c9b6 requires_dist: - numpy<3,>=1.24.3 - scipy<2,>=1.10.1 @@ -8442,6 +8442,7 @@ packages: - pre-commit ; extra == 'linters' - pandas<3,>=1.5.3 ; extra == 'optional' - tensorflow<3,>=2.13.1 ; extra == 'tensorflow' + - packaging<25,>=23.2 ; extra == 'tests' - pytest<9,>=7.2.2 ; extra == 'tests' - pytest-cov<6,>=4.1.0 ; extra == 'tests' - pytest-xdist<4,>=3.5.0 ; extra == 'tests' @@ -10289,50 +10290,47 @@ packages: timestamp: 1727863581528 - kind: conda name: libdeflate - version: '1.21' - build: h4bc722e_0 - subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.21-h4bc722e_0.conda - sha256: 728c24ce835700bfdfdf106bf04233fdb040a61ca4ecfd3f41b46fa90cd4f971 - md5: 36ce76665bf67f5aac36be7a0d21b7f3 + version: '1.22' + build: h00291cd_0 + subdir: osx-64 + url: https://conda.anaconda.org/conda-forge/osx-64/libdeflate-1.22-h00291cd_0.conda + sha256: 681035346974c3315685dc40898e26f65f1c00cbb0b5fd80cc2599e207a34b31 + md5: a15785ccc62ae2a8febd299424081efb depends: - - __glibc >=2.17,<3.0.a0 - - libgcc-ng >=12 + - __osx >=10.13 license: MIT - license_family: MIT purls: [] - size: 71163 - timestamp: 1722820138782 + size: 70407 + timestamp: 1728177128525 - kind: conda name: libdeflate - version: '1.21' - build: h99b78c6_0 - subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.21-h99b78c6_0.conda - sha256: 243ca6d733954df9522eb9da24f5fe58da7ac19a2ca9438fd4abef5bb2cd1f83 - md5: 67d666c1516be5a023c3aaa85867099b + version: '1.22' + build: hb9d3cd8_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.22-hb9d3cd8_0.conda + sha256: 780f0530a3adfc1497ba49d626931c6afc978c540e1abfde6ccd57128ded6ad6 + md5: b422943d5d772b7cc858b36ad2a92db5 depends: - - __osx >=11.0 + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 license: MIT - license_family: MIT purls: [] - size: 54533 - timestamp: 1722820240854 + size: 72242 + timestamp: 1728177071251 - kind: conda name: libdeflate - version: '1.21' - build: hfdf4475_0 - subdir: osx-64 - url: https://conda.anaconda.org/conda-forge/osx-64/libdeflate-1.21-hfdf4475_0.conda - sha256: 1defb3e5243a74a9ef64de2a47812f524664e46ca9dbecb8d7c746cb1779038e - md5: 88409b23a5585c15d52de0073f3c9c61 + version: '1.22' + build: hd74edd7_0 + subdir: osx-arm64 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.22-hd74edd7_0.conda + sha256: 3552894ca62bebc33d05982937cda25a4fa19e56a82af2ff20944ff4c2532fda + md5: 2d3e3f3d8ab315748420ef58d5a3ae0f depends: - - __osx >=10.13 + - __osx >=11.0 license: MIT - license_family: MIT purls: [] - size: 70570 - timestamp: 1722820232914 + size: 54089 + timestamp: 1728177149927 - kind: conda name: libdrm version: 2.4.123 @@ -11954,16 +11952,17 @@ packages: - kind: conda name: libtiff version: 4.7.0 - build: h5f227bf_0 + build: h583c2ba_1 + build_number: 1 subdir: osx-64 - url: https://conda.anaconda.org/conda-forge/osx-64/libtiff-4.7.0-h5f227bf_0.conda - sha256: 7a348f9e5833b3ade5036eb12e8ba8b4f4518413ee777ab6666766bb93db98d1 - md5: 2ae42f38aacee5eda6c541cad957e703 + url: https://conda.anaconda.org/conda-forge/osx-64/libtiff-4.7.0-h583c2ba_1.conda + sha256: 4d58c695dfed6f308d0fd3ff552e0078bb98bc0be2ea0bf55820eb6e86fa5355 + md5: 4b78bcdcc8780cede8b3d090deba874d depends: - __osx >=10.13 - lerc >=4.0.0,<5.0a0 - libcxx >=17 - - libdeflate >=1.21,<1.22.0a0 + - libdeflate >=1.22,<1.23.0a0 - libjpeg-turbo >=3.0.0,<4.0a0 - libwebp-base >=1.4.0,<2.0a0 - libzlib >=1.3.1,<2.0a0 @@ -11971,20 +11970,21 @@ packages: - zstd >=1.5.6,<1.6.0a0 license: HPND purls: [] - size: 395973 - timestamp: 1726667328916 + size: 395980 + timestamp: 1728232302162 - kind: conda name: libtiff version: 4.7.0 - build: h6565414_0 + build: he137b08_1 + build_number: 1 subdir: linux-64 - url: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.0-h6565414_0.conda - sha256: f50a0516ec5bbe6270f1a44feb8dae15626c62166d6c02a013bb0e5982eb0dd8 - md5: 80eaf80d84668fa5620ac9ec1b4bf56f + url: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.0-he137b08_1.conda + sha256: 9890121db85f6ef463fe12eb04ef1471176e3ef3b5e2d62e8d6dac713df00df4 + md5: 63872517c98aa305da58a757c443698e depends: - __glibc >=2.17,<3.0.a0 - lerc >=4.0.0,<5.0a0 - - libdeflate >=1.21,<1.22.0a0 + - libdeflate >=1.22,<1.23.0a0 - libgcc >=13 - libjpeg-turbo >=3.0.0,<4.0a0 - libstdcxx >=13 @@ -11994,21 +11994,22 @@ packages: - zstd >=1.5.6,<1.6.0a0 license: HPND purls: [] - size: 428159 - timestamp: 1726667242674 + size: 428156 + timestamp: 1728232228989 - kind: conda name: libtiff version: 4.7.0 - build: h9c1d414_0 + build: hfce79cd_1 + build_number: 1 subdir: osx-arm64 - url: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.0-h9c1d414_0.conda - sha256: 2fb2d204de0ef47518587da769a0dfb114cce4ae4d4ba3b60a9f59d9759f9800 - md5: 5f8f92ddf488a4cd50f9f5a9c4ff27c4 + url: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.0-hfce79cd_1.conda + sha256: 97ba24c74750b6e731b3fe0d2a751cda6148b4937d2cc3f72d43bf7b3885c39d + md5: b9abf45f7c64caf3303725f1aa0e9a4d depends: - __osx >=11.0 - lerc >=4.0.0,<5.0a0 - libcxx >=17 - - libdeflate >=1.21,<1.22.0a0 + - libdeflate >=1.22,<1.23.0a0 - libjpeg-turbo >=3.0.0,<4.0a0 - libwebp-base >=1.4.0,<2.0a0 - libzlib >=1.3.1,<2.0a0 @@ -12016,8 +12017,8 @@ packages: - zstd >=1.5.6,<1.6.0a0 license: HPND purls: [] - size: 367224 - timestamp: 1726667500299 + size: 366323 + timestamp: 1728232400072 - kind: conda name: libuuid version: 2.38.1 diff --git a/pyproject.toml b/pyproject.toml index 840d8deb2..ac97b0ecf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -71,6 +71,7 @@ keras = [ "keras>=3.0.5,<4", ] tests = [ + "packaging>=23.2,<25", "pytest>=7.2.2,<9", "pytest-cov>=4.1.0,<6", "pytest-xdist>=3.5.0,<4", @@ -172,6 +173,7 @@ python = "~=3.11.0" python = "~=3.12.0" [tool.pixi.feature.tests.dependencies] +packaging = ">=23.2,<25" pytest-cov = ">=4.1.0,<6" pytest-xdist = ">=3.5.0,<4" From 09570a3c53642e2bb5cabbca1f92cdd97990fb82 Mon Sep 17 00:00:00 2001 From: Guillaume Lemaitre Date: Sun, 6 Oct 2024 22:36:05 +0200 Subject: [PATCH 5/7] ite --- imblearn/utils/tests/test_min_dependencies.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/imblearn/utils/tests/test_min_dependencies.py b/imblearn/utils/tests/test_min_dependencies.py index 7a9acd397..ff2fe9cb9 100644 --- a/imblearn/utils/tests/test_min_dependencies.py +++ b/imblearn/utils/tests/test_min_dependencies.py @@ -6,7 +6,6 @@ from pathlib import Path import pytest -import tomllib from packaging.requirements import Requirement from packaging.version import parse @@ -14,9 +13,13 @@ @pytest.mark.skipif( - platform.system() == "Windows", reason="This test is enough on unix system" + platform.system() == "Windows" or parse(platform.python_version()) < parse("3.11"), + reason="This test is enough on unix system and requires Python >= 3.11", ) def test_min_dependencies_readme(): + # local import to not import the file with Python < 3.11 + import tomllib + # Test that the minimum dependencies in the README.rst file are # consistent with the minimum dependencies defined at the file: # pyproject.toml From e42f6a53c37772a5f3add80ab64e73b645c0e89c Mon Sep 17 00:00:00 2001 From: Guillaume Lemaitre Date: Sun, 6 Oct 2024 22:54:25 +0200 Subject: [PATCH 6/7] iter --- imblearn/utils/estimator_checks.py | 49 ++++---- imblearn/utils/tests/test_estimator_checks.py | 2 +- pixi.lock | 116 ++---------------- pyproject.toml | 4 +- 4 files changed, 35 insertions(+), 136 deletions(-) diff --git a/imblearn/utils/estimator_checks.py b/imblearn/utils/estimator_checks.py index 271df753b..fc58c321c 100644 --- a/imblearn/utils/estimator_checks.py +++ b/imblearn/utils/estimator_checks.py @@ -12,7 +12,6 @@ from functools import partial import numpy as np -import pytest import sklearn from scipy import sparse from sklearn.base import clone, is_classifier, is_regressor @@ -30,7 +29,6 @@ SkipTest, assert_allclose, assert_array_equal, - assert_raises_regex, raises, set_random_state, ) @@ -62,11 +60,6 @@ def sample_dataset_generator(): return X, y -@pytest.fixture(name="sample_dataset_generator") -def sample_dataset_generator_fixture(): - return sample_dataset_generator() - - def _set_checking_parameters(estimator): params = estimator.get_params() name = estimator.__class__.__name__ @@ -166,6 +159,7 @@ def parametrize_with_checks(estimators): ... def test_sklearn_compatible_estimator(estimator, check): ... check(estimator) """ + import pytest def checks_generator(): for estimator in estimators: @@ -185,24 +179,14 @@ def check_target_type(name, estimator_orig): X = np.random.random((20, 2)) y = np.linspace(0, 1, 20) msg = "Unknown label type:" - assert_raises_regex( - ValueError, - msg, - estimator.fit_resample, - X, - y, - ) + with raises(ValueError, err_msg=msg): + estimator.fit_resample(X, y) # if the target is multilabel then we should raise an error rng = np.random.RandomState(42) y = rng.randint(2, size=(20, 3)) msg = "Multilabel and multioutput targets are not supported." - assert_raises_regex( - ValueError, - msg, - estimator.fit_resample, - X, - y, - ) + with raises(ValueError, err_msg=msg): + estimator.fit_resample(X, y) def check_samplers_one_label(name, sampler_orig): @@ -303,7 +287,12 @@ def check_samplers_sparse(name, sampler_orig): def check_samplers_pandas_sparse(name, sampler_orig): - pd = pytest.importorskip("pandas") + try: + import pandas as pd + except ImportError: + raise SkipTest( + "pandas is not installed: not checking column name consistency for pandas" + ) sampler = clone(sampler_orig) # Check that the samplers handle pandas dataframe and pandas series X, y = sample_dataset_generator() @@ -331,7 +320,12 @@ def check_samplers_pandas_sparse(name, sampler_orig): def check_samplers_pandas(name, sampler_orig): - pd = pytest.importorskip("pandas") + try: + import pandas as pd + except ImportError: + raise SkipTest( + "pandas is not installed: not checking column name consistency for pandas" + ) sampler = clone(sampler_orig) # Check that the samplers handle pandas dataframe and pandas series X, y = sample_dataset_generator() @@ -451,14 +445,19 @@ def check_classifier_on_multilabel_or_multioutput_targets(name, estimator_orig): estimator = clone(estimator_orig) X, y = make_multilabel_classification(n_samples=30) msg = "Multilabel and multioutput targets are not supported." - with pytest.raises(ValueError, match=msg): + with raises(ValueError, match=msg): estimator.fit(X, y) def check_classifiers_with_encoded_labels(name, classifier_orig): # Non-regression test for #709 # https://github.com/scikit-learn-contrib/imbalanced-learn/issues/709 - pd = pytest.importorskip("pandas") + try: + import pandas as pd + except ImportError: + raise SkipTest( + "pandas is not installed: not checking column name consistency for pandas" + ) classifier = clone(classifier_orig) iris = load_iris(as_frame=True) df, y = iris.data, iris.target diff --git a/imblearn/utils/tests/test_estimator_checks.py b/imblearn/utils/tests/test_estimator_checks.py index ca704f222..32e9c6723 100644 --- a/imblearn/utils/tests/test_estimator_checks.py +++ b/imblearn/utils/tests/test_estimator_checks.py @@ -98,7 +98,7 @@ def test_check_samplers_nan(): mapping_estimator_error = { - "BaseBadSampler": (AssertionError, "ValueError not raised by fit"), + "BaseBadSampler": (AssertionError, None), "SamplerSingleClass": (AssertionError, "Sampler can't balance when only"), "NotFittedSampler": (AssertionError, "No fitted attribute"), "NoAcceptingSparseSampler": (TypeError, "dense data is required"), diff --git a/pixi.lock b/pixi.lock index f3b64c5e6..7a151ce5f 100644 --- a/pixi.lock +++ b/pixi.lock @@ -9,7 +9,6 @@ environments: linux-64: - conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-24.2.0-pyh71513ae_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py310hf71b8c6_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2024.8.30-hbcca054_0.conda @@ -54,7 +53,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pooch-1.8.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha2e5f31_6.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-7.2.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.6.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.10.15-h4a871b0_1_cpython.conda @@ -75,7 +74,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.6-ha6fb4c9_0.conda - pypi: . osx-64: - - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-24.2.0-pyh71513ae_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/brotli-python-1.1.0-py310h53e7c6a_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-hfdf4475_7.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ca-certificates-2024.8.30-h8857fd0_0.conda @@ -112,7 +110,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pooch-1.8.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha2e5f31_6.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-7.2.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.6.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.10.15-h6bafeb6_1_cpython.conda @@ -133,7 +131,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/zstd-1.5.6-h915ae27_0.conda - pypi: . osx-arm64: - - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-24.2.0-pyh71513ae_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.1.0-py310hb4ad77e_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h99b78c6_7.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ca-certificates-2024.8.30-hf0a4a13_0.conda @@ -170,7 +167,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pooch-1.8.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha2e5f31_6.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-7.2.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.6.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.10.15-h7d35d02_1_cpython.conda @@ -191,7 +188,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.6-hb46c0d2_0.conda - pypi: . win-64: - - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-24.2.0-pyh71513ae_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-python-1.1.0-py310h9e98ed7_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h2466b09_7.conda - conda: https://conda.anaconda.org/conda-forge/win-64/ca-certificates-2024.8.30-h56e8100_0.conda @@ -234,7 +230,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/pthreads-win32-2.9.1-hfa6e2cd_3.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyh0701188_6.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-7.2.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.6.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.10.15-hfaddaf0_1_cpython.conda @@ -480,7 +476,6 @@ environments: linux-64: - conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-24.2.0-pyh71513ae_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py310hf71b8c6_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2024.8.30-hbcca054_0.conda @@ -526,7 +521,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pooch-1.8.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha2e5f31_6.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-7.2.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.6.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.10.15-h4a871b0_1_cpython.conda @@ -550,7 +545,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.6-ha6fb4c9_0.conda - pypi: . osx-64: - - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-24.2.0-pyh71513ae_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/brotli-python-1.1.0-py310h53e7c6a_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-hfdf4475_7.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ca-certificates-2024.8.30-h8857fd0_0.conda @@ -588,7 +582,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pooch-1.8.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha2e5f31_6.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-7.2.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.6.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.10.15-h6bafeb6_1_cpython.conda @@ -612,7 +606,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/zstd-1.5.6-h915ae27_0.conda - pypi: . osx-arm64: - - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-24.2.0-pyh71513ae_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.1.0-py310hb4ad77e_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h99b78c6_7.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ca-certificates-2024.8.30-hf0a4a13_0.conda @@ -650,7 +643,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pooch-1.8.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha2e5f31_6.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-7.2.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.6.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.10.15-h7d35d02_1_cpython.conda @@ -674,7 +667,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.6-hb46c0d2_0.conda - pypi: . win-64: - - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-24.2.0-pyh71513ae_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-python-1.1.0-py310h9e98ed7_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h2466b09_7.conda - conda: https://conda.anaconda.org/conda-forge/win-64/ca-certificates-2024.8.30-h56e8100_0.conda @@ -718,7 +710,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/pthreads-win32-2.9.1-hfa6e2cd_3.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyh0701188_6.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-7.2.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.6.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.10.15-hfaddaf0_1_cpython.conda @@ -2483,9 +2475,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2024.8.30-hbcca054_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.4.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.43-h712a8e2_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-24_linux64_openblas.conda @@ -2510,9 +2499,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-he02047a_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-1.26.4-py312heda63a1_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.3.2-hb9d3cd8_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.5.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.7-hc5c86c4_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.12-5_cp312.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8228510_1.conda @@ -2521,16 +2507,12 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-75.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.5.0-pyhc1e730c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h4845f30_101.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2024b-hc8b5060_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xz-5.2.6-h166bdaf_0.tar.bz2 - pypi: . osx-64: - conda: https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-hfdf4475_7.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ca-certificates-2024.8.30-h8857fd0_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.4.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.9.0-22_osx64_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.9.0-22_osx64_openblas.conda @@ -2547,9 +2529,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.5-hf036a51_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/numpy-1.26.4-py312he3a82b2_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.3.2-hd23fc13_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.5.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.12.7-h8f8b54e_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python_abi-3.12-5_cp312.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/readline-8.2-h9e318b2_1.conda @@ -2558,16 +2537,12 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-75.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.5.0-pyhc1e730c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-h1abcd95_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2024b-hc8b5060_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/xz-5.2.6-h775f41a_0.tar.bz2 - pypi: . osx-arm64: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h99b78c6_7.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ca-certificates-2024.8.30-hf0a4a13_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.4.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.9.0-24_osxarm64_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.9.0-24_osxarm64_openblas.conda @@ -2584,9 +2559,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h7bae524_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-1.26.4-py312h8442bc7_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.3.2-h8359307_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.5.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.7-h739c21a_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python_abi-3.12-5_cp312.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.2-h92ec313_1.conda @@ -2595,16 +2567,12 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-75.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.5.0-pyhc1e730c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h5083fa2_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2024b-hc8b5060_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xz-5.2.6-h57fd34a_0.tar.bz2 - pypi: . win-64: - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h2466b09_7.conda - conda: https://conda.anaconda.org/conda-forge/win-64/ca-certificates-2024.8.30-h56e8100_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_0.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/intel-openmp-2024.2.1-h57928b3_1083.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.4.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.9.0-24_win64_mkl.conda @@ -2620,10 +2588,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2024.1.0-h66d3029_694.conda - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-1.26.4-py312h8753938_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.3.2-h2466b09_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.5.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pthreads-win32-2.9.1-hfa6e2cd_3.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.12.7-hce54a09_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python_abi-3.12-5_cp312.conda - conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.5.2-py312h816cc57_1.conda @@ -2632,7 +2597,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2021.13.0-hc790b64_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.5.0-pyhc1e730c_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h5226925_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2024b-hc8b5060_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.22621.0-h57928b3_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/win-64/vc-14.3-h8a93ad2_21.conda @@ -3559,7 +3523,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.13.6-h5008d03_3.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/docutils-0.21.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.3.0-h59595ed_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/expat-2.6.3-h5888daf_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/flatbuffers-24.3.25-h59595ed_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 @@ -3586,7 +3549,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/imagesize-1.4.1-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.4.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/keras-3.5.0-pyhd8ed1ab_1.conda @@ -3679,7 +3641,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.44-hba22ea6_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-10.4.0-py312h56024de_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pixman-0.43.2-h59595ed_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.5.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/protobuf-4.25.3-py312h83439f5_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-6.0.0-py312h66e93f0_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda @@ -3691,7 +3652,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.1.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.7.3-py312h91f0f75_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha2e5f31_6.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.7-hc5c86c4_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-flatbuffers-24.3.25-pyh59ac667_0.conda @@ -3796,7 +3756,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/contourpy-1.3.0-py312hc5c4d5f_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/docutils-0.21.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/flatbuffers-24.3.25-h73e2aa4_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/fonttools-4.54.1-py312hb553811_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/freetype-2.12.1-h60636b9_2.conda @@ -3813,7 +3772,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/imagesize-1.4.1-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.4.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/keras-3.5.0-pyhd8ed1ab_1.conda @@ -3874,7 +3832,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/pandas-2.2.3-py312h98e817e_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/patsy-0.5.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/pillow-10.4.0-py312h683ea77_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.5.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/protobuf-4.25.3-py312hd13efa9_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/psutil-6.0.0-py312hb553811_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/pthread-stubs-0.4-h00291cd_1002.conda @@ -3885,7 +3842,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.1.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha2e5f31_6.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.12.7-h8f8b54e_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-flatbuffers-24.3.25-pyh59ac667_0.conda @@ -3966,7 +3922,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/contourpy-1.3.0-py312h6142ec9_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/docutils-0.21.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/flatbuffers-24.3.25-hebf3989_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fonttools-4.54.1-py312h024a12e_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.12.1-hadb7bae_2.conda @@ -3983,7 +3938,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/imagesize-1.4.1-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.4.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/keras-3.5.0-pyhd8ed1ab_1.conda @@ -4044,7 +3998,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-2.2.3-py312hcd31e36_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/patsy-0.5.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-10.4.0-py312h8609ca0_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.5.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/protobuf-4.25.3-py312he4aa971_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/psutil-6.0.0-py312h024a12e_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-hd74edd7_1002.conda @@ -4055,7 +4008,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.1.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha2e5f31_6.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.7-h739c21a_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-flatbuffers-24.3.25-pyh59ac667_0.conda @@ -4129,12 +4081,9 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-1.17.1-py311hf29c0ef_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cfgv-3.3.1-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.1.7-unix_pyh707e725_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.3.8-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.16.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/identify-2.6.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.4.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.43-h712a8e2_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-24_linux64_openblas.conda @@ -4164,10 +4113,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.3.6-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.5.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pre-commit-4.0.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.11.10-hc5c86c4_2_cpython.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.11-5_cp311.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.2-py311h9ecbd09_1.conda @@ -4178,7 +4125,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-75.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.5.0-pyhc1e730c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h4845f30_101.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2024b-hc8b5060_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ukkonen-1.0.1-py311hd18a35c_5.conda - conda: https://conda.anaconda.org/conda-forge/noarch/virtualenv-20.26.6-pyhd8ed1ab_0.conda @@ -4192,12 +4138,9 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/cffi-1.17.1-py311h137bacd_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cfgv-3.3.1-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.1.7-unix_pyh707e725_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.3.8-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.16.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/identify-2.6.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.4.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.9.0-22_osx64_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.9.0-22_osx64_openblas.conda @@ -4219,10 +4162,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.3.6-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.5.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pre-commit-4.0.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.11.10-h8f8b54e_2_cpython.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python_abi-3.11-5_cp311.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/pyyaml-6.0.2-py311h3336109_1.conda @@ -4233,7 +4174,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-75.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.5.0-pyhc1e730c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-h1abcd95_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2024b-hc8b5060_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ukkonen-1.0.1-py311hf2f7c97_5.conda - conda: https://conda.anaconda.org/conda-forge/noarch/virtualenv-20.26.6-pyhd8ed1ab_0.conda @@ -4247,12 +4187,9 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-1.17.1-py311h3a79f62_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cfgv-3.3.1-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.1.7-unix_pyh707e725_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.3.8-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.16.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/identify-2.6.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.4.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.9.0-24_osxarm64_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.9.0-24_osxarm64_openblas.conda @@ -4274,10 +4211,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.3.6-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.5.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pre-commit-4.0.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.11.10-h739c21a_2_cpython.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python_abi-3.11-5_cp311.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.2-py311h460d6c5_1.conda @@ -4288,7 +4223,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-75.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.5.0-pyhc1e730c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h5083fa2_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2024b-hc8b5060_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ukkonen-1.0.1-py311h2c37856_5.conda - conda: https://conda.anaconda.org/conda-forge/noarch/virtualenv-20.26.6-pyhd8ed1ab_0.conda @@ -4304,10 +4238,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.1.7-win_pyh7428d3b_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.3.8-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.16.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/identify-2.6.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/intel-openmp-2024.2.1-h57928b3_1083.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.4.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.9.0-24_win64_mkl.conda @@ -4328,11 +4260,9 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.3.6-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.5.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pre-commit-4.0.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pthreads-win32-2.9.1-hfa6e2cd_3.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.11.10-hce54a09_2_cpython.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python_abi-3.11-5_cp311.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.2-py311he736701_1.conda @@ -4343,7 +4273,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2021.13.0-hc790b64_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.5.0-pyhc1e730c_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h5226925_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2024b-hc8b5060_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.22621.0-h57928b3_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/win-64/ukkonen-1.0.1-py311h3257749_5.conda @@ -8413,14 +8342,13 @@ packages: name: imbalanced-learn version: 0.13.0.dev0 path: . - sha256: 6c5d43d5dc04feb801c87f245b32d35e26a70b8dafe7d9a2a8603abdb8f2c9b6 + sha256: d8c21bd48db96b9d5a14cd694ad42f90c64d55f356e5cd3a8b70111ff59af47a requires_dist: - numpy<3,>=1.24.3 - scipy<2,>=1.10.1 - scikit-learn<2,>=1.2.2 - joblib<2,>=1.1.1 - threadpoolctl<4,>=2.0.0 - - pytest<9,>=7.2.2 - ipykernel ; extra == 'dev' - ipython ; extra == 'dev' - jupyterlab ; extra == 'dev' @@ -15981,32 +15909,6 @@ packages: - pkg:pypi/pysocks?source=hash-mapping size: 18981 timestamp: 1661604969727 -- kind: conda - name: pytest - version: 7.2.2 - build: pyhd8ed1ab_0 - subdir: noarch - noarch: python - url: https://conda.anaconda.org/conda-forge/noarch/pytest-7.2.2-pyhd8ed1ab_0.conda - sha256: d074ef1e2ffc3334927c281e4b79aebfa8eefd2d1588c3830a45ba4bce3e6ea9 - md5: 60958b19354e0ec295b43f6ab5cfab86 - depends: - - attrs >=19.2.0 - - colorama - - exceptiongroup - - iniconfig - - packaging - - pluggy >=0.12,<2.0 - - python >=3.8 - - tomli >=1.0.0 - constrains: - - pytest-faulthandler >=2 - license: MIT - license_family: MIT - purls: - - pkg:pypi/pytest?source=hash-mapping - size: 236598 - timestamp: 1677886053433 - kind: conda name: pytest version: 8.3.3 diff --git a/pyproject.toml b/pyproject.toml index ac97b0ecf..88c4caa60 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,7 +29,6 @@ dependencies = [ "scikit-learn>=1.2.2,<2", "joblib>=1.1.1,<2", "threadpoolctl>=2.0.0,<4", - "pytest>=7.2.2,<9", ] [tool.setuptools.dynamic] @@ -95,7 +94,6 @@ scipy = ">=1.10.1,<2" scikit-learn = ">=1.2.2,<2" joblib = ">=1.1.1,<2" threadpoolctl = ">=2.0.0,<4" -pytest = ">=7.2.2,<9" [tool.pixi.feature.dev.dependencies] ipykernel = "*" @@ -140,7 +138,6 @@ scipy = "==1.10.1" scikit-learn = "==1.2.2" joblib = "==1.1.1" threadpoolctl = "==2.0.0" -pytest = "==7.2.2" [tool.pixi.feature.min-optional-dependencies.dependencies] pandas = "==1.5.3" @@ -174,6 +171,7 @@ python = "~=3.12.0" [tool.pixi.feature.tests.dependencies] packaging = ">=23.2,<25" +pytest = ">=7.2.2,<9" pytest-cov = ">=4.1.0,<6" pytest-xdist = ">=3.5.0,<4" From ab419491e5c7960c5b7168f798e64b0c87b5d77f Mon Sep 17 00:00:00 2001 From: Guillaume Lemaitre Date: Sun, 6 Oct 2024 23:06:29 +0200 Subject: [PATCH 7/7] update readme --- README.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.rst b/README.rst index f68c1473b..564efc30e 100644 --- a/README.rst +++ b/README.rst @@ -129,6 +129,21 @@ of the scikit-learn community. Therefore, you can refer to their `Development Guide `_. +Endorsement of the Scientific Python Specification +-------------------------------------------------- + +We endorse good practices from the Scientific Python Ecosystem Coordination (SPEC). +The full list of recommendations is available `here`_. + +See below the list of recommendations that we endorse for the imbalanced-learn project. + +|SPEC 0 — Minimum Supported Dependencies| + +.. |SPEC 0 — Minimum Supported Dependencies| image:: https://img.shields.io/badge/SPEC-0-green?labelColor=%23004811&color=%235CA038 + :target: https://scientific-python.org/specs/spec-0000/ + +.. _here: https://scientific-python.org/specs/ + About -----