Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Smoothing Splines: Theory and Implementation

This document outlines the theoretical background for the scatter_smooth package, synthesizing concepts from The Elements of Statistical Learning (Hastie, Tibshirani, Friedman), specifically Chapter 5. It also discusses the implementation details (Reinsch form vs. Basis form) and compares the features with R’s smooth.spline.

1. The Smoothing Spline Problem

The goal of smoothing splines is to find a function f(x)f(x) that fits the data {(xi,yi)}i=1N\{(x_i, y_i)\}_{i=1}^N well while remaining smooth. This is formulated as the minimization of a penalized residual sum of squares:

min⁡f∑i=1Nwi(yi−f(xi))2+λ∫[f′′(t)]2dt\min_{f} \sum_{i=1}^N w_i (y_i - f(x_i))^2 + \lambda \int [f''(t)]^2 dt

where λ≥0\lambda \ge 0 is a smoothing parameter and wi>0w_i > 0 are weights.

The Solution: Natural Cubic Spline

Remarkably, the solution to this infinite-dimensional optimization problem is a finite-dimensional Natural Cubic Spline with knots at the unique values of xix_i.

A natural cubic spline is a piecewise cubic polynomial that is continuous up to its second derivative, and linear beyond the boundary knots (i.e., its second derivative is zero at the boundaries).

2. Matrix Formulation

Since the solution is a linear combination of basis functions, we can write:

f(x)=∑j=1NθjNj(x)f(x) = \sum_{j=1}^N \theta_j N_j(x)

where Nj(x)N_j(x) are the natural cubic spline basis functions.

The optimization problem reduces to:

min⁡θ(y−Nθ)TW(y−Nθ)+λθTΩNθ\min_{ \theta} (y - N \theta)^T W (y - N \theta) + \lambda \theta^T \Omega_N \theta

where:

The solution is a generalized ridge regression:

θ^=(NTWN+λΩN)−1NTWy\hat{\theta} = (N^T W N + \lambda \Omega_N)^{-1} N^T W y

Reinsch Form vs. Basis Form

There are three primary ways to compute the solution in our package:

A. The Natural Spline Basis Form (engine='natural')

We explicitly construct the basis matrix NN and the penalty matrix Ω\Omega.

Why Basis Form?

B. The Reinsch Form (engine='reinsch')

If knots are placed at every data point (K=NK=N), the solution vector f=(f(x1),…,f(xN))T\mathbf{f} = (f(x_1), \dots, f(x_N))^T can be found directly without forming NN.

f^=(W+λK)−1Wy\hat{\mathbf{f}} = (W + \lambda K)^{-1} W y

where K=QR−1QTK = Q R^{-1} Q^T.

This allows solving the system in O(N)O(N) time using banded solvers, making it extremely fast for large NN. This matches the algorithm used in R’s smooth.spline.

C. The B-Spline Basis Form (engine='bspline')

Uses the B-spline basis with compact support.

D. Automatic (engine='auto')

Uses Reinsch form if possible, else uses the B-Spline form.

3. Degrees of Freedom (dfdf)

The smoothing parameter λ\lambda is abstract. A more intuitive measure of model complexity is the effective degrees of freedom (dfdf).

df(λ)=trace(Sλ)df(\lambda) = \text{trace}(\mathbf{S}_\lambda)

where Sλ\mathbf{S}_\lambda is the smoother matrix such that y^=Sλy\hat{y} = \mathbf{S}_\lambda y.

For the Reinsch form, we compute the trace in O(N)O(N) time using Takahashi’s equations (calculating selected elements of the inverse of a banded matrix via Cholesky decomposition).

For the Basis form (Natural), we compute the trace of the dense matrix inverse.

For the B-spline form, we also use Takahashi’s equations on the banded system to compute the trace efficiently in O(N)O(N) time.

3.1. Details on Degrees of Freedom Computation

Trace of Inverse via Takahashi’s Algorithm

Computing the degrees of freedom requires the trace of the smoother matrix SλS_\lambda. In the basis form:

y^=Nθ^=N(NTWN+λΩN)−1NTWy\hat{y} = N \hat{\theta} = N (N^T W N + \lambda \Omega_N)^{-1} N^T W y

Thus, Sλ=N(NTWN+λΩN)−1NTWS_\lambda = N (N^T W N + \lambda \Omega_N)^{-1} N^T W. Using the cyclic property of the trace:

df(λ)=trace(Sλ)=trace((NTWN+λΩN)−1NTWN)\text{df}(\lambda) = \text{trace}(S_\lambda) = \text{trace}((N^T W N + \lambda \Omega_N)^{-1} N^T W N)

Let A=NTWNA = N^T W N. We need to compute trace((A+λΩN)−1A)\text{trace}((A + \lambda \Omega_N)^{-1} A).

For banded matrices (like those in our B-spline implementation), we can compute this trace efficiently in O(N)O(N) time without inverting the full matrix.

Let H=A+λΩNH = A + \lambda \Omega_N. We need trace(H−1A)\text{trace}(H^{-1} A). Since AA and ΩN\Omega_N are banded, HH is banded. Let H=UTUH = U^T U be the Cholesky decomposition of HH, where UU is an upper triangular banded matrix. We need elements of Z=H−1Z = H^{-1} specifically those where AA is non-zero. Since AA is banded, we only need the elements of ZZ within the bandwidth of AA.

Takahashi’s Equations provide a way to compute the elements of ZZ within the band of HH (which includes the band of AA) using a backward recurrence starting from the bottom-right corner.

  1. Compute ZNN=1/UNN2Z_{NN} = 1 / U_{NN}^2.

  2. Iterate backwards from i=N−1i = N-1 to 1:

    • Compute off-diagonal elements ZijZ_{ij} inside the band using previously computed ZZ values and UU.

    • Compute diagonal element ZiiZ_{ii}.

Once we have the band of ZZ, we can compute the trace of the product ZAZ A efficiently:

trace(ZA)=∑i,jZijAji\text{trace}(Z A) = \sum_{i,j} Z_{ij} A_{ji}

Since AA is symmetric and banded, the sum only involves indices (i,j)(i,j) where ∣i−j∣≤bandwidth|i-j| \le \text{bandwidth}, which are exactly the elements we computed.

Handling Natural Boundary Conditions (The Projection Matrix PP)

The “natural” boundary conditions (zero second derivative at endpoints) imply linear constraints on the B-spline coefficients θ\theta.

θ0=ws1θ1+ws2θ2\theta_0 = w_{s1} \theta_1 + w_{s2} \theta_2

θN−1=we1θN−2+we2θN−3\theta_{N-1} = w_{e1} \theta_{N-2} + w_{e2} \theta_{N-3}

These constraints reduce the number of free parameters from NN to N−2N-2. We can express the full parameter vector θ\theta as a linear transformation of the reduced parameters γ=(θ1,…,θN−2)T\gamma = (\theta_1, \dots, \theta_{N-2})^T:

θ=Pγ\theta = P \gamma

where PP is an N×(N−2)N \times (N-2) sparse projection matrix. PP is mostly an identity matrix (shifted), with the first row and last row having non-zero entries in the first two and last two columns respectively.

The optimization problem in terms of γ\gamma becomes:

min⁡γ(y−NPγ)TW(y−NPγ)+λγTPTΩNPγ\min_{\gamma} (y - N P \gamma)^T W (y - N P \gamma) + \lambda \gamma^T P^T \Omega_N P \gamma

The normal equations are:

(PTNTWNP+λPTΩNP)γ=PTNTWy(P^T N^T W N P + \lambda P^T \Omega_N P) \gamma = P^T N^T W y

Let A~=PTNTWNP\tilde{A} = P^T N^T W N P and Ω~=PTΩNP\tilde{\Omega} = P^T \Omega_N P. These matrices remain banded (with slightly modified corners). We solve for γ\gamma and then reconstruct θ=Pγ\theta = P \gamma.

Our bspline engine implements this by:

  1. Constructing the full banded matrices NTWNN^T W N and ΩN\Omega_N.

  2. Applying the projection PP in-place to the bands (modifying the top-left and bottom-right blocks).

  3. Solving the reduced banded system.

  4. Using Takahashi’s algorithm on the reduced system to compute the degrees of freedom efficiently.

4. Comparison with R’s smooth.spline

Featuresmooth.spline (R)scatter_smooth (Python)
AlgorithmReinsch Form (O(N)O(N))engine='auto' (selects best), engine='reinsch' (O(N)O(N)), engine='natural' (O(NK2)O(NK^2)), engine='bspline' (Banded)
KnotsAll unique xx (default) or nknotsnknotsAll unique xx or specified n_knots
Input λ\lambdaVia spar or lambdaVia lamval
Input dfdfSupportedSupported (via root finding)
Automatic TuningGCV / CVGCV
WeightsSupportedSupported
DerivativesBuilt-inExposed via predict(deriv=...)
ExtrapolationLinear (via predict)Linear (explicitly handled)

Key Differences

  1. Speed: For N=10,000N=10,000, smooth.spline is faster because it exploits the band structure of the full system. Our implementation allows n_knots < N (Regression Splines), which restores speed (O(NK2)O(N K^2)) and reduces overfitting risk, a feature smooth.spline supports via nknots.

    • engine='reinsch': Fast O(N)O(N) implementation for when knots = unique xx. Matches smooth.spline performance.

    • engine='natural': Uses the natural cubic spline basis. Good for general use and when K<NK < N.

    • engine='bspline': Uses B-spline basis. Efficient and stable, good for sparse matrices.

  2. Spar (R specific): R uses a scaling parameter spar related to λ\lambda. We use raw λ\lambda (scaled by xx-range cubed for numerical stability).

5. References

  1. Hastie, T., Tibshirani, R., & Friedman, J. (2009). The Elements of Statistical Learning: Data Mining, Inference, and Prediction. 2nd Edition. Springer. (Chapter 5).

  2. Green, P. J., & Silverman, B. W. (1994). Nonparametric Regression and Generalized Linear Models: A Roughness Penalty Approach. Chapman and Hall/CRC.