From 12ee9b2e591ec379793bcff1966095b43dac10c6 Mon Sep 17 00:00:00 2001 From: Manuel Calzolari Date: Sat, 19 Aug 2023 12:27:26 +0200 Subject: [PATCH] Update for modern versions of sklearn and numpy --- README.rst | 4 ++-- docs/source/installation.rst | 4 ++-- genetic_selection/gscv.py | 29 +++++++++++++++++++++-------- requirements.txt | 2 +- setup.py | 8 +++++--- 5 files changed, 31 insertions(+), 16 deletions(-) diff --git a/README.rst b/README.rst index 0e239a3..d41bab6 100644 --- a/README.rst +++ b/README.rst @@ -27,8 +27,8 @@ Dependencies sklearn-genetic requires: -- Python (>= 3.6) -- scikit-learn (>= 0.23) +- Python (>= 3.7) +- scikit-learn (>= 1.0) - deap (>= 1.0.2) - numpy - multiprocess diff --git a/docs/source/installation.rst b/docs/source/installation.rst index 7873feb..5dab615 100644 --- a/docs/source/installation.rst +++ b/docs/source/installation.rst @@ -6,8 +6,8 @@ Dependencies sklearn-genetic requires: -- Python (>= 3.6) -- scikit-learn (>= 0.23) +- Python (>= 3.7) +- scikit-learn (>= 1.0) - deap (>= 1.0.2) - numpy - multiprocess diff --git a/genetic_selection/gscv.py b/genetic_selection/gscv.py index 1b49756..c86a7bc 100644 --- a/genetic_selection/gscv.py +++ b/genetic_selection/gscv.py @@ -19,7 +19,7 @@ import multiprocess import numpy as np from sklearn.utils import check_X_y -from sklearn.utils.metaestimators import if_delegate_has_method +from sklearn.utils.metaestimators import available_if from sklearn.base import BaseEstimator from sklearn.base import MetaEstimatorMixin from sklearn.base import clone @@ -122,7 +122,7 @@ def _evalFunction(individual, estimator, X, y, groups, cv, scorer, fit_params, m individual_tuple = tuple(individual) if caching and individual_tuple in scores_cache: return scores_cache[individual_tuple][0], individual_sum, scores_cache[individual_tuple][1] - X_selected = X[:, np.array(individual, dtype=np.bool)] + X_selected = X[:, np.array(individual, dtype=bool)] scores = cross_val_score(estimator=estimator, X=X_selected, y=y, groups=groups, scoring=scorer, cv=cv, fit_params=fit_params) scores_mean = np.mean(scores) @@ -132,6 +132,19 @@ def _evalFunction(individual, estimator, X, y, groups, cv, scorer, fit_params, m return scores_mean, individual_sum, scores_std +def _estimator_has(attr): + """Check if we can delegate a method to the underlying estimator. + + First, we check the first fitted estimator if available, otherwise we + check the unfitted estimator. + """ + return lambda self: ( + hasattr(self.estimator_, attr) + if hasattr(self, "estimator_") + else hasattr(self.estimator, attr) + ) + + class GeneticSelectionCV(BaseEstimator, MetaEstimatorMixin, SelectorMixin): """Feature selection with genetic algorithm. @@ -348,7 +361,7 @@ def _fit(self, X, y, groups=None): pool.join() # Set final attributes - support_ = np.array(hof, dtype=np.bool)[0] + support_ = np.array(hof, dtype=bool)[0] self.estimator_ = clone(self.estimator) self.estimator_.fit(X[:, support_], y) @@ -358,7 +371,7 @@ def _fit(self, X, y, groups=None): return self - @if_delegate_has_method(delegate='estimator') + @available_if(_estimator_has("predict")) def predict(self, X): """Reduce X to the selected features and then predict using the underlying estimator. @@ -374,7 +387,7 @@ def predict(self, X): """ return self.estimator_.predict(self.transform(X)) - @if_delegate_has_method(delegate='estimator') + @available_if(_estimator_has("score")) def score(self, X, y): """Reduce X to the selected features and return the score of the underlying estimator. @@ -391,14 +404,14 @@ def score(self, X, y): def _get_support_mask(self): return self.support_ - @if_delegate_has_method(delegate='estimator') + @available_if(_estimator_has("decision_function")) def decision_function(self, X): return self.estimator_.decision_function(self.transform(X)) - @if_delegate_has_method(delegate='estimator') + @available_if(_estimator_has("predict_proba")) def predict_proba(self, X): return self.estimator_.predict_proba(self.transform(X)) - @if_delegate_has_method(delegate='estimator') + @available_if(_estimator_has("predict_log_proba")) def predict_log_proba(self, X): return self.estimator_.predict_log_proba(self.transform(X)) diff --git a/requirements.txt b/requirements.txt index 9f8ad93..64ba618 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -scikit-learn>=0.23 +scikit-learn>=1.0 deap>=1.0.2 numpy multiprocess diff --git a/setup.py b/setup.py index 6a5853e..db7483e 100644 --- a/setup.py +++ b/setup.py @@ -31,11 +31,13 @@ 'Topic :: Scientific/Engineering', 'License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', ], packages=find_packages(), - python_requires='>=3.6', - install_requires=['scikit-learn>=0.23', 'deap>=1.0.2', 'numpy', 'multiprocess'], + python_requires='>=3.7', + install_requires=['scikit-learn>=1.0', 'deap>=1.0.2', 'numpy', 'multiprocess'], )