def test_spline_transformer_periodicity_of_extrapolation(knots, n_knots, degree): """Test that the SplineTransformer is periodic for multiple features.""" X_1 = linspace((-1, 0), (1, 5), 10) X_2 = 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))
def test_spline_transformer_extrapolation(bias, intercept, degree): """Test that B-spline extrapolation works correctly.""" # we use a straight line for that X = np.linspace(-1, 1, 100)[:, None] y = X.squeeze() # 'constant' pipe = Pipeline( [ [ "spline", SplineTransformer( n_knots=4, degree=degree, include_bias=bias, extrapolation="constant", ), ], ["ols", LinearRegression(fit_intercept=intercept)], ] ) pipe.fit(X, y) assert_allclose(pipe.predict([[-10], [5]]), [-1, 1]) # 'linear' pipe = Pipeline( [ [ "spline", SplineTransformer( n_knots=4, degree=degree, include_bias=bias, extrapolation="linear", ), ], ["ols", LinearRegression(fit_intercept=intercept)], ] ) pipe.fit(X, y) assert_allclose(pipe.predict([[-10], [5]]), [-10, 5]) # 'error' splt = SplineTransformer( n_knots=4, degree=degree, include_bias=bias, extrapolation="error" ) splt.fit(X) with pytest.raises(ValueError): splt.transform([[-10]]) with pytest.raises(ValueError): splt.transform([[5]])
def test_spline_transformer_unity_decomposition(degree, n_knots, knots, extrapolation): """Test that B-splines are indeed a decomposition of unity. Splines basis functions must sum up to 1 per row, if we stay in between boundaries. """ X = np.linspace(0, 1, 100)[:, None] # make the boundaries 0 and 1 part of X_train, for sure. X_train = np.r_[[[0]], X[::2, :], [[1]]] X_test = X[1::2, :] if extrapolation == "periodic": n_knots = n_knots + degree # periodic splines require degree < n_knots splt = SplineTransformer( n_knots=n_knots, degree=degree, knots=knots, include_bias=True, extrapolation=extrapolation, ) splt.fit(X_train) for X in [X_train, X_test]: assert_allclose(np.sum(splt.transform(X), axis=1), 1)
def test_spline_transformer_n_features_out(n_knots, include_bias, degree): """Test that transform results in n_features_out_ features.""" splt = SplineTransformer(n_knots=n_knots, degree=degree, include_bias=include_bias) X = np.linspace(0, 1, 10)[:, None] splt.fit(X) assert splt.transform(X).shape[1] == splt.n_features_out_
# polynomials and show very nice and smooth behaviour. They have also good # options to control the extrapolation, which defaults to continue with a # constant. Note that most often, you would rather increase the number of knots # but keep ``degree=3``. # # In order to give more insights into the generated feature bases, we plot all # columns of both transformers separately. fig, axes = plt.subplots(ncols=2, figsize=(16, 5)) pft = PolynomialFeatures(degree=3).fit(X_train) axes[0].plot(x_plot, pft.transform(X_plot)) axes[0].legend(axes[0].lines, [f"degree {n}" for n in range(4)]) axes[0].set_title("PolynomialFeatures") splt = SplineTransformer(n_knots=4, degree=3).fit(X_train) axes[1].plot(x_plot, splt.transform(X_plot)) axes[1].legend(axes[1].lines, [f"spline {n}" for n in range(6)]) axes[1].set_title("SplineTransformer") # plot knots of spline knots = splt.bsplines_[0].t axes[1].vlines(knots[3:-3], ymin=0, ymax=0.8, linestyles="dashed") plt.show() # %% # In the left plot, we recognize the lines corresponding to simple monomials # from ``x**0`` to ``x**3``. In the right figure, we see the six B-spline # basis functions of ``degree=3`` and also the four knot positions that were # chosen during ``fit``. Note that there are ``degree`` number of additional # knots each to the left and to the right of the fitted interval. These are # there for technical reasons, so we refrain from showing them. Every basis
# polynomials and show very nice and smooth behaviour. They have also good # options to control the extrapolation, which defaults to continue with a # constant. Note that most often, you would rather increase the number of knots # but keep ``degree=3``. # # In order to give more insights into the generated feature bases, we plot all # columns of both transformers separately. fig, axes = plt.subplots(ncols=2, figsize=(16, 5)) pft = PolynomialFeatures(degree=3).fit(X_train) axes[0].plot(x_plot, pft.transform(X_plot)) axes[0].legend(axes[0].lines, [f"degree {n}" for n in range(4)]) axes[0].set_title("PolynomialFeatures") splt = SplineTransformer(n_knots=4, degree=3).fit(X_train) axes[1].plot(x_plot, splt.transform(X_plot)) axes[1].legend(axes[1].lines, [f"spline {n}" for n in range(4)]) axes[1].set_title("SplineTransformer") # plot knots of spline knots = splt.bsplines_[0].t axes[1].vlines(knots[3:-3], ymin=0, ymax=0.8, linestyles='dashed') plt.show() # %% # In the left plot, we recognize the lines corresponding to simple monomials # from ``x**0`` to ``x**3``. In the right figure, we see the four B-spline # basis functions of ``degree=3`` and also the four knot positions that were # chosen during ``fit``. Note that there are ``degree`` number of additional # knots each to the left and to the right of the fitted interval. These are # there for technical reasons, so we refrain from showing them. Every basis