diff --git a/.travis.yml b/.travis.yml index 222d6028..12f535b6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ matrix: - python: 3.9 name: "Py39-024+" env: - - sklver=">=0.24.0" + - sklver=">=0.24.2" - jlver=">=1.0" - python: 3.8 name: "Py38-023" diff --git a/_doc/sphinxdoc/source/conf.py b/_doc/sphinxdoc/source/conf.py index 8431a1d0..6f2b117c 100644 --- a/_doc/sphinxdoc/source/conf.py +++ b/_doc/sphinxdoc/source/conf.py @@ -5,7 +5,7 @@ import sys import os import alabaster -from pyquickhelper.helpgen.default_conf import set_sphinx_variables, get_default_stylesheet +from pyquickhelper.helpgen.default_conf import set_sphinx_variables sys.path.insert(0, os.path.abspath(os.path.join(os.path.split(__file__)[0]))) @@ -20,8 +20,9 @@ title="mlinsights", book=True) blog_root = "http://www.xavierdupre.fr/app/mlinsights/helpsphinx/" -html_context = {'css_files': get_default_stylesheet() + - ['_static/my-styles.css']} + +html_css_files = ['my-styles.css'] + html_logo = "phdoc_static/project_ico.png" html_sidebars = {} language = "en" diff --git a/_doc/pipeline.png b/_doc/sphinxdoc/source/pipeline.png similarity index 100% rename from _doc/pipeline.png rename to _doc/sphinxdoc/source/pipeline.png diff --git a/_unittests/ut_mlmodel/test_categories_to_integers.py b/_unittests/ut_mlmodel/test_categories_to_integers.py index ff46011b..ab97c421 100644 --- a/_unittests/ut_mlmodel/test_categories_to_integers.py +++ b/_unittests/ut_mlmodel/test_categories_to_integers.py @@ -119,10 +119,11 @@ def test_categories_to_integers_grid_search(self): LogisticRegression()) self.assertRaise(lambda: test_sklearn_grid_search_cv( lambda: pipe, df), ValueError) - self.assertRaise( - lambda: test_sklearn_grid_search_cv( - lambda: pipe, X, y, categoriestointegers__single=[True, False]), - ValueError, "Unable to find category value") + if compare_module_version(sklver, "0.24") >= 0: + self.assertRaise( + lambda: test_sklearn_grid_search_cv( + lambda: pipe, X, y, categoriestointegers__single=[True, False]), + ValueError, "Unable to find category value") pipe = make_pipeline(CategoriesToIntegers(), Imputer(strategy='most_frequent'), LogisticRegression(n_jobs=1)) diff --git a/_unittests/ut_mlmodel/test_classification_kmeans.py b/_unittests/ut_mlmodel/test_classification_kmeans.py index 6e227b20..7ae4e8f5 100644 --- a/_unittests/ut_mlmodel/test_classification_kmeans.py +++ b/_unittests/ut_mlmodel/test_classification_kmeans.py @@ -57,7 +57,12 @@ def test_classification_kmeans_intercept_weights(self): def test_classification_kmeans_pickle(self): iris = datasets.load_iris() X, y = iris.data, iris.target - test_sklearn_pickle(lambda: ClassifierAfterKMeans(), X, y) + try: + test_sklearn_pickle(lambda: ClassifierAfterKMeans(), X, y) + except AttributeError as e: + if compare_module_version(sklver, "0.24") < 0: + return + raise e def test_classification_kmeans_clone(self): self.maxDiff = None diff --git a/_unittests/ut_mlmodel/test_piecewise_regressor.py b/_unittests/ut_mlmodel/test_piecewise_regressor.py index 41fbdc65..c39d4974 100644 --- a/_unittests/ut_mlmodel/test_piecewise_regressor.py +++ b/_unittests/ut_mlmodel/test_piecewise_regressor.py @@ -7,6 +7,8 @@ from numpy.random import random import pandas from sklearn.linear_model import LinearRegression +from sklearn.datasets import make_regression +from sklearn.tree import DecisionTreeRegressor from pyquickhelper.pycode import ExtTestCase, ignore_warnings from mlinsights.mlmodel import test_sklearn_pickle, test_sklearn_clone, test_sklearn_grid_search_cv from mlinsights.mlmodel.piecewise_estimator import PiecewiseRegressor @@ -148,6 +150,23 @@ def test_piecewise_regressor_grid_search(self): self.assertGreater(res['score'], 0) self.assertLesser(res['score'], 1) + def test_piecewise_regressor_issue(self): + X, y = make_regression(10000, n_features=1, n_informative=1, # pylint: disable=W0632 + n_targets=1) + y = y.reshape((-1, 1)) + model = PiecewiseRegressor( + binner=DecisionTreeRegressor(min_samples_leaf=300)) + model.fit(X, y) + vvc = model.predict(X) + self.assertEqual(vvc.shape, (X.shape[0], )) + + def test_piecewise_regressor_raise(self): + X, y = make_regression(10000, n_features=2, n_informative=2, # pylint: disable=W0632 + n_targets=2) + model = PiecewiseRegressor( + binner=DecisionTreeRegressor(min_samples_leaf=300)) + self.assertRaise(lambda: model.fit(X, y), RuntimeError) + if __name__ == "__main__": unittest.main() diff --git a/mlinsights/mlmodel/_kmeans_022.py b/mlinsights/mlmodel/_kmeans_022.py index 51a1ae28..acc4a0fd 100644 --- a/mlinsights/mlmodel/_kmeans_022.py +++ b/mlinsights/mlmodel/_kmeans_022.py @@ -9,7 +9,7 @@ from sklearn.utils.sparsefuncs_fast import assign_rows_csr # pylint: disable=W0611,E0611 try: from sklearn.cluster._kmeans import _check_sample_weight -except ImportError: +except ImportError: # pragma: no cover from sklearn.cluster._kmeans import ( _check_normalize_sample_weight as _check_sample_weight) from sklearn.metrics.pairwise import pairwise_distances_argmin_min diff --git a/mlinsights/mlmodel/_kmeans_constraint_.py b/mlinsights/mlmodel/_kmeans_constraint_.py index 2f170a6d..764d8e83 100644 --- a/mlinsights/mlmodel/_kmeans_constraint_.py +++ b/mlinsights/mlmodel/_kmeans_constraint_.py @@ -234,7 +234,8 @@ def _compute_strategy_coefficient(distances, strategy, labels): ar = numpy.arange(distances.shape[0]) dist = distances[ar, labels] return distances - dist[:, numpy.newaxis] - raise ValueError("Unknwon strategy '{0}'.".format(strategy)) + raise ValueError( # pragma: no cover + "Unknwon strategy '{0}'.".format(strategy)) def _randomize_index(index, weights): diff --git a/mlinsights/mlmodel/decision_tree_logreg.py b/mlinsights/mlmodel/decision_tree_logreg.py index eb6c2be8..8f6918c6 100644 --- a/mlinsights/mlmodel/decision_tree_logreg.py +++ b/mlinsights/mlmodel/decision_tree_logreg.py @@ -422,7 +422,7 @@ def _fit_parallel(self, X, y, sample_weight): def _fit_perpendicular(self, X, y, sample_weight): "Implements the perpendicular strategy." - raise NotImplementedError() + raise NotImplementedError() # pragma: no cover def predict(self, X): """ diff --git a/mlinsights/mlmodel/extended_features.py b/mlinsights/mlmodel/extended_features.py index de0edc6c..fcaf8fcf 100644 --- a/mlinsights/mlmodel/extended_features.py +++ b/mlinsights/mlmodel/extended_features.py @@ -71,7 +71,7 @@ def _get_feature_names_poly(self, input_features=None): input_features = ["x%d" % i for i in range(0, self.n_input_features_)] elif len(input_features) != self.n_input_features_: - raise ValueError( + raise ValueError( # pragma: no cover "input_features should contain {} strings.".format( self.n_input_features_)) @@ -125,7 +125,7 @@ def fit(self, X, y=None): return self._fit_poly(X, y) elif self.kind == 'poly-slow': return self._fit_poly(X, y) - raise ValueError( + raise ValueError( # pragma: no cover "Unknown extended features '{}'.".format(self.kind)) def _fit_poly(self, X, y=None): @@ -148,7 +148,8 @@ def transform(self, X): """ n_features = X.shape[1] if n_features != self.n_input_features_: - raise ValueError("X shape does not match training shape") + raise ValueError( # pragma: no cover + "X shape does not match training shape") if self.kind == 'poly': return self._transform_poly(X) if self.kind == 'poly-slow': diff --git a/mlinsights/mlmodel/piecewise_estimator.py b/mlinsights/mlmodel/piecewise_estimator.py index a843137d..d56d31bc 100644 --- a/mlinsights/mlmodel/piecewise_estimator.py +++ b/mlinsights/mlmodel/piecewise_estimator.py @@ -13,7 +13,7 @@ from sklearn.utils.fixes import _joblib_parallel_args try: from tqdm import tqdm -except ImportError: +except ImportError: # pragma: no cover pass @@ -223,6 +223,12 @@ def fit(self, X, y, sample_weight=None): * `dim_`: dimension of the output * `mean_`: average targets """ + if len(y.shape) == 2: + if y.shape[-1] == 1: + y = y.ravel() + else: + raise RuntimeError( + "This regressor only works with single dimension targets.") if isinstance(X, pandas.DataFrame): X = X.values if isinstance(X, list): diff --git a/mlinsights/mlmodel/sklearn_testing.py b/mlinsights/mlmodel/sklearn_testing.py index f8f9c879..ca6c9287 100644 --- a/mlinsights/mlmodel/sklearn_testing.py +++ b/mlinsights/mlmodel/sklearn_testing.py @@ -259,6 +259,8 @@ def test_sklearn_grid_search_cv(fct_model, X, y=None, sample_weight=None, **grid clf = GridSearchCV(pipe, parameters) if y_train is None and w_train is None: clf.fit(X_train) + elif w_train is None: + clf.fit(X_train, y_train) # pylint: disable=E1121 else: clf.fit(X_train, y_train, w_train) # pylint: disable=E1121 score = clf.score(X_test, y_test) diff --git a/mlinsights/mlmodel/sklearn_transform_inv_fct.py b/mlinsights/mlmodel/sklearn_transform_inv_fct.py index 3af89143..596eff93 100644 --- a/mlinsights/mlmodel/sklearn_transform_inv_fct.py +++ b/mlinsights/mlmodel/sklearn_transform_inv_fct.py @@ -46,12 +46,13 @@ def __init__(self, fct, fct_inv=None): BaseReciprocalTransformer.__init__(self) if isinstance(fct, str): if fct_inv is not None: - raise ValueError( + raise ValueError( # pragma: no cover "If fct is a function name, fct_inv must not be specified.") opts = self.__class__.available_fcts() if fct not in opts: - raise ValueError("Unknown fct '{}', it should in {}.".format( - fct, list(sorted(opts)))) + raise ValueError( # pragma: no cover + "Unknown fct '{}', it should in {}.".format( + fct, list(sorted(opts)))) else: if fct_inv is None: raise ValueError( @@ -117,7 +118,8 @@ def fit(self, X=None, y=None, sample_weight=None): Defines a random permutation over the targets. """ if y is None: - raise RuntimeError("targets cannot be empty.") + raise RuntimeError( # pragma: no cover + "targets cannot be empty.") num = numpy.issubdtype(y.dtype, numpy.floating) perm = {} for u in y.ravel(): @@ -141,7 +143,7 @@ def fit(self, X=None, y=None, sample_weight=None): def _check_is_fitted(self): if not hasattr(self, 'permutation_'): - raise NotFittedError( + raise NotFittedError( # pragma: no cover "This instance {} is not fitted yet. Call 'fit' with " "appropriate arguments before using this method.".format( type(self))) @@ -169,8 +171,9 @@ def _find_closest(self, cl): return float(res) if self.knn_perm_.dtype in (numpy.int32, numpy.int64): return int(res) - raise NotImplementedError("The function does not work for type {}.".format( - self.knn_perm_.dtype)) + raise NotImplementedError( # pragma: no cover + "The function does not work for type {}.".format( + self.knn_perm_.dtype)) def transform(self, X, y): """ diff --git a/mlinsights/mlmodel/target_predictors.py b/mlinsights/mlmodel/target_predictors.py index 8646ce91..ef65b2d9 100644 --- a/mlinsights/mlmodel/target_predictors.py +++ b/mlinsights/mlmodel/target_predictors.py @@ -109,7 +109,7 @@ def predict(self, X): Predicted values. """ if not hasattr(self, 'regressor_'): - raise NotFittedError( + raise NotFittedError( # pragma: no cover "This instance {} is not fitted yet. Call 'fit' with " "appropriate arguments before using this method.".format( type(self))) diff --git a/mlinsights/mltree/tree_structure.py b/mlinsights/mltree/tree_structure.py index 4fe86edb..1f96517e 100644 --- a/mlinsights/mltree/tree_structure.py +++ b/mlinsights/mltree/tree_structure.py @@ -14,7 +14,8 @@ def _get_tree(obj): return obj if hasattr(obj, "tree_"): return obj.tree_ - raise AttributeError("obj is no tree: {}".format(type(obj))) + raise AttributeError( # pragma: no cover + "obj is no tree: {}".format(type(obj))) def tree_leave_index(model):