Splines

Published on 2026-11-04·v1.0

Learning Objectives

  • Explain why many low-degree pieces avoid the oscillation of one high-degree polynomial, and measure the difference on Runge's function.
  • Count the conditions that define a cubic spline, and derive the tridiagonal system for its second derivatives.
  • Compare natural, clamped and not-a-knot boundaries, and predict the order of each from what it assumes at the ends.
  • Implement a cubic spline and check it against scipy.interpolate.CubicSpline.
  • Recognize where a C2C^2 spline misleads (overshoot, extrapolation, noisy data) and reach for a shape-preserving alternative.

Context & Motivation

polynomial-interpolation-lagrange-and-newton left an open problem: Chebyshev nodes rescue a high-degree polynomial, but real data usually arrives on a grid someone else chose, and there the polynomial diverges. Splines keep the grid and give up the single formula. They use a low-degree polynomial on each interval between nodes and join the pieces smoothly. The degree stays fixed while the number of pieces grows, so accuracy comes from hh getting small rather than from the degree getting large, and the effect of one data value fades quickly with distance.

The name comes from the flexible strip draftsmen bent through fixed points. Cubic splines are a common default for smooth curves through data because they are cheap to build, with one tridiagonal solve, and cheap to evaluate.

Core Theory

Linear splines

Joining the points with straight segments gives the linear spline. If f∈C2[a,b]f\in C^2[a,b] and hh is the largest gap between nodes, the error formula for two nodes gives

∣f(x)−S(x)∣≤h28max⁡[a,b]∣f′′∣,\lvert f(x)-S(x)\rvert \le \frac{h^2}{8}\max_{[a,b]}\lvert f''\rvert,

because ∣(x−xi)(x−xi+1)∣≤h2/4\lvert(x-x_i)(x-x_{i+1})\rvert\le h^2/4 on each interval. The map from data to interpolant has absolute condition number 1 in the max norm: a change of size ε\varepsilon in the data changes SS by exactly ε\varepsilon in that norm. The price is a corner at every node.

Counting the conditions of a cubic spline

A cubic spline on nodes x0<⋯<xnx_0<\cdots<x_n is a function that is a cubic on each interval, lies in C2[x0,xn]C^2[x_0,x_n], and interpolates the data. With nn intervals there are 4n4n coefficients. Matching the data at both ends of each interval gives 2n2n conditions. Continuity of S′S' and S′′S'' at the n−1n-1 interior nodes gives 2(n−1)2(n-1) more. That is 4n−24n-2, so exactly two conditions remain free, and they are the boundary conditions.

The system for the second derivatives

Let Mi=S′′(xi)M_i = S''(x_i), hi=xi+1−xih_i = x_{i+1}-x_i and δi=(yi+1−yi)/hi\delta_i = (y_{i+1}-y_i)/h_i. On each interval S′′S'' is linear from MiM_i to Mi+1M_{i+1}. Integrating twice and imposing the data gives, on [xi,xi+1][x_i,x_{i+1}],

S(x)=Mi(xi+1−x)3+Mi+1(x−xi)36hi+(yihi−Mihi6)(xi+1−x)+(yi+1hi−Mi+1hi6)(x−xi).S(x) = \frac{M_i(x_{i+1}-x)^3 + M_{i+1}(x-x_i)^3}{6h_i} + \Big(\frac{y_i}{h_i}-\frac{M_ih_i}{6}\Big)(x_{i+1}-x) + \Big(\frac{y_{i+1}}{h_i}-\frac{M_{i+1}h_i}{6}\Big)(x-x_i).

Values and S′′S'' now match by construction. Requiring S′S' to agree from both sides at an interior node gives, for i=1,…,n−1i=1,\ldots,n-1,

hi−1Mi−1+2(hi−1+hi)Mi+hiMi+1=6(δi−δi−1).h_{i-1}M_{i-1} + 2(h_{i-1}+h_i)M_i + h_iM_{i+1} = 6(\delta_i-\delta_{i-1}).

This system is the reason splines are practical. It is tridiagonal, so it is solved in O(n)O(n) operations, not O(n3)O(n^3). With natural or clamped ends it is strictly diagonally dominant by rows on any mesh, since 2(hi−1+hi)>hi−1+hi2(h_{i-1}+h_i) > h_{i-1}+h_i, so it is invertible and needs no pivoting (compare lu-decomposition-and-partial-pivoting and the dominance condition in iterative-methods-jacobi-and-gauss-seidel). On a uniform mesh with natural ends it is hh times the matrix with 4 on the diagonal and 1 beside it, whose eigenvalues lie in (2,6)(2,6), so κ2<3\kappa_2<3: measured 2.812.81, 2.9982.998, 2.999982.99998 for n=10,100,1000n=10,100,1000. Compare that with the Vandermonde matrices of the previous lesson.

Boundary conditions

  • Natural: M0=Mn=0M_0 = M_n = 0. It needs no extra data, and it asserts that ff has no curvature at the ends.
  • Clamped: S′(x0)=s0S'(x_0) = s_0 and S′(xn)=snS'(x_n) = s_n for given slopes. The added equations are 2h0M0+h0M1=6(δ0−s0)2h_0M_0 + h_0M_1 = 6(\delta_0-s_0) and hn−1Mn−1+2hn−1Mn=6(sn−δn−1)h_{n-1}M_{n-1}+2h_{n-1}M_n = 6(s_n-\delta_{n-1}).
  • Not-a-knot: S′′′S''' is continuous at x1x_1 and xn−1x_{n-1}, so the first two pieces are one cubic, and likewise the last two. It needs no extra data and is the default in CubicSpline.
  • Periodic: S′S' and S′′S'' match between the two ends, for data with y0=yny_0=y_n.

Order. If f∈C4[a,b]f\in C^4[a,b] on a uniform mesh, the not-a-knot spline satisfies ∥f−S∥∞≤Ch4\lVert f-S\rVert_\infty\le Ch^4 for small enough hh. Clamped splines with the true slopes reach the same order (Example 4). The natural spline forces S′′=0S''=0 at the ends, so when f′′≠0f''\ne0 there it is only O(h2)O(h^2), and the loss sits at the ends.

Minimum curvature

Among all g∈C2[x0,xn]g\in C^2[x_0,x_n] that interpolate the data, the natural spline minimizes ∫(g′′)2 dx\int (g'')^2\,dx. With both end slopes fixed, the clamped spline minimizes it among interpolants with those slopes. This integral only approximates the bending energy of a physical strip: true curvature is g′′/(1+g′2)3/2g''/(1+g'^2)^{3/2}, so the two agree only for small slopes.

Worked Examples

Example 1: a natural spline by hand

Take nodes 0,1,2,30,1,2,3 with values 1,2,0,11,2,0,1. Then hi=1h_i=1 and δ=(1,−2,1)\delta = (1,-2,1), and with M0=M3=0M_0=M_3=0 the system is

4M1+M2=6(−2−1)=−18,M1+4M2=6(1+2)=18,\begin{aligned}4M_1 + M_2 &= 6(-2-1) = -18,\\ M_1 + 4M_2 &= 6(1+2) = 18,\end{aligned}

so M1=−6M_1=-6 and M2=6M_2=6. With t=x−xit = x-x_i, the pieces are

S0=1+2t−t3,S1=2−t−3t2+2t3,S2=−t+3t2−t3.\begin{aligned}S_0 &= 1 + 2t - t^3,\\ S_1 &= 2 - t - 3t^2 + 2t^3,\\ S_2 &= -t + 3t^2 - t^3.\end{aligned}

At x=1x=1: S0′=−1=S1′S_0' = -1 = S_1' and S0′′=−6=S1′′S_0''=-6=S_1'', but S0′′′=−6S_0'''=-6 and S1′′′=12S_1'''=12. The third derivative jumps, as it must, or the pieces would be one cubic. The midpoint values are 158,1,18\tfrac{15}8, 1, \tfrac18, and CubicSpline(x, y, bc_type="natural") returns 1.875,1,0.1251.875, 1, 0.125 with exactly these coefficients.

The curvature integral ∫03(S′′)2 dx\int_0^3 (S'')^2\,dx is 3636 for this spline. It is 8181 for the not-a-knot spline, which on four points is the single cubic through them, and 64.864.8 for the spline clamped to zero slope. The natural spline has the smallest, as the theorem requires.

Example 2: implementation, checked against SciPy

plaintext
import numpy as np from scipy.linalg import solve_banded def moments(t, y, slopes=None): """Second derivatives at the knots: natural, or clamped if slopes=(s0, sn).""" n, h = len(t) - 1, np.diff(t) d = np.diff(y) / h up, main, lo, r = np.zeros((4, n + 1)) main[1:n] = 2 * (h[:-1] + h[1:]) lo[:n - 1] = h[:-1] up[2:] = h[1:] r[1:n] = 6 * (d[1:] - d[:-1]) main[[0, n]] = 1.0 if slopes is not None: main[0], up[1], r[0] = 2 * h[0], h[0], 6 * (d[0] - slopes[0]) main[n], lo[n - 1], r[n] = 2 * h[-1], h[-1], 6 * (slopes[1] - d[-1]) return solve_banded((1, 1), np.vstack([up, main, lo]), r)

On 40 random nodes in [−1,1][-1,1] (gaps from 0.00160.0016 to 0.280.28) with f=1/(1+25x2)f = 1/(1+25x^2), evaluating the piecewise formula above on 20001 points differs from CubicSpline by at most 3.3×10−163.3\times10^{-16}, both for natural ends and for clamped ends with the true slopes.

Example 3: Runge's function

Max errors for f(x)=1/(1+25x2)f(x)=1/(1+25x^2) on [−1,1][-1,1] with n+1n+1 nodes, on a grid of 20001 points. All columns but the last use equispaced nodes.

text
n polynomial linear cubic not-a-knot Chebyshev polynomial 10 1.92e+00 6.74e-02 2.20e-02 1.09e-01 20 5.98e+01 4.18e-02 3.18e-03 1.53e-02 40 1.05e+05 1.40e-02 2.78e-04 2.89e-04 80 6.31e+14 3.80e-03 1.61e-05 1.02e-07

The polynomial diverges on the same data for which the spline converges. Observed orders of the cubic, from doubling nn, are 2.792.79, 3.523.52, 4.114.11, then 4.064.06 and 4.024.02 at n=160n=160 and 320320: "order 4" appears only once hh resolves the peak of width about 1/51/5. Splines are not simply better than Chebyshev interpolation. Up to n=20n=20 the spline wins, at n=40n=40 they tie, and at n=80n=80 the polynomial on chosen nodes is about 160 times better, because this ff is analytic and geometric convergence overtakes h4h^4.

The reason for the stability is locality. Set y=1y=1 at the central node and y=0y=0 at the others, n=20n=20, and record the largest value in each interval:

text
interval from the bump natural spline polynomial 1 1.37e-01 2.65e-01 2 3.66e-02 2.32e-01 3 9.81e-03 3.01e-01 5 7.05e-04 1.25e+00 9 4.41e-06 1.82e+03

Each interval multiplies the spline by a factor of about 0.268=2−30.268 = 2-\sqrt3, the size of the decaying root of the recurrence Mi−1+4Mi+Mi+1=0M_{i-1}+4M_i+M_{i+1}=0. The polynomial grows toward the ends. The Lebesgue constant at n=20n=20 is 1.551.55 for the natural spline and 1.971.97 for not-a-knot, against 1.1×1041.1\times10^4 for the polynomial. With n=40n=40, adding 10−310^{-3} to the central value changes the spline by 10−310^{-3} and the polynomial by 5.7×1055.7\times10^{5}.

Example 4: the boundary condition sets the order

Max error for exe^x on [0,1][0,1], where f′′≠0f''\ne0 at both ends:

text
n natural clamped not-a-knot 10 1.33e-03 6.96e-07 6.93e-06 20 3.34e-04 4.39e-08 4.56e-07 40 8.34e-05 2.75e-09 2.92e-08 80 2.09e-05 1.73e-10 1.85e-09 160 5.21e-06 1.08e-11 1.17e-10 ord 2.00 4.00 3.99

The natural error sits next to the right end, at x=0.990x=0.990 for n=40n=40, where the false assumption S′′=0S''=0 costs most. On sin⁡x\sin x over [0,π][0,\pi], where f′′f'' really is 00 at both ends, the natural spline recovers order 4.004.00. Here the clamped error times n4/en^4/e is 0.00260.0026, close to 1/3841/384, for this ff. The linear spline's bound h2e/8h^2e/8 is nearly attained: 8.289×10−78.289\times10^{-7} measured against 8.296×10−78.296\times10^{-7} at n=640n=640.

Example 5: when ff is not smooth

For x\sqrt x on [0,1][0,1], f′f' is unbounded at 00, so the hypothesis f∈C4f\in C^4 fails.

text
n uniform linear uniform cubic graded cubic, x_i = (i/n)^4 20 5.59e-02 3.94e-02 7.20e-03 80 2.80e-02 1.97e-02 4.50e-04 320 1.40e-02 9.85e-03 2.81e-05

On a uniform mesh both orders fall to 0.500.50, and the extra degree buys almost nothing. Splines accept any mesh, so nodes can be moved toward the singularity, which gives order 2.002.00 with the same node count.

Example 6: overshoot and extrapolation

Take data 00 at x=0,…,5x=0,\ldots,5 and 11 at x=6,…,10x=6,\ldots,10. CubicSpline ranges from −0.108-0.108 to 1.1081.108. Smoothness forces it past the data. Now take an illustrative latency distribution, with x=1,2,5,10,20,50,100,200,500x=1,2,5,10,20,50,100,200,500 ms and cumulative fractions 0.10,0.40,0.70,0.85,0.92,0.97,0.99,0.995,0.9990.10, 0.40, 0.70, 0.85, 0.92, 0.97, 0.99, 0.995, 0.999. On this uneven grid the cubic reaches 1.0411.041 near 388388 ms, an impossible probability, and decreases on about a third of the range. PchipInterpolator stays monotone, between 0.10.1 and 0.9990.999, at the cost of being only C1C^1.

Outside the data a spline is just its last cubic, and CubicSpline extrapolates by default. For sin⁡2πx\sin 2\pi x on 11 nodes in [0,1][0,1], the value at x=1.2x=1.2 is 1.181.18 against 0.950.95, and at x=2x=2 it is −18.6-18.6 against 00. Pass extrapolate=False to get nan there.

Common Misconceptions & Pitfalls

  • "Splines never oscillate." They avoid Runge's growth, not overshoot: Example 6 overshoots by about 11%11\% of the step.
  • "The natural spline is the most natural choice." The name refers to the condition, not to accuracy. It is order 2 unless f′′f'' vanishes at the ends (Example 4).
  • "bc_type='clamped' means the slopes are matched." In SciPy that string means zero slope at both ends. On exe^x with n=40n=40 it gives error 0.01160.0116, against 2.75×10−92.75\times10^{-9} for bc_type=((1, 1.0), (1, np.e)).
  • "An interpolating spline removes noise." It passes through every data point, noise included. For noisy data use a fit: see least-squares-curve-fitting, or scipy.interpolate.make_smoothing_spline.
  • "Splines always beat global polynomials." On smooth functions with chosen nodes, Chebyshev interpolation wins eventually (Example 3).
  • "Book vs today." Older code uses scipy.interpolate.interp1d, which SciPy now calls legacy. New code should use CubicSpline, PchipInterpolator or make_interp_spline. With the default boundary, make_interp_spline(t, y, k=3) agrees with CubicSpline to 6.7×10−166.7\times10^{-16} on the step data of Example 6.

Summary

A cubic spline has 4n4n coefficients and 4n−24n-2 matching conditions, so two boundary conditions complete it. The second derivatives solve a tridiagonal, strictly diagonally dominant system in O(n)O(n), with κ2<3\kappa_2<3 on a uniform natural mesh. For f∈C4f\in C^4, not-a-knot and clamped splines reach order 4, and natural ones fall to order 2 unless f′′f'' vanishes at the ends. On Runge's function the error goes from 2.2×10−22.2\times10^{-2} to 1.6×10−51.6\times10^{-5} between n=10n=10 and n=80n=80 while the equispaced polynomial explodes, because a spline's response to one value is multiplied by a factor of 0.2680.268 per interval. Cubic splines still overshoot and extrapolate badly: use a monotone interpolant such as PCHIP when shape matters, and a fit when the data is noisy.

Documentation Links