You join your data points with straight lines and the result looks wrong. It zigzags. It has sharp corners at every point, and the real thing you are measuring does not have corners in it.
A cubic spline fixes that. Instead of one straight line per gap, you fit a gentle curve to each gap, then join the curves so the transitions are smooth. What you get is a curve that goes through all your points and bends naturally between them.
The quick answer
A cubic spline fits a separate cubic curve to each gap between your points, chosen so that the curves meet with matching slope and matching bend at every point. You solve a small set of equations once, then you can read off any value you want.
Doing it by hand is fine for four or five points. Beyond that, use the calculator and pick spline mode.
Why not just use one big curve?
You could fit a single polynomial through all your points. For a handful of points that works. For more than about six, it goes badly wrong: the curve starts swinging wildly between the points even though it hits every one of them exactly. That failure has a name, Runge's phenomenon, and it is the reason splines exist.
A spline avoids this by never using a curve more complicated than a cubic, no matter how many points you have. More points means more small pieces, not one increasingly unstable curve.
Splines are also local. Move one data point in a single big polynomial and the whole curve shifts, including parts nowhere near the change. Move one point in a spline and mostly the neighbouring pieces respond. That is why CAD software and font rendering use them.
What the pieces have to do
Say you have 5 points. That gives 4 gaps, so 4 cubic curves, and each cubic has 4 numbers in it, making 16 unknowns. The conditions come from three requirements:
- Each curve hits the data point at both ends of its gap. With 4 gaps that is 8 conditions.
- Where two curves meet they have the same slope, so there is no visible kink. That is 3 more.
- Where two curves meet they also bend by the same amount. Another 3.
That totals 14 for 16 unknowns, so you need two more conditions, and what you pick for those decides the flavour of spline. A natural spline makes the curve stop bending at both ends, which is the easiest to do by hand and the one solved below. A clamped spline lets you set the slope at each end, useful when physics tells you what it should be. Not-a-knot applies a technical condition near each end that usually looks best on real data, and it is the default in MATLAB and SciPy.
The equations you solve
You do not solve for all 16 coefficients directly. You solve for one number per data point, namely how sharply the curve bends there. That number is the second derivative, written Mi, and once you have those, every coefficient falls out with simple arithmetic.
Each interior point gives you one equation. With hi being the width of gap i:
hi−1Mi−1 + 2(hi−1 + hi)Mi + hiMi+1 = 6 × [ (yi+1 − yi)/hi − (yi − yi−1)/hi−1 ]It looks worse than it is. The right hand side is just the slope coming out of the point minus the slope going into it, times 6. In plain terms it measures how sharply your data turns a corner at that point. Data that runs in a straight line gives zero on the right, which gives zero bend everywhere, which gives you straight lines back. Sensible.
The left hand side ties the bend at each point to the bend at its two neighbours.
Worked example with four points
These numbers were picked so the arithmetic stays tidy:
| i | xi | yi |
|---|---|---|
| 0 | 0 | 1 |
| 1 | 1 | 2 |
| 2 | 2 | 0 |
| 3 | 3 | 1 |
The data goes up, then down harder, then up again. Every gap is 1 wide, so h = 1 throughout, which keeps the numbers clean. Going with a natural spline means the bend at the two ends is zero: M0 = 0 and M3 = 0. That leaves two unknowns, M1 and M2.
Step 1: write the equation at each interior point
At x = 1, the slope coming in is 2 − 1 = 1, and the slope going out is 0 − 2 = −2. The data turns downward here, so expect a negative bend:
M0 + 4M1 + M2 = 6 × [ (−2) − (1) ] = 6 × (−3) = −18At x = 2, the slope coming in is −2 and the slope going out is 1. The data turns upward:
M1 + 4M2 + M3 = 6 × [ (1) − (−2) ] = 6 × 3 = 18Drop in M0 = 0 and M3 = 0 and you are left with two equations, two unknowns:
4M1 + M2 = −18M1 + 4M2 = 18
Step 2: solve them
Multiply the first by 4 to line up the M2 terms, then subtract the second:
16M1 + 4M2 = −72−( M1 + 4M2 = 18 )
15M1 = −90, so M1 = −6
Put that back into the second equation: M2 = (18 − (−6)) ÷ 4 = 24 ÷ 4 = 6.
So the bend at your four points is 0, −6, 6, 0. Negative at the peak and positive at the trough, which is exactly what you would sketch by eye. Curving down over a hump, curving up through a dip.
Step 3: turn the bends into curves
Each gap gets its own cubic. Writing t for how far you are into the gap, so t = x − xi:
Si(t) = yi + bit + (Mi/2)t2 + ((Mi+1 − Mi) / 6h)t3The four terms are the starting value, the starting slope, the bend, and how fast the bend is changing. The only piece you have to work out is bi, the slope at the start of the gap:
bi = (yi+1 − yi)/h − h(2Mi + Mi+1)/6That is the plain straight-line slope across the gap, adjusted for the fact that the curve bends. Now do all three gaps.
Gap 0, from x = 0 to x = 1:
b0 = 1 − (2×0 + (−6))/6 = 1 + 1 = 2S0(t) = 1 + 2t − t3
Gap 1, from x = 1 to x = 2:
b1 = −2 − (2×(−6) + 6)/6 = −2 + 1 = −1S1(t) = 2 − t − 3t2 + 2t3
Gap 2, from x = 2 to x = 3:
b2 = 1 − (2×6 + 0)/6 = 1 − 2 = −1S2(t) = −t + 3t2 − t3
Step 4: check the joins
Do this before you trust any of it. Each curve should land exactly on its endpoints, and where two curves meet they should agree on both slope and bend.
| Check | From the left curve | From the right curve |
|---|---|---|
| Value at x = 1 | S0(1) = 1 + 2 − 1 = 2 | S1(0) = 2 |
| Value at x = 2 | S1(1) = 2 − 1 − 3 + 2 = 0 | S2(0) = 0 |
| Slope at x = 1 | S0′(1) = 2 − 3 = −1 | S1′(0) = −1 |
| Slope at x = 2 | S1′(1) = −1 − 6 + 6 = −1 | S2′(0) = −1 |
| Bend at x = 1 | S0″(1) = −6 | S1″(0) = −6 |
| Bend at x = 2 | S1″(1) = −6 + 12 = 6 | S2″(0) = 6 |
Every row matches. The two end conditions hold too: S0″(0) = 0 and S2″(1) = 6 − 6 = 0, which is what "natural" asked for. The spline is correct.
Step 5: read off values
Pick your x, work out which gap it falls in, subtract the start of that gap to get t, and plug in.
| x | Which curve | Arithmetic | Answer | Straight line would give |
|---|---|---|---|---|
| 0.5 | S0, t = 0.5 | 1 + 1 − 0.125 | 1.875 | 1.5 |
| 1.5 | S1, t = 0.5 | 2 − 0.5 − 0.75 + 0.25 | 1.000 | 1.0 |
| 2.5 | S2, t = 0.5 | −0.5 + 0.75 − 0.125 | 0.125 | 0.5 |
Look at the last column. At x = 0.5 the spline sits well above the straight line, because it is still curving up toward the peak at x = 1 rather than heading straight for it. At x = 2.5 it sits below, because it is climbing shallowly out of the trough. At x = 1.5 the two happen to agree, because the data is symmetric around that point.
That difference is the whole reason to use a spline. A straight line only knows about the two points either side of you. The spline knows about the shape of the data further out, and uses it.
The one thing to watch out for
A natural cubic spline is smooth, but it does not promise to stay inside your data. If your points only ever go up, the spline can still dip slightly below on its way between two of them. Usually harmless. Not harmless if the quantity physically cannot go below zero, like a concentration, a count, or a probability.
If you need a guarantee that the curve will not overshoot, use a monotone method such as PCHIP. It is slightly less smooth and it will not invent a dip that is not in your data.
Is it worth the effort?
Setting up the spline takes work proportional to how many points you have, and after that each lookup is almost free. Query the same table many times and the setup cost disappears into the noise.
With a fine table and gently changing data, stick with linear interpolation. It is simpler and the difference will not show up. Splines earn their keep on a coarse table over curved data, when you need the slope rather than just the value, or when you are drawing a curve someone will look at, since zigzags read as errors even when the numbers are fine.
Solving the equations by hand gets tedious past four or five points, and the sixth one teaches you nothing the first five did not. Paste the points into the calculator and let it build the spline.