From 2598f65ff613e8b3cc6479c2769f9d06497d86f0 Mon Sep 17 00:00:00 2001 From: Kendall Date: Sat, 30 Jul 2022 07:56:59 -0400 Subject: [PATCH 1/8] added _parameter_constraints --- sklearn/preprocessing/_polynomial.py | 16 +++++++++++++++- sklearn/tests/test_common.py | 1 - 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/sklearn/preprocessing/_polynomial.py b/sklearn/preprocessing/_polynomial.py index d90411a0c8bfa..d913dafcb2aef 100644 --- a/sklearn/preprocessing/_polynomial.py +++ b/sklearn/preprocessing/_polynomial.py @@ -3,6 +3,7 @@ """ import collections import numbers +from numbers import Integral, Real from itertools import chain, combinations from itertools import combinations_with_replacement as combinations_w_r @@ -15,6 +16,7 @@ from ..utils import check_array from ..utils.deprecation import deprecated from ..utils.validation import check_is_fitted, FLOAT_DTYPES, _check_sample_weight +from ..utils._param_validation import Interval, StrOptions, Hidden from ..utils.validation import _check_feature_names_in from ..utils.stats import _weighted_percentile @@ -627,6 +629,17 @@ class SplineTransformer(TransformerMixin, BaseEstimator): [0. , 0. , 0.5 , 0.5 ]]) """ + _parameter_constraints = { + "n_knots": [Interval(Integral, 2, None, closed="left")], + "degree": [Interval(Integral, 0, None, closed="left")], + "knots": [StrOptions({"uniform", "quantile"}), "array-like"], + "extrapolation": [ + StrOptions({"error", "constant", "linear", "continue", "periodic"}) + ], + "include_bias": ["boolean"], + "order": [StrOptions({"C", "F"})], + } + def __init__( self, n_knots=5, @@ -767,6 +780,8 @@ def fit(self, X, y=None, sample_weight=None): self : object Fitted transformer. """ + self._validate_params() + X = self._validate_data( X, reset=True, @@ -936,7 +951,6 @@ def transform(self, X): spl = self.bsplines_[i] if self.extrapolation in ("continue", "error", "periodic"): - if self.extrapolation == "periodic": # With periodic extrapolation we map x to the segment # [spl.t[k], spl.t[n]]. diff --git a/sklearn/tests/test_common.py b/sklearn/tests/test_common.py index 04e6b77e8c506..e656094426cb3 100644 --- a/sklearn/tests/test_common.py +++ b/sklearn/tests/test_common.py @@ -499,7 +499,6 @@ def test_estimators_do_not_raise_errors_in_init_or_set_params(Estimator): "SpectralBiclustering", "SpectralCoclustering", "SpectralEmbedding", - "SplineTransformer", "TransformedTargetRegressor", ] From ee71eade54227bb286d71be8538af13d22791553 Mon Sep 17 00:00:00 2001 From: Kendall Date: Sat, 30 Jul 2022 08:36:45 -0400 Subject: [PATCH 2/8] fixed linting removed test from test_polynomial.py --- sklearn/preprocessing/_polynomial.py | 2 +- .../preprocessing/tests/test_polynomial.py | 99 ------------------- 2 files changed, 1 insertion(+), 100 deletions(-) diff --git a/sklearn/preprocessing/_polynomial.py b/sklearn/preprocessing/_polynomial.py index d913dafcb2aef..09c18b9c8f2da 100644 --- a/sklearn/preprocessing/_polynomial.py +++ b/sklearn/preprocessing/_polynomial.py @@ -16,7 +16,7 @@ from ..utils import check_array from ..utils.deprecation import deprecated from ..utils.validation import check_is_fitted, FLOAT_DTYPES, _check_sample_weight -from ..utils._param_validation import Interval, StrOptions, Hidden +from ..utils._param_validation import Interval, StrOptions from ..utils.validation import _check_feature_names_in from ..utils.stats import _weighted_percentile diff --git a/sklearn/preprocessing/tests/test_polynomial.py b/sklearn/preprocessing/tests/test_polynomial.py index 0ab8f0f335f43..7ef420796dd44 100644 --- a/sklearn/preprocessing/tests/test_polynomial.py +++ b/sklearn/preprocessing/tests/test_polynomial.py @@ -28,80 +28,6 @@ def is_c_contiguous(a): assert np.isfortran(est(order="F").fit_transform(X)) -@pytest.mark.parametrize( - "params, err_msg", - [ - ({"degree": -1}, "degree must be a non-negative integer"), - ({"degree": 2.5}, "degree must be a non-negative integer"), - ({"degree": "string"}, "degree must be a non-negative integer"), - ({"n_knots": 1}, "n_knots must be a positive integer >= 2."), - ({"n_knots": 1}, "n_knots must be a positive integer >= 2."), - ({"n_knots": 2.5}, "n_knots must be a positive integer >= 2."), - ({"n_knots": "string"}, "n_knots must be a positive integer >= 2."), - ({"knots": 1}, "Expected 2D array, got scalar array instead:"), - ({"knots": [1, 2]}, "Expected 2D array, got 1D array instead:"), - ( - {"knots": [[1]]}, - r"Number of knots, knots.shape\[0\], must be >= 2.", - ), - ( - {"knots": [[1, 5], [2, 6]]}, - r"knots.shape\[1\] == n_features is violated.", - ), - ( - {"knots": [[1], [1], [2]]}, - "knots must be sorted without duplicates.", - ), - ({"knots": [[2], [1]]}, "knots must be sorted without duplicates."), - ( - {"extrapolation": None}, - "extrapolation must be one of 'error', 'constant', 'linear', " - "'continue' or 'periodic'.", - ), - ( - {"extrapolation": 1}, - "extrapolation must be one of 'error', 'constant', 'linear', " - "'continue' or 'periodic'.", - ), - ( - {"extrapolation": "string"}, - "extrapolation must be one of 'error', 'constant', 'linear', " - "'continue' or 'periodic'.", - ), - ({"include_bias": None}, "include_bias must be bool."), - ({"include_bias": 1}, "include_bias must be bool."), - ({"include_bias": "string"}, "include_bias must be bool."), - ( - {"extrapolation": "periodic", "n_knots": 3, "degree": 3}, - "Periodic splines require degree < n_knots. Got n_knots=3 and degree=3.", - ), - ( - {"extrapolation": "periodic", "knots": [[0], [1]], "degree": 2}, - "Periodic splines require degree < n_knots. Got n_knots=2 and degree=2.", - ), - ], -) -def test_spline_transformer_input_validation(params, err_msg): - """Test that we raise errors for invalid input in SplineTransformer.""" - X = [[1], [2]] - - with pytest.raises(ValueError, match=err_msg): - SplineTransformer(**params).fit(X) - - -def test_spline_transformer_manual_knot_input(): - """ - Test that array-like knot positions in SplineTransformer are accepted. - """ - X = np.arange(20).reshape(10, 2) - knots = [[0.5, 1], [1.5, 2], [5, 10]] - st1 = SplineTransformer(degree=3, knots=knots, n_knots=None).fit(X) - knots = np.asarray(knots) - st2 = SplineTransformer(degree=3, knots=knots, n_knots=None).fit(X) - for i in range(X.shape[1]): - assert_allclose(st1.bsplines_[i].t, st2.bsplines_[i].t) - - @pytest.mark.parametrize("extrapolation", ["continue", "periodic"]) def test_spline_transformer_integer_knots(extrapolation): """Test that SplineTransformer accepts integer value knot positions.""" @@ -238,31 +164,6 @@ def test_spline_transformer_get_base_knot_positions( assert_allclose(base_knots, expected_knots) -@pytest.mark.parametrize( - "knots, n_knots, degree", - [ - ("uniform", 5, 3), - ("uniform", 12, 8), - ( - [[-1.0, 0.0], [0, 1.0], [0.1, 2.0], [0.2, 3.0], [0.3, 4.0], [1, 5.0]], - None, - 3, - ), - ], -) -def test_spline_transformer_periodicity_of_extrapolation(knots, n_knots, degree): - """Test that the SplineTransformer is periodic for multiple features.""" - X_1 = np.linspace((-1, 0), (1, 5), 10) - X_2 = np.linspace((1, 5), (3, 10), 10) - - splt = SplineTransformer( - knots=knots, n_knots=n_knots, degree=degree, extrapolation="periodic" - ) - splt.fit(X_1) - - assert_allclose(splt.transform(X_1), splt.transform(X_2)) - - @pytest.mark.parametrize(["bias", "intercept"], [(True, False), (False, True)]) def test_spline_transformer_periodic_linear_regression(bias, intercept): """Test that B-splines fit a periodic curve pretty well.""" From 9345121a802fdf44ee5e48b3c84b83fb667fad66 Mon Sep 17 00:00:00 2001 From: Kendall Date: Sat, 30 Jul 2022 09:00:52 -0400 Subject: [PATCH 3/8] linting --- sklearn/preprocessing/_polynomial.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sklearn/preprocessing/_polynomial.py b/sklearn/preprocessing/_polynomial.py index 09c18b9c8f2da..585c21eb2d1ab 100644 --- a/sklearn/preprocessing/_polynomial.py +++ b/sklearn/preprocessing/_polynomial.py @@ -3,7 +3,7 @@ """ import collections import numbers -from numbers import Integral, Real +from numbers import Integral from itertools import chain, combinations from itertools import combinations_with_replacement as combinations_w_r From e5ea157b481b39f219dda80ade01d8be0bbb68de Mon Sep 17 00:00:00 2001 From: Kendall Date: Wed, 3 Aug 2022 18:02:03 -0400 Subject: [PATCH 4/8] added _parameter_constraints for SpectralEmbedding --- sklearn/manifold/_spectral_embedding.py | 24 ++++++++++++++++++++++++ sklearn/tests/test_common.py | 1 - 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/sklearn/manifold/_spectral_embedding.py b/sklearn/manifold/_spectral_embedding.py index b43329a4f89ca..a0fc6823e2a09 100644 --- a/sklearn/manifold/_spectral_embedding.py +++ b/sklearn/manifold/_spectral_embedding.py @@ -11,6 +11,7 @@ from scipy import sparse from scipy.linalg import eigh from scipy.sparse.linalg import eigsh +from numbers import Integral, Real from scipy.sparse.csgraph import connected_components from scipy.sparse.csgraph import laplacian as csgraph_laplacian @@ -22,6 +23,7 @@ ) from ..utils._arpack import _init_arpack_v0 from ..utils.extmath import _deterministic_vector_sign_flip +from ..utils._param_validation import Interval, StrOptions from ..utils.fixes import lobpcg from ..metrics.pairwise import rbf_kernel from ..neighbors import kneighbors_graph, NearestNeighbors @@ -542,6 +544,27 @@ class SpectralEmbedding(BaseEstimator): (100, 2) """ + _parameter_constraints = { + "n_components": [Interval(Integral, 1, None, closed="left")], + "affinity": [ + StrOptions( + { + "nearest_neighbors", + "rbf", + "precomputed", + "precomputed_nearest_neighbors", + }, + ), + callable, + ], + "gamma": [Interval(Real, 0, None, closed="left"), None], + "random_state": ["random_state", None], + "eigen_solver": [StrOptions({"arpack", "lobpcg", "amg"}), None], + "eigen_tol": [Interval(Real, 0, None, closed="left"), StrOptions({"auto"})], + "n_neighbors": [Interval(Integral, 1, None, closed="left"), None], + "n_jobs": [None, Integral], + } + def __init__( self, n_components=2, @@ -649,6 +672,7 @@ def fit(self, X, y=None): self : object Returns the instance itself. """ + self._validate_params() X = self._validate_data(X, accept_sparse="csr", ensure_min_samples=2) diff --git a/sklearn/tests/test_common.py b/sklearn/tests/test_common.py index ab90a0dda301c..06c3d8409ca76 100644 --- a/sklearn/tests/test_common.py +++ b/sklearn/tests/test_common.py @@ -516,7 +516,6 @@ def test_estimators_do_not_raise_errors_in_init_or_set_params(Estimator): "SkewedChi2Sampler", "SpectralBiclustering", "SpectralCoclustering", - "SpectralEmbedding", "TransformedTargetRegressor", ] From ac7c955bd1ea1e00e975facdd5a5b4fdec58552f Mon Sep 17 00:00:00 2001 From: Kendall Date: Wed, 24 Aug 2022 19:03:10 -0400 Subject: [PATCH 5/8] Removed changes related to `SplineTransformer` --- sklearn/preprocessing/_polynomial.py | 13 --- .../preprocessing/tests/test_polynomial.py | 99 +++++++++++++++++++ sklearn/tests/test_common.py | 1 + 3 files changed, 100 insertions(+), 13 deletions(-) diff --git a/sklearn/preprocessing/_polynomial.py b/sklearn/preprocessing/_polynomial.py index 585c21eb2d1ab..be82dfabbd0b2 100644 --- a/sklearn/preprocessing/_polynomial.py +++ b/sklearn/preprocessing/_polynomial.py @@ -629,17 +629,6 @@ class SplineTransformer(TransformerMixin, BaseEstimator): [0. , 0. , 0.5 , 0.5 ]]) """ - _parameter_constraints = { - "n_knots": [Interval(Integral, 2, None, closed="left")], - "degree": [Interval(Integral, 0, None, closed="left")], - "knots": [StrOptions({"uniform", "quantile"}), "array-like"], - "extrapolation": [ - StrOptions({"error", "constant", "linear", "continue", "periodic"}) - ], - "include_bias": ["boolean"], - "order": [StrOptions({"C", "F"})], - } - def __init__( self, n_knots=5, @@ -780,8 +769,6 @@ def fit(self, X, y=None, sample_weight=None): self : object Fitted transformer. """ - self._validate_params() - X = self._validate_data( X, reset=True, diff --git a/sklearn/preprocessing/tests/test_polynomial.py b/sklearn/preprocessing/tests/test_polynomial.py index 7ef420796dd44..0ab8f0f335f43 100644 --- a/sklearn/preprocessing/tests/test_polynomial.py +++ b/sklearn/preprocessing/tests/test_polynomial.py @@ -28,6 +28,80 @@ def is_c_contiguous(a): assert np.isfortran(est(order="F").fit_transform(X)) +@pytest.mark.parametrize( + "params, err_msg", + [ + ({"degree": -1}, "degree must be a non-negative integer"), + ({"degree": 2.5}, "degree must be a non-negative integer"), + ({"degree": "string"}, "degree must be a non-negative integer"), + ({"n_knots": 1}, "n_knots must be a positive integer >= 2."), + ({"n_knots": 1}, "n_knots must be a positive integer >= 2."), + ({"n_knots": 2.5}, "n_knots must be a positive integer >= 2."), + ({"n_knots": "string"}, "n_knots must be a positive integer >= 2."), + ({"knots": 1}, "Expected 2D array, got scalar array instead:"), + ({"knots": [1, 2]}, "Expected 2D array, got 1D array instead:"), + ( + {"knots": [[1]]}, + r"Number of knots, knots.shape\[0\], must be >= 2.", + ), + ( + {"knots": [[1, 5], [2, 6]]}, + r"knots.shape\[1\] == n_features is violated.", + ), + ( + {"knots": [[1], [1], [2]]}, + "knots must be sorted without duplicates.", + ), + ({"knots": [[2], [1]]}, "knots must be sorted without duplicates."), + ( + {"extrapolation": None}, + "extrapolation must be one of 'error', 'constant', 'linear', " + "'continue' or 'periodic'.", + ), + ( + {"extrapolation": 1}, + "extrapolation must be one of 'error', 'constant', 'linear', " + "'continue' or 'periodic'.", + ), + ( + {"extrapolation": "string"}, + "extrapolation must be one of 'error', 'constant', 'linear', " + "'continue' or 'periodic'.", + ), + ({"include_bias": None}, "include_bias must be bool."), + ({"include_bias": 1}, "include_bias must be bool."), + ({"include_bias": "string"}, "include_bias must be bool."), + ( + {"extrapolation": "periodic", "n_knots": 3, "degree": 3}, + "Periodic splines require degree < n_knots. Got n_knots=3 and degree=3.", + ), + ( + {"extrapolation": "periodic", "knots": [[0], [1]], "degree": 2}, + "Periodic splines require degree < n_knots. Got n_knots=2 and degree=2.", + ), + ], +) +def test_spline_transformer_input_validation(params, err_msg): + """Test that we raise errors for invalid input in SplineTransformer.""" + X = [[1], [2]] + + with pytest.raises(ValueError, match=err_msg): + SplineTransformer(**params).fit(X) + + +def test_spline_transformer_manual_knot_input(): + """ + Test that array-like knot positions in SplineTransformer are accepted. + """ + X = np.arange(20).reshape(10, 2) + knots = [[0.5, 1], [1.5, 2], [5, 10]] + st1 = SplineTransformer(degree=3, knots=knots, n_knots=None).fit(X) + knots = np.asarray(knots) + st2 = SplineTransformer(degree=3, knots=knots, n_knots=None).fit(X) + for i in range(X.shape[1]): + assert_allclose(st1.bsplines_[i].t, st2.bsplines_[i].t) + + @pytest.mark.parametrize("extrapolation", ["continue", "periodic"]) def test_spline_transformer_integer_knots(extrapolation): """Test that SplineTransformer accepts integer value knot positions.""" @@ -164,6 +238,31 @@ def test_spline_transformer_get_base_knot_positions( assert_allclose(base_knots, expected_knots) +@pytest.mark.parametrize( + "knots, n_knots, degree", + [ + ("uniform", 5, 3), + ("uniform", 12, 8), + ( + [[-1.0, 0.0], [0, 1.0], [0.1, 2.0], [0.2, 3.0], [0.3, 4.0], [1, 5.0]], + None, + 3, + ), + ], +) +def test_spline_transformer_periodicity_of_extrapolation(knots, n_knots, degree): + """Test that the SplineTransformer is periodic for multiple features.""" + X_1 = np.linspace((-1, 0), (1, 5), 10) + X_2 = np.linspace((1, 5), (3, 10), 10) + + splt = SplineTransformer( + knots=knots, n_knots=n_knots, degree=degree, extrapolation="periodic" + ) + splt.fit(X_1) + + assert_allclose(splt.transform(X_1), splt.transform(X_2)) + + @pytest.mark.parametrize(["bias", "intercept"], [(True, False), (False, True)]) def test_spline_transformer_periodic_linear_regression(bias, intercept): """Test that B-splines fit a periodic curve pretty well.""" diff --git a/sklearn/tests/test_common.py b/sklearn/tests/test_common.py index 06c3d8409ca76..dd6ede13d47d2 100644 --- a/sklearn/tests/test_common.py +++ b/sklearn/tests/test_common.py @@ -516,6 +516,7 @@ def test_estimators_do_not_raise_errors_in_init_or_set_params(Estimator): "SkewedChi2Sampler", "SpectralBiclustering", "SpectralCoclustering", + "SplineTransformer", "TransformedTargetRegressor", ] From e91aa376c602cb50b63ca164ed4ab7732b2ab006 Mon Sep 17 00:00:00 2001 From: Kendall Date: Wed, 24 Aug 2022 19:33:07 -0400 Subject: [PATCH 6/8] linting --- sklearn/preprocessing/_polynomial.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/sklearn/preprocessing/_polynomial.py b/sklearn/preprocessing/_polynomial.py index be82dfabbd0b2..e6998981e9e31 100644 --- a/sklearn/preprocessing/_polynomial.py +++ b/sklearn/preprocessing/_polynomial.py @@ -3,7 +3,6 @@ """ import collections import numbers -from numbers import Integral from itertools import chain, combinations from itertools import combinations_with_replacement as combinations_w_r @@ -16,7 +15,6 @@ from ..utils import check_array from ..utils.deprecation import deprecated from ..utils.validation import check_is_fitted, FLOAT_DTYPES, _check_sample_weight -from ..utils._param_validation import Interval, StrOptions from ..utils.validation import _check_feature_names_in from ..utils.stats import _weighted_percentile From 829c954e0b8580e21b5cc55090984321af3f3094 Mon Sep 17 00:00:00 2001 From: jeremie du boisberranger Date: Wed, 31 Aug 2022 22:10:20 +0200 Subject: [PATCH 7/8] address review comments and remove old validation --- sklearn/manifold/_spectral_embedding.py | 24 +++---------------- .../manifold/tests/test_spectral_embedding.py | 11 --------- sklearn/preprocessing/_polynomial.py | 1 + 3 files changed, 4 insertions(+), 32 deletions(-) diff --git a/sklearn/manifold/_spectral_embedding.py b/sklearn/manifold/_spectral_embedding.py index a0fc6823e2a09..13181a653d1dc 100644 --- a/sklearn/manifold/_spectral_embedding.py +++ b/sklearn/manifold/_spectral_embedding.py @@ -5,13 +5,13 @@ # License: BSD 3 clause +from numbers import Integral, Real import warnings import numpy as np from scipy import sparse from scipy.linalg import eigh from scipy.sparse.linalg import eigsh -from numbers import Integral, Real from scipy.sparse.csgraph import connected_components from scipy.sparse.csgraph import laplacian as csgraph_laplacian @@ -544,7 +544,7 @@ class SpectralEmbedding(BaseEstimator): (100, 2) """ - _parameter_constraints = { + _parameter_constraints: dict = { "n_components": [Interval(Integral, 1, None, closed="left")], "affinity": [ StrOptions( @@ -558,7 +558,7 @@ class SpectralEmbedding(BaseEstimator): callable, ], "gamma": [Interval(Real, 0, None, closed="left"), None], - "random_state": ["random_state", None], + "random_state": ["random_state"], "eigen_solver": [StrOptions({"arpack", "lobpcg", "amg"}), None], "eigen_tol": [Interval(Real, 0, None, closed="left"), StrOptions({"auto"})], "n_neighbors": [Interval(Integral, 1, None, closed="left"), None], @@ -677,24 +677,6 @@ def fit(self, X, y=None): X = self._validate_data(X, accept_sparse="csr", ensure_min_samples=2) random_state = check_random_state(self.random_state) - if isinstance(self.affinity, str): - if self.affinity not in { - "nearest_neighbors", - "rbf", - "precomputed", - "precomputed_nearest_neighbors", - }: - raise ValueError( - "%s is not a valid affinity. Expected " - "'precomputed', 'rbf', 'nearest_neighbors' " - "or a callable." - % self.affinity - ) - elif not callable(self.affinity): - raise ValueError( - "'affinity' is expected to be an affinity name or a callable. Got: %s" - % self.affinity - ) affinity_matrix = self._get_affinity_matrix(X) self.embedding_ = spectral_embedding( diff --git a/sklearn/manifold/tests/test_spectral_embedding.py b/sklearn/manifold/tests/test_spectral_embedding.py index 37412eac14490..cf7f253b66de1 100644 --- a/sklearn/manifold/tests/test_spectral_embedding.py +++ b/sklearn/manifold/tests/test_spectral_embedding.py @@ -351,17 +351,6 @@ def test_spectral_embedding_unknown_eigensolver(seed=36): se.fit(S) -def test_spectral_embedding_unknown_affinity(seed=36): - # Test that SpectralClustering fails with an unknown affinity type - se = SpectralEmbedding( - n_components=1, - affinity="", - random_state=np.random.RandomState(seed), - ) - with pytest.raises(ValueError): - se.fit(S) - - def test_connectivity(seed=36): # Test that graph connectivity test works as expected graph = np.array( diff --git a/sklearn/preprocessing/_polynomial.py b/sklearn/preprocessing/_polynomial.py index f3a77df066abc..b895dda65b832 100644 --- a/sklearn/preprocessing/_polynomial.py +++ b/sklearn/preprocessing/_polynomial.py @@ -926,6 +926,7 @@ def transform(self, X): spl = self.bsplines_[i] if self.extrapolation in ("continue", "error", "periodic"): + if self.extrapolation == "periodic": # With periodic extrapolation we map x to the segment # [spl.t[k], spl.t[n]]. From 7210082df5e3f3b06bb08c366639fc49d252b8ae Mon Sep 17 00:00:00 2001 From: jeremie du boisberranger Date: Wed, 31 Aug 2022 22:15:37 +0200 Subject: [PATCH 8/8] cln --- sklearn/preprocessing/_polynomial.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sklearn/preprocessing/_polynomial.py b/sklearn/preprocessing/_polynomial.py index b895dda65b832..f3a77df066abc 100644 --- a/sklearn/preprocessing/_polynomial.py +++ b/sklearn/preprocessing/_polynomial.py @@ -926,7 +926,6 @@ def transform(self, X): spl = self.bsplines_[i] if self.extrapolation in ("continue", "error", "periodic"): - if self.extrapolation == "periodic": # With periodic extrapolation we map x to the segment # [spl.t[k], spl.t[n]].