From 819d2bfb523327ee225d79ee5b854caa3ca652fb Mon Sep 17 00:00:00 2001 From: Soledad Galli Date: Fri, 31 Jul 2026 08:31:23 +0200 Subject: [PATCH] Remove sklearn<=1.6 compatibility code The project already requires scikit-learn>=1.7.0 (pyproject.toml, tox.ini, .circleci/config.yml), so the sklearn<=1.6 branches of every check_estimator/tags conditional were dead code. This removes them, keeping only the >=1.6 branch (the one using check_estimator(expected_failed_checks=...)): - feature_engine/tags.py: collapse the sklearn_version > 1.6 check in _return_tags(), the shared helper used across ~20 estimator classes. - 11 tests/**/test_check_estimator_*.py files: collapse each if/else on sklearn_version vs 1.6, drop the now-unused sklearn/ parse_version imports and sklearn_version variables. - tests/test_prediction/test_check_estimator_prediction.py: this file had no >=1.6 branch, only the dead <1.6 one (its own TODO already flagged this). Removing it leaves the prediction module with no test_check_estimator_from_sklearn coverage - a pre-existing gap, not introduced by this change, left as a follow-up. - tests/test_creation/test_geo_features.py: __sklearn_tags__ always exists at sklearn>=1.7, so drop the hasattr() guard around it. - tests/test_wrappers/test_sklearn_wrapper.py: also collapse the _OneHotEncoder() test helper's sparse/sparse_output branch (sklearn <1.2 compat, dead for the same reason). The separate KBinsDiscretizer(quantile_method=...) branch (sklearn<1.7) is intentionally left as-is - different threshold, out of scope here. - tests/check_estimators_with_parametrize_tests.py: delete entirely. A standalone, non-CI reference file documenting the pre-1.6 parametrize_with_checks() call signature. _more_tags()/__sklearn_tags__() method definitions are untouched: _more_tags() is feature_engine's own internal metadata/xfail-checks store (read by tests/estimator_checks/*.py), not a legacy sklearn shim, and __sklearn_tags__() is the current sklearn API. Verified: identical test suite pass/fail counts before and after (2010 passed, 114 failed - all 114 are pre-existing narwhals-migration WIP failures unrelated to this change), flake8 and mypy clean (the one remaining mypy error is pre-existing in datetime_subtraction.py, unrelated to this PR). --- feature_engine/tags.py | 25 +-- ...check_estimators_with_parametrize_tests.py | 200 ------------------ .../test_check_estimator_creation.py | 23 +- tests/test_creation/test_geo_features.py | 6 +- .../test_check_estimator_discretisers.py | 24 +-- .../test_check_estimator_encoders.py | 26 +-- .../test_check_estimator_imputers.py | 23 +- .../test_check_estimator_outliers.py | 64 +++--- .../test_check_estimator_prediction.py | 17 +- .../test_check_estimator_preprocessing.py | 64 +++--- .../test_check_estimator_selectors.py | 36 +--- .../test_check_estimator_forecasting.py | 37 ++-- .../test_check_estimator_transformers.py | 84 ++++---- .../test_check_estimator_wrappers.py | 24 +-- tests/test_wrappers/test_sklearn_wrapper.py | 8 +- 15 files changed, 164 insertions(+), 497 deletions(-) delete mode 100644 tests/check_estimators_with_parametrize_tests.py diff --git a/feature_engine/tags.py b/feature_engine/tags.py index ad36b030a..0c15bb1a0 100644 --- a/feature_engine/tags.py +++ b/feature_engine/tags.py @@ -1,9 +1,3 @@ -import sklearn -from sklearn.utils.fixes import parse_version - -sklearn_version = parse_version(parse_version(sklearn.__version__).base_version) - - def _return_tags(): tags = { "preserves_dtype": [], @@ -32,14 +26,13 @@ def _return_tags(): }, } - if sklearn_version > parse_version("1.6"): - msg1 = "against Feature-engines design." - msg2 = "Our transformers do not preserve dtype." - all_fail = { - "check_do_not_raise_errors_in_init_or_set_params": msg1, - "check_transformer_preserve_dtypes": msg2, - # TODO: investigate this test further. - "check_n_features_in_after_fitting": "not sure why it fails, we do check.", - } - tags["_xfail_checks"].update(all_fail) # type: ignore + msg1 = "against Feature-engines design." + msg2 = "Our transformers do not preserve dtype." + all_fail = { + "check_do_not_raise_errors_in_init_or_set_params": msg1, + "check_transformer_preserve_dtypes": msg2, + # TODO: investigate this test further. + "check_n_features_in_after_fitting": "not sure why it fails, we do check.", + } + tags["_xfail_checks"].update(all_fail) # type: ignore return tags diff --git a/tests/check_estimators_with_parametrize_tests.py b/tests/check_estimators_with_parametrize_tests.py deleted file mode 100644 index 039bd50c2..000000000 --- a/tests/check_estimators_with_parametrize_tests.py +++ /dev/null @@ -1,200 +0,0 @@ -""" -This file is only intended to help understand check_estimator tests on Feature-engine -transformers. It is not run as part of the battery of acceptance tests. Works up to -sklearn < 1.6. -""" - -from sklearn.impute import SimpleImputer -from sklearn.linear_model import LogisticRegression -from sklearn.utils.estimator_checks import parametrize_with_checks - -from feature_engine.creation import ( - CyclicalFeatures, - DecisionTreeFeatures, - MathFeatures, - RelativeFeatures, -) -from feature_engine.encoding import ( - CountEncoder, - DecisionTreeEncoder, - MeanEncoder, - OneHotEncoder, - OrdinalEncoder, - RareLabelEncoder, - StringSimilarityEncoder, - WoEEncoder, -) -from feature_engine.imputation import ( - AddMissingIndicator, - ArbitraryImputer, - CategoricalImputer, - DropMissingData, - EndTailImputer, - MeanImputer, - RandomSampleImputer, -) -from feature_engine.outliers import ArbitraryOutlierCapper, OutlierTrimmer, Winsoriser -from feature_engine.selection import ( - MRMR, - DropConstantFeatures, - DropCorrelatedFeatures, - DropDuplicateFeatures, - DropFeatures, - DropHighPSIFeatures, - ProbeFeatureSelection, - RecursiveFeatureAddition, - RecursiveFeatureElimination, - SelectByInformationValue, - SelectByShuffling, - SelectBySingleFeaturePerformance, - SelectByTargetEncoding, - SmartCorrelatedSelection, -) -from feature_engine.timeseries.forecasting import ( - ExpandingWindowFeatures, - LagFeatures, - WindowFeatures, -) -from feature_engine.transformation import ( - ArcsinTransformer, - BoxCoxTransformer, - LogTransformer, - PowerTransformer, - ReciprocalTransformer, - YeoJohnsonTransformer, -) -from feature_engine.wrappers import SklearnWrapper - - -# creation -@parametrize_with_checks( - [ - DecisionTreeFeatures(regression=False), - CyclicalFeatures(), - MathFeatures(variables=["x0", "x1"], func="mean", missing_values="ignore"), - RelativeFeatures( - variables=["x0", "x1"], - reference=["x0"], - func=["add"], - missing_values="ignore", - ), - ] -) -def test_sklearn_compatible_creator(estimator, check): - check(estimator) - - -# imputation -@parametrize_with_checks( - [ - MeanImputer(), - ArbitraryImputer(), - CategoricalImputer(fill_value=0, ignore_format=True), - EndTailImputer(), - AddMissingIndicator(), - RandomSampleImputer(), - DropMissingData(), - ] -) -def test_sklearn_compatible_imputer(estimator, check): - check(estimator) - - -# encoding -@parametrize_with_checks( - [ - CountEncoder(ignore_format=True), - DecisionTreeEncoder(regression=False, ignore_format=True), - MeanEncoder(ignore_format=True), - OneHotEncoder(ignore_format=True), - OrdinalEncoder(ignore_format=True), - RareLabelEncoder( - tol=0.00000000001, - n_categories=100000000000, - replace_with=10, - ignore_format=True, - ), - WoEEncoder(ignore_format=True), - StringSimilarityEncoder(ignore_format=True), - ] -) -def test_sklearn_compatible_encoder(estimator, check): - check(estimator) - - -# outliers -@parametrize_with_checks( - [ - ArbitraryOutlierCapper(max_capping_dict={"x0": 10}), - OutlierTrimmer(), - Winsoriser(), - ] -) -def test_sklearn_compatible_outliers(estimator, check): - check(estimator) - - -# transformers -@parametrize_with_checks( - [ - ArcsinTransformer(), - BoxCoxTransformer(), - LogTransformer(), - PowerTransformer(), - ReciprocalTransformer(), - YeoJohnsonTransformer(), - ] -) -def test_sklearn_compatible_transformer(estimator, check): - check(estimator) - - -# selectors -@parametrize_with_checks( - [ - DropFeatures(features_to_drop=["x0"]), - DropConstantFeatures(missing_values="ignore"), - DropDuplicateFeatures(), - DropCorrelatedFeatures(), - SmartCorrelatedSelection(), - DropHighPSIFeatures(bins=5), - SelectByShuffling( - LogisticRegression(max_iter=2, random_state=1), scoring="accuracy" - ), - SelectBySingleFeaturePerformance( - LogisticRegression(max_iter=2, random_state=1), scoring="accuracy" - ), - RecursiveFeatureAddition( - LogisticRegression(max_iter=2, random_state=1), scoring="accuracy" - ), - RecursiveFeatureElimination( - LogisticRegression(max_iter=2, random_state=1), - scoring="accuracy", - threshold=-100, - ), - SelectByTargetEncoding(scoring="roc_auc", bins=3, regression=False), - SelectByInformationValue(), - MRMR(), - ProbeFeatureSelection(estimator=LogisticRegression()), - ] -) -def test_sklearn_compatible_selectors(estimator, check): - check(estimator) - - -# wrappers -@parametrize_with_checks([SklearnWrapper(SimpleImputer())]) -def test_sklearn_compatible_wrapper(estimator, check): - check(estimator) - - -# test_forecasting -@parametrize_with_checks( - [ - LagFeatures(missing_values="ignore"), - WindowFeatures(missing_values="ignore"), - ExpandingWindowFeatures(missing_values="ignore"), - ] -) -def test_sklearn_compatible_forecasters(estimator, check): - check(estimator) diff --git a/tests/test_creation/test_check_estimator_creation.py b/tests/test_creation/test_check_estimator_creation.py index e3c22caa1..d58bc3b25 100644 --- a/tests/test_creation/test_check_estimator_creation.py +++ b/tests/test_creation/test_check_estimator_creation.py @@ -1,9 +1,7 @@ import pandas as pd import pytest -import sklearn from sklearn.pipeline import Pipeline from sklearn.utils.estimator_checks import check_estimator -from sklearn.utils.fixes import parse_version from feature_engine.creation import ( CyclicalFeatures, @@ -14,8 +12,6 @@ ) from tests.estimator_checks.estimator_checks import check_feature_engine_estimator -sklearn_version = parse_version(parse_version(sklearn.__version__).base_version) - # Estimators for sklearn's check_estimator # Note: GeoDistanceFeatures is not included here because it requires 4 specific # named coordinate columns, but sklearn's check_estimator generates test data @@ -29,20 +25,13 @@ DecisionTreeFeatures(regression=False), ] -if sklearn_version > parse_version("1.6"): - - @pytest.mark.parametrize("estimator", _estimators) - def test_check_estimator_from_sklearn(estimator): - return check_estimator( - estimator=estimator, - expected_failed_checks=estimator._more_tags()["_xfail_checks"], - ) - -else: - @pytest.mark.parametrize("estimator", _estimators) - def test_check_estimator_from_sklearn(estimator): - return check_estimator(estimator) +@pytest.mark.parametrize("estimator", _estimators) +def test_check_estimator_from_sklearn(estimator): + return check_estimator( + estimator=estimator, + expected_failed_checks=estimator._more_tags()["_xfail_checks"], + ) _estimators = [ diff --git a/tests/test_creation/test_geo_features.py b/tests/test_creation/test_geo_features.py index bbd800044..4fd0f0c5c 100644 --- a/tests/test_creation/test_geo_features.py +++ b/tests/test_creation/test_geo_features.py @@ -356,7 +356,5 @@ def test_more_tags_and_sklearn_tags(): == "transformer has mandatory parameters" ) - # basic check for sklearn tags if available (new sklearn versions) - if hasattr(transformer, "__sklearn_tags__"): - tags = transformer.__sklearn_tags__() - assert tags is not None + tags = transformer.__sklearn_tags__() + assert tags is not None diff --git a/tests/test_discretisation/test_check_estimator_discretisers.py b/tests/test_discretisation/test_check_estimator_discretisers.py index 87e175eac..4809013ca 100644 --- a/tests/test_discretisation/test_check_estimator_discretisers.py +++ b/tests/test_discretisation/test_check_estimator_discretisers.py @@ -1,10 +1,8 @@ import numpy as np import pandas as pd import pytest -import sklearn from sklearn.pipeline import Pipeline from sklearn.utils.estimator_checks import check_estimator -from sklearn.utils.fixes import parse_version from feature_engine.discretisation import ( ArbitraryDiscretiser, @@ -15,9 +13,6 @@ ) from tests.estimator_checks.estimator_checks import check_feature_engine_estimator -sklearn_version = parse_version(parse_version(sklearn.__version__).base_version) - - _estimators = [ DecisionTreeDiscretiser(regression=False), EqualFrequencyDiscretiser(), @@ -26,20 +21,13 @@ GeometricWidthDiscretiser(), ] -if sklearn_version < parse_version("1.6"): - - @pytest.mark.parametrize("estimator", _estimators) - def test_check_estimator_from_sklearn(estimator): - return check_estimator(estimator) -else: - - @pytest.mark.parametrize("estimator", _estimators) - def test_check_estimator_from_sklearn(estimator): - return check_estimator( - estimator=estimator, - expected_failed_checks=estimator._more_tags()["_xfail_checks"], - ) +@pytest.mark.parametrize("estimator", _estimators) +def test_check_estimator_from_sklearn(estimator): + return check_estimator( + estimator=estimator, + expected_failed_checks=estimator._more_tags()["_xfail_checks"], + ) @pytest.mark.parametrize("estimator", _estimators) diff --git a/tests/test_encoding/test_check_estimator_encoders.py b/tests/test_encoding/test_check_estimator_encoders.py index 0e30f2939..82e299588 100644 --- a/tests/test_encoding/test_check_estimator_encoders.py +++ b/tests/test_encoding/test_check_estimator_encoders.py @@ -1,12 +1,10 @@ import pandas as pd import pytest -import sklearn from numpy import nan from sklearn import clone from sklearn.exceptions import NotFittedError from sklearn.pipeline import Pipeline from sklearn.utils.estimator_checks import check_estimator -from sklearn.utils.fixes import parse_version from feature_engine.encoding import ( CountEncoder, @@ -25,8 +23,6 @@ test_df, ) -sklearn_version = parse_version(parse_version(sklearn.__version__).base_version) - _estimators = [ CountEncoder(ignore_format=True), CountFrequencyEncoder(ignore_format=True), @@ -46,22 +42,16 @@ ] -if sklearn_version < parse_version("1.6"): - - @pytest.mark.parametrize("estimator", _estimators) - def test_check_estimator_from_sklearn(estimator): - return check_estimator(estimator) +expected_fails = _return_tags()["_xfail_checks"] +expected_fails.update({"check_estimators_nan_inf": "transformer allows NA"}) -else: - expected_fails = _return_tags()["_xfail_checks"] - expected_fails.update({"check_estimators_nan_inf": "transformer allows NA"}) - @pytest.mark.parametrize("estimator", _estimators) - def test_check_estimator_from_sklearn(estimator): - if estimator.__class__.__name__ != "WoEEncoder": - return check_estimator( - estimator=estimator, expected_failed_checks=expected_fails - ) +@pytest.mark.parametrize("estimator", _estimators) +def test_check_estimator_from_sklearn(estimator): + if estimator.__class__.__name__ != "WoEEncoder": + return check_estimator( + estimator=estimator, expected_failed_checks=expected_fails + ) _estimators = [ diff --git a/tests/test_imputation/test_check_estimator_imputers.py b/tests/test_imputation/test_check_estimator_imputers.py index 8b47b6147..45f2fa86b 100644 --- a/tests/test_imputation/test_check_estimator_imputers.py +++ b/tests/test_imputation/test_check_estimator_imputers.py @@ -1,9 +1,7 @@ import pandas as pd import pytest -import sklearn from sklearn.pipeline import Pipeline from sklearn.utils.estimator_checks import check_estimator -from sklearn.utils.fixes import parse_version from feature_engine.imputation import ( MissingIndicator, @@ -26,22 +24,13 @@ DropMissingData(), ] -sklearn_version = parse_version(parse_version(sklearn.__version__).base_version) -if sklearn_version < parse_version("1.6"): - - @pytest.mark.parametrize("estimator", _estimators) - def test_check_estimator_from_sklearn(estimator): - return check_estimator(estimator) - -else: - - @pytest.mark.parametrize("estimator", _estimators) - def test_check_estimator_from_sklearn(estimator): - return check_estimator( - estimator=estimator, - expected_failed_checks=estimator._more_tags()["_xfail_checks"], - ) +@pytest.mark.parametrize("estimator", _estimators) +def test_check_estimator_from_sklearn(estimator): + return check_estimator( + estimator=estimator, + expected_failed_checks=estimator._more_tags()["_xfail_checks"], + ) @pytest.mark.parametrize("estimator", _estimators) diff --git a/tests/test_outliers/test_check_estimator_outliers.py b/tests/test_outliers/test_check_estimator_outliers.py index c0d30300f..0b5ee3491 100644 --- a/tests/test_outliers/test_check_estimator_outliers.py +++ b/tests/test_outliers/test_check_estimator_outliers.py @@ -1,9 +1,7 @@ import pandas as pd import pytest -import sklearn from sklearn.pipeline import Pipeline from sklearn.utils.estimator_checks import check_estimator -from sklearn.utils.fixes import parse_version from feature_engine.outliers import ArbitraryOutlierCapper, OutlierTrimmer, Winsoriser from feature_engine.tags import _return_tags @@ -15,42 +13,32 @@ Winsoriser(), ] -sklearn_version = parse_version(parse_version(sklearn.__version__).base_version) - -if sklearn_version < parse_version("1.6"): - - @pytest.mark.parametrize("estimator", _estimators) - def test_check_estimator_from_sklearn(estimator): - return check_estimator(estimator) - -else: - FAILED_CHECKS = _return_tags()["_xfail_checks"] - FAILED_CHECKS_AOC = _return_tags()["_xfail_checks"] - - msg1 = ( - "transformers raise errors when data variation is low, " "thus this check fails" - ) - - msg2 = "transformer has 1 mandatory parameter" - - FAILED_CHECKS.update({"check_fit2d_1sample": msg1}) - FAILED_CHECKS_AOC.update( - { - "check_fit2d_1sample": msg1, - "check_parameters_default_constructible": msg2, - } - ) - - @pytest.mark.parametrize( - "estimator, failed_tests", - [ - (_estimators[0], FAILED_CHECKS_AOC), - (_estimators[1], FAILED_CHECKS), - (_estimators[2], FAILED_CHECKS), - ], - ) - def test_check_estimator_from_sklearn(estimator, failed_tests): - return check_estimator(estimator=estimator, expected_failed_checks=failed_tests) +FAILED_CHECKS = _return_tags()["_xfail_checks"] +FAILED_CHECKS_AOC = _return_tags()["_xfail_checks"] + +msg1 = "transformers raise errors when data variation is low, " "thus this check fails" + +msg2 = "transformer has 1 mandatory parameter" + +FAILED_CHECKS.update({"check_fit2d_1sample": msg1}) +FAILED_CHECKS_AOC.update( + { + "check_fit2d_1sample": msg1, + "check_parameters_default_constructible": msg2, + } +) + + +@pytest.mark.parametrize( + "estimator, failed_tests", + [ + (_estimators[0], FAILED_CHECKS_AOC), + (_estimators[1], FAILED_CHECKS), + (_estimators[2], FAILED_CHECKS), + ], +) +def test_check_estimator_from_sklearn(estimator, failed_tests): + return check_estimator(estimator=estimator, expected_failed_checks=failed_tests) @pytest.mark.parametrize("estimator", _estimators) diff --git a/tests/test_prediction/test_check_estimator_prediction.py b/tests/test_prediction/test_check_estimator_prediction.py index 3618933b3..afe45db71 100644 --- a/tests/test_prediction/test_check_estimator_prediction.py +++ b/tests/test_prediction/test_check_estimator_prediction.py @@ -1,11 +1,8 @@ import numpy as np import pandas as pd import pytest -import sklearn from sklearn.base import clone from sklearn.exceptions import NotFittedError -from sklearn.utils.estimator_checks import check_estimator -from sklearn.utils.fixes import parse_version from feature_engine._prediction.base_predictor import BaseTargetMeanEstimator from feature_engine._prediction.target_mean_classifier import TargetMeanClassifier @@ -18,18 +15,14 @@ from tests.estimator_checks.dataframe_for_checks import test_df from tests.estimator_checks.fit_functionality_checks import check_error_if_y_not_passed -sklearn_version = parse_version(parse_version(sklearn.__version__).base_version) - _estimators = [BaseTargetMeanEstimator(), TargetMeanClassifier(), TargetMeanRegressor()] _predictors = [TargetMeanRegressor(), TargetMeanClassifier()] -if sklearn_version < parse_version("1.6"): - # In sklearn version 1.6, changes into the developer api were introduced - # that break the tests. Need to dig further into it. - # TODO: add tests for sklearn version > 1.6 - @pytest.mark.parametrize("estimator", [BaseTargetMeanEstimator()]) - def test_check_estimator_from_sklearn(estimator): - return check_estimator(estimator) + +# TODO: no test_check_estimator_from_sklearn exists for this module — the previous +# sklearn<1.6 version of this test was removed when dropping sklearn<=1.6 support, +# and a sklearn>=1.6-compatible replacement (using expected_failed_checks=...) was +# never written. See the module's git history for the removed sklearn<1.6 branch. @pytest.mark.parametrize("estimator", _estimators) diff --git a/tests/test_preprocessing/test_check_estimator_preprocessing.py b/tests/test_preprocessing/test_check_estimator_preprocessing.py index 378091840..ca16f8863 100644 --- a/tests/test_preprocessing/test_check_estimator_preprocessing.py +++ b/tests/test_preprocessing/test_check_estimator_preprocessing.py @@ -1,12 +1,10 @@ import pandas as pd import pytest -import sklearn from numpy import nan from sklearn import clone from sklearn.exceptions import NotFittedError from sklearn.pipeline import Pipeline from sklearn.utils.estimator_checks import check_estimator -from sklearn.utils.fixes import parse_version from feature_engine.preprocessing import MatchCategories, MatchVariables from feature_engine.tags import _return_tags @@ -15,43 +13,35 @@ test_df, ) -sklearn_version = parse_version(parse_version(sklearn.__version__).base_version) - _estimators = [MatchCategories(ignore_format=True), MatchVariables()] -if sklearn_version < parse_version("1.6"): - - @pytest.mark.parametrize("estimator", _estimators) - def test_check_estimator_from_sklearn(estimator): - return check_estimator(estimator) - -else: - FAILED_CHECKS = _return_tags()["_xfail_checks"] - FAILED_CHECKS_MATCHCOLS = _return_tags()["_xfail_checks"] - - msg1 = "input shape of dataframes in fit and transform can differ" - msg2 = ( - "transformer takes categorical variables, and inf cannot be determined" - "on these variables. Thus, check is not implemented" - ) - - FAILED_CHECKS.update({"check_estimators_nan_inf": msg2}) - FAILED_CHECKS_MATCHCOLS.update( - { - "check_transformer_general": msg1, - "check_estimators_nan_inf": msg2, - } - ) - - @pytest.mark.parametrize( - "estimator, failed_tests", - [ - (_estimators[0], FAILED_CHECKS), - (_estimators[1], FAILED_CHECKS_MATCHCOLS), - ], - ) - def test_check_estimator_from_sklearn(estimator, failed_tests): - return check_estimator(estimator=estimator, expected_failed_checks=failed_tests) +FAILED_CHECKS = _return_tags()["_xfail_checks"] +FAILED_CHECKS_MATCHCOLS = _return_tags()["_xfail_checks"] + +msg1 = "input shape of dataframes in fit and transform can differ" +msg2 = ( + "transformer takes categorical variables, and inf cannot be determined" + "on these variables. Thus, check is not implemented" +) + +FAILED_CHECKS.update({"check_estimators_nan_inf": msg2}) +FAILED_CHECKS_MATCHCOLS.update( + { + "check_transformer_general": msg1, + "check_estimators_nan_inf": msg2, + } +) + + +@pytest.mark.parametrize( + "estimator, failed_tests", + [ + (_estimators[0], FAILED_CHECKS), + (_estimators[1], FAILED_CHECKS_MATCHCOLS), + ], +) +def test_check_estimator_from_sklearn(estimator, failed_tests): + return check_estimator(estimator=estimator, expected_failed_checks=failed_tests) @pytest.mark.parametrize("estimator", [MatchCategories(), MatchVariables()]) diff --git a/tests/test_selection/test_check_estimator_selectors.py b/tests/test_selection/test_check_estimator_selectors.py index debbe165e..7ce85634b 100644 --- a/tests/test_selection/test_check_estimator_selectors.py +++ b/tests/test_selection/test_check_estimator_selectors.py @@ -1,10 +1,8 @@ import pandas as pd import pytest -import sklearn from sklearn.linear_model import LogisticRegression from sklearn.pipeline import Pipeline from sklearn.utils.estimator_checks import check_estimator -from sklearn.utils.fixes import parse_version from feature_engine.selection import ( MRMR, @@ -29,8 +27,6 @@ check_raises_error_if_only_1_variable, ) -sklearn_version = parse_version(parse_version(sklearn.__version__).base_version) - _logreg = LogisticRegression(C=0.0001, max_iter=2, random_state=1) _estimators = [ @@ -84,27 +80,17 @@ ProbeFeatureSelection(estimator=_logreg, scoring="accuracy"), ] -if sklearn_version < parse_version("1.6"): - - @pytest.mark.parametrize("estimator", _estimators) - def test_check_estimator_from_sklearn(estimator): - return check_estimator(estimator) - -else: - # In sklearn 1.6. the API changes break the tests for the target mean selector. - # We need to investigate further. - # TODO: investigate checks for target mean selector. - @pytest.mark.parametrize("estimator", _estimators) - def test_check_estimator_from_sklearn(estimator): - if estimator.__class__.__name__ not in [ - "SelectByTargetEncoding", - "SelectByTargetMeanPerformance", - "SelectByInformationValue", - ]: - failed_tests = estimator._more_tags()["_xfail_checks"] - return check_estimator( - estimator=estimator, expected_failed_checks=failed_tests - ) + +# TODO: investigate checks for target mean selector. +@pytest.mark.parametrize("estimator", _estimators) +def test_check_estimator_from_sklearn(estimator): + if estimator.__class__.__name__ not in [ + "SelectByTargetEncoding", + "SelectByTargetMeanPerformance", + "SelectByInformationValue", + ]: + failed_tests = estimator._more_tags()["_xfail_checks"] + return check_estimator(estimator=estimator, expected_failed_checks=failed_tests) @pytest.mark.parametrize("estimator", _univariate_estimators) diff --git a/tests/test_time_series/test_forecasting/test_check_estimator_forecasting.py b/tests/test_time_series/test_forecasting/test_check_estimator_forecasting.py index f9905a4d0..85a4af38c 100644 --- a/tests/test_time_series/test_forecasting/test_check_estimator_forecasting.py +++ b/tests/test_time_series/test_forecasting/test_check_estimator_forecasting.py @@ -1,11 +1,9 @@ import numpy as np import pandas as pd import pytest -import sklearn from sklearn.base import clone from sklearn.pipeline import Pipeline from sklearn.utils.estimator_checks import check_estimator -from sklearn.utils.fixes import parse_version from feature_engine.timeseries.forecasting import ( ExpandingWindowFeatures, @@ -21,28 +19,19 @@ ] -sklearn_version = parse_version(parse_version(sklearn.__version__).base_version) - -if sklearn_version < parse_version("1.6"): - - @pytest.mark.parametrize("estimator", _estimators) - def test_check_estimator_from_sklearn(estimator): - return check_estimator(estimator) - -else: - @pytest.mark.parametrize("estimator", _estimators) - def test_check_estimator_from_sklearn(estimator): - extra_failing_checks = { - "check_estimators_nan_inf": "Time Series transformers do not handle NaNs " - "or infinity." - } - return check_estimator( - estimator=estimator, - expected_failed_checks={ - **extra_failing_checks, - **estimator._more_tags()["_xfail_checks"], - }, - ) +@pytest.mark.parametrize("estimator", _estimators) +def test_check_estimator_from_sklearn(estimator): + extra_failing_checks = { + "check_estimators_nan_inf": "Time Series transformers do not handle NaNs " + "or infinity." + } + return check_estimator( + estimator=estimator, + expected_failed_checks={ + **extra_failing_checks, + **estimator._more_tags()["_xfail_checks"], + }, + ) @pytest.mark.parametrize("estimator", _estimators) diff --git a/tests/test_transformation/test_check_estimator_transformers.py b/tests/test_transformation/test_check_estimator_transformers.py index 8f482e10d..8440e20de 100644 --- a/tests/test_transformation/test_check_estimator_transformers.py +++ b/tests/test_transformation/test_check_estimator_transformers.py @@ -1,9 +1,7 @@ import pandas as pd import pytest -import sklearn from sklearn.pipeline import Pipeline from sklearn.utils.estimator_checks import check_estimator -from sklearn.utils.fixes import parse_version from feature_engine.transformation import ( ArcsinTransformer, @@ -28,53 +26,45 @@ YeoJohnsonTransformer(), ] -sklearn_version = parse_version(parse_version(sklearn.__version__).base_version) - -if sklearn_version < parse_version("1.6"): - - @pytest.mark.parametrize("estimator", _estimators) - def test_check_estimator_from_sklearn(estimator): - return check_estimator(estimator) +checks_with_negative_values = [ + "check_readonly_memmap_input", + "check_fit_score_takes_y", + "check_dont_overwrite_parameters", + "check_estimators_nan_inf", + "check_f_contiguous_array_estimator", + "check_fit2d_1feature", + "check_fit2d_1sample", + "check_dict_unchanged", + "check_fit_check_is_fitted", + "check_n_features_in", + "check_positive_only_tag_during_fit", + "check_methods_subset_invariance", +] +estimators_not_supporting_negative_values = [ + "BoxCoxTransformer", + "LogTransformer", + "ArcsinTransformer", +] +extra_failing_checks = { + estimator_name: dict.fromkeys( + checks_with_negative_values, + "this checks passes a negative value which is not supported by " + "the transformer", + ) + for estimator_name in estimators_not_supporting_negative_values +} -else: - checks_with_negative_values = [ - "check_readonly_memmap_input", - "check_fit_score_takes_y", - "check_dont_overwrite_parameters", - "check_estimators_nan_inf", - "check_f_contiguous_array_estimator", - "check_fit2d_1feature", - "check_fit2d_1sample", - "check_dict_unchanged", - "check_fit_check_is_fitted", - "check_n_features_in", - "check_positive_only_tag_during_fit", - "check_methods_subset_invariance", - ] - estimators_not_supporting_negative_values = [ - "BoxCoxTransformer", - "LogTransformer", - "ArcsinTransformer", - ] - extra_failing_checks = { - estimator_name: dict.fromkeys( - checks_with_negative_values, - "this checks passes a negative value which is not supported by " - "the transformer", - ) - for estimator_name in estimators_not_supporting_negative_values - } - @pytest.mark.parametrize("estimator", _estimators) - def test_check_estimator_from_sklearn(estimator): - expected_failed_checks = estimator._more_tags()["_xfail_checks"] - expected_failed_checks.update( - extra_failing_checks.get(estimator.__class__.__name__, {}) - ) - return check_estimator( - estimator=estimator, - expected_failed_checks=expected_failed_checks, - ) +@pytest.mark.parametrize("estimator", _estimators) +def test_check_estimator_from_sklearn(estimator): + expected_failed_checks = estimator._more_tags()["_xfail_checks"] + expected_failed_checks.update( + extra_failing_checks.get(estimator.__class__.__name__, {}) + ) + return check_estimator( + estimator=estimator, + expected_failed_checks=expected_failed_checks, + ) @pytest.mark.parametrize("estimator", _estimators[4:]) diff --git a/tests/test_wrappers/test_check_estimator_wrappers.py b/tests/test_wrappers/test_check_estimator_wrappers.py index f663ad7b5..cee500d43 100644 --- a/tests/test_wrappers/test_check_estimator_wrappers.py +++ b/tests/test_wrappers/test_check_estimator_wrappers.py @@ -1,10 +1,8 @@ import pandas as pd import pytest -import sklearn from sklearn.impute import SimpleImputer from sklearn.preprocessing import OrdinalEncoder, StandardScaler from sklearn.utils.estimator_checks import check_estimator -from sklearn.utils.fixes import parse_version from feature_engine.wrappers import SklearnWrapper from tests.estimator_checks.estimator_checks import ( @@ -17,22 +15,14 @@ check_numerical_variables_assignment, ) -sklearn_version = parse_version(parse_version(sklearn.__version__).base_version) -if sklearn_version < parse_version("1.6"): - - def test_sklearn_transformer_wrapper(): - check_estimator(SklearnWrapper(transformer=SimpleImputer())) - -else: - - def test_sklearn_transformer_wrapper(): - check_estimator( - estimator=SklearnWrapper(transformer=SimpleImputer()), - expected_failed_checks=SklearnWrapper( - transformer=SimpleImputer() - )._more_tags()["_xfail_checks"], - ) +def test_sklearn_transformer_wrapper(): + check_estimator( + estimator=SklearnWrapper(transformer=SimpleImputer()), + expected_failed_checks=SklearnWrapper( + transformer=SimpleImputer() + )._more_tags()["_xfail_checks"], + ) @pytest.mark.parametrize( diff --git a/tests/test_wrappers/test_sklearn_wrapper.py b/tests/test_wrappers/test_sklearn_wrapper.py index f15063cd9..76e816c7e 100644 --- a/tests/test_wrappers/test_sklearn_wrapper.py +++ b/tests/test_wrappers/test_sklearn_wrapper.py @@ -60,13 +60,7 @@ def _OneHotEncoder(sparse, drop=None, dtype=np.float64) -> OneHotEncoder: - """OneHotEncoder sparse argument has been renamed as sparse_output - in scikitlearn >=1.2""" - - if skl_version.split(".")[0] == "1" and int(skl_version.split(".")[1]) >= 2: - return OneHotEncoder(sparse_output=sparse, drop=drop, dtype=dtype) - else: - return OneHotEncoder(sparse=sparse, drop=drop, dtype=dtype) + return OneHotEncoder(sparse_output=sparse, drop=drop, dtype=dtype) @pytest.mark.parametrize(