PYTHON / SCIPY
Interpolation
Build interpolants through sampled data with np.interp, CubicSpline and RegularGridInterpolator, and judge when each one is trustworthy.
What you will learn
- Build a callable interpolant with CubicSpline and evaluate it at any query point
- Use np.interp for fast piecewise-linear values on ascending sample points
- Get derivatives from a spline with cs(x, 1) instead of finite differences
- Control out-of-range behaviour with extrapolate=False or bounds_error
Understanding Interpolation
Interpolation answers a narrow question: given exact samples (x0,y0)...(xn,yn), what value should sit between them? Every interpolant in scipy.interpolate passes through all the given points by construction, which is the opposite of a fit that minimises residuals. That is why interpolation is the right tool for a lookup table, a resampled signal, or a tabulated physical constant, and the wrong tool for noisy measurements.
The mental model for a spline is one low-degree polynomial per interval between knots, glued together by continuity conditions. A cubic has four coefficients per interval, and matching the two endpoint values plus continuous first and second derivatives at the interior knots leaves exactly two conditions unused, which is why CubicSpline needs a bc_type. The default 'not-a-knot' spends them by forcing the third derivative to be continuous across the second and second-to-last knot, and a useful consequence is that if the data really comes from a single cubic, the spline recovers that cubic exactly.
Outside the sample range the picture changes completely. np.interp clamps to the first and last y value, while CubicSpline keeps evaluating the end polynomial, whose cubic leading term dominates and grows without bound. Neither is a prediction: the data constrains the interpolant only between the knots, so treat anything outside [x[0], x[-1]] as a modelling decision you have to make on purpose.
import numpy as np
from scipy.interpolate import CubicSpline
x = np.arange(6.0) # knots at 0, 1, 2, 3, 4, 5
y = x**3 - 2*x # samples of a cubic
cs = CubicSpline(x, y) # bc_type='not-a-knot' by default
xq = 2.5
exact = xq**3 - 2*xq
lin = np.interp(xq, x, y)
print(f"exact f(2.5) = {exact:.4f}")
print(f"linear = {lin:.4f}")
print(f"cubic spline = {float(cs(xq)):.4f}")
print(f"linear error = {abs(lin - exact):.4f}")
print(f"spline error = {abs(float(cs(xq)) - exact):.4f}")
print(f"spline f'(2.5) = {float(cs(xq, 1)):.4f}")An interpolant is a piecewise polynomial pinned to your samples, so its accuracy comes from the knots and its behaviour outside them is unconstrained.
Worked examples
Three different answers outside the data
Shows that clamping, cubic extrapolation and refusing to extrapolate are three deliberate choices, not one correct value.
import numpy as np
from scipy.interpolate import CubicSpline
x = np.array([0.0, 1.0, 2.0, 3.0])
y = np.array([1.0, 2.0, 0.0, 3.0])
cs_ext = CubicSpline(x, y) # extrapolates
cs_nan = CubicSpline(x, y, extrapolate=False) # refuses
print(f"np.interp at 5.0 = {np.interp(5.0, x, y):.4f}")
print(f"spline at 5.0 = {float(cs_ext(5.0)):.4f}")
print(f"no-extrap at 5.0 = {float(cs_nan(5.0))}")Example explained
Line 1np.interp has no notion of trend: for any x above x[-1] it returns y[-1], here 3.0.
Line 2With four knots the not-a-knot spline is a single cubic through the data, and continuing it to x=5 gives 56 even though every sample lies in [0, 3].
Line 3extrapolate=False makes the same object return nan outside [0, 3], which turns a silent wrong number into a visible gap.
Line 4float() is used because calling a spline with a scalar returns a 0-d NumPy array, not a Python float.
Bilinear interpolation on a 2-D grid
Interpolates values sampled on a rectangular grid with RegularGridInterpolator.
import numpy as np
from scipy.interpolate import RegularGridInterpolator
xs = np.array([0.0, 1.0, 2.0])
ys = np.array([0.0, 1.0])
vals = np.array([[0.0, 1.0],
[1.0, 3.0],
[4.0, 8.0]])
f = RegularGridInterpolator((xs, ys), vals) # method='linear'
for pt in [[0.5, 0.5], [1.5, 0.25]]:
print(f"f{tuple(pt)} = {float(f([pt])[0]):.4f}")Example explained
Line 1The axes are passed as a tuple of 1-D coordinate arrays, and vals must have shape (len(xs), len(ys)).
Line 2At the centre of the first cell the result 1.25 is the plain average of its four corner values 0, 1, 1 and 3.
Line 3At (1.5, 0.25) the corners are weighted by distance, so the nearby values 1 and 4 dominate over 3 and 8.
Line 4The interpolator takes an array of points, so a single point must be wrapped as [pt] and the scalar read back with [0].
Important notes
scipy.interpolate.interp1d is legacy and no longer recommended for new code; use np.interp, CubicSpline, make_interp_spline or PchipInterpolator instead.
A cubic spline through monotone data can still dip or overshoot between knots, because smoothness is enforced but monotonicity is not; PchipInterpolator is the fix.
Common mistakes
Passing x in descending or shuffled order: CubicSpline raises 'x must be strictly increasing sequence', while np.interp silently returns meaningless numbers because it assumes ascending xp.
Using interpolation on noisy measurements: the curve is forced through every noisy point, so it oscillates wildly between samples instead of smoothing anything.
Trusting the default extrapolation of CubicSpline just outside the data, where the end cubic can be off by orders of magnitude, as in the value 56 above.
Try it yourself
Change, predict, then run
Sample f(x) = 1/(1 + x**2) at 7 evenly spaced points on [-5, 5], then print the absolute error of np.interp and of CubicSpline at x = 4.5 against the true value.
Open the Python workspaceCheck your understanding
You interpolate strictly increasing data with CubicSpline and find that the curve dips below y[-2] between the last two knots. What explains this?
- A cubic spline only enforces passing through the points with continuous first and second derivatives; monotonicity is not one of its constraints
- The spline degree is too low, and a quintic spline would be monotone by construction
- The knots must be equally spaced, otherwise the spline is not a valid interpolant
- The data was passed as integers, and casting to float would remove the dip
Show answer
The spline solves for coefficients satisfying interpolation and C2 smoothness only, so it is free to overshoot or dip between knots; PchipInterpolator adds shape preservation. Unequal spacing is fully supported by CubicSpline and is not the cause, so that option is a red herring.