INTERP_UV_SPLINE

Overview

The INTERP_UV_SPLINE function performs one-dimensional interpolation using a univariate spline that passes exactly through all provided data points. This approach is ideal when the data is known precisely and the interpolated curve must honor every observation.

Spline interpolation is a form of piecewise polynomial interpolation where low-degree polynomials (typically cubic, degree 3) are fitted between consecutive data points. Unlike fitting a single high-degree polynomial to all data, splines avoid Runge’s phenomenon—unwanted oscillations that can occur at the edges of the interpolation interval. The term “spline” originates from flexible rulers historically used in shipbuilding and drafting to draw smooth curves through predefined points.

This implementation uses SciPy’s InterpolatedUnivariateSpline class, which internally relies on FITPACK routines for B-spline construction. The function fits a spline of degree k (default k = 3 for cubic splines) such that the spline passes through all data points with zero residual. The spline is constructed by solving a system of equations that ensures continuity of the function and its derivatives up to order k - 1 at each interior knot.

The degree parameter (k) controls the polynomial order of each spline segment. Common choices include:

  • k = 1: Linear interpolation (piecewise linear)
  • k = 3: Cubic spline (smooth curves with continuous first and second derivatives)
  • k = 5: Quintic spline (even smoother, continuous through fourth derivative)

Optional weights allow emphasizing certain data points during spline fitting. The extrapolation mode (ext) determines behavior when evaluating the spline outside the original data range: the function can extrapolate, return zeros, raise an error, or return the boundary value.

For more details on spline theory, see the Wikipedia article on spline interpolation or the SciPy interpolation tutorial. The source code is available on GitHub.

This example function is provided as-is without any representation of accuracy.

Excel Usage

=INTERP_UV_SPLINE(x, y, x_new, w, k, ext)
  • x (list[list], required): The x-coordinates of the data points
  • y (list[list], required): The y-coordinates of the data points
  • x_new (list[list], required): The x-coordinates at which to evaluate the spline
  • w (list[list], optional, default: null): Weights for spline fitting
  • k (int, optional, default: 3): Degree of the smoothing spline
  • ext (str, optional, default: “extrapolate”): Extrapolation mode

Returns (list[list]): A 2D list containing the interpolated values, or an error message (str) if invalid.

Examples

Example 1: Demo case 1

Inputs:

x y x_new
0 0 0.5
1 1 1.5
2 2 2.5
3 3

Excel formula:

=INTERP_UV_SPLINE({0;1;2;3}, {0;1;2;3}, {0.5;1.5;2.5})

Expected output:

Result
0.5
1.5
2.5

Example 2: Demo case 2

Inputs:

x y x_new k
0 0 1.5 3
1 1 2.5
2 4
3 9
4 16

Excel formula:

=INTERP_UV_SPLINE({0;1;2;3;4}, {0;1;4;9;16}, {1.5;2.5}, 3)

Expected output:

Result
2.25
6.25

Example 3: Demo case 3

Inputs:

x y x_new w k
0 1 0.5 1 3
1 2 2 1
2 3 3.5 2
3 2.5 1
4 1.5 1

Excel formula:

=INTERP_UV_SPLINE({0;1;2;3;4}, {1;2;3;2.5;1.5}, {0.5;2;3.5}, {1;1;2;1;1}, 3)

Expected output:

Result
1.367
3
1.961

Example 4: Demo case 4

Inputs:

x y x_new k ext
1 1 0 2 extrapolate
2 4 2.5
3 9 5
4 16

Excel formula:

=INTERP_UV_SPLINE({1;2;3;4}, {1;4;9;16}, {0;2.5;5}, 2, "extrapolate")

Expected output:

Result
0
6.25
25

Python Code

import math
from scipy.interpolate import InterpolatedUnivariateSpline as scipy_InterpolatedUnivariateSpline

def interp_uv_spline(x, y, x_new, w=None, k=3, ext='extrapolate'):
    """
    1-D interpolating spline for data.

    See: https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.InterpolatedUnivariateSpline.html

    This example function is provided as-is without any representation of accuracy.

    Args:
        x (list[list]): The x-coordinates of the data points
        y (list[list]): The y-coordinates of the data points
        x_new (list[list]): The x-coordinates at which to evaluate the spline
        w (list[list], optional): Weights for spline fitting Default is None.
        k (int, optional): Degree of the smoothing spline Default is 3.
        ext (str, optional): Extrapolation mode Valid options: extrapolate, zeros, raise, const. Default is 'extrapolate'.

    Returns:
        list[list]: A 2D list containing the interpolated values, or an error message (str) if invalid.
    """
    def to2d(val):
        """Convert scalar to 2D list if needed."""
        return [[val]] if not isinstance(val, list) else val

    def flatten(arr):
        """Flatten a 2D list to 1D."""
        return [item for sublist in arr for item in sublist]

    def validate_numeric_2d_list(val, name):
        """Validate that a value is a 2D list of numbers."""
        if not isinstance(val, list):
            return f"Invalid input: {name} must be a 2D list."
        if not val:
            return f"Invalid input: {name} must not be empty."
        for i, row in enumerate(val):
            if not isinstance(row, list):
                return f"Invalid input: {name} must be a 2D list."
            if not row:
                return f"Invalid input: {name} must not contain empty rows."
            for j, item in enumerate(row):
                if not isinstance(item, (int, float)):
                    return f"Invalid input: {name}[{i}][{j}] must be a number."
                if math.isnan(item) or math.isinf(item):
                    return f"Invalid input: {name}[{i}][{j}] must be finite."
        return None

    # Normalize inputs to 2D lists
    x = to2d(x)
    y = to2d(y)
    x_new = to2d(x_new)

    # Validate x, y, x_new
    error = validate_numeric_2d_list(x, "x")
    if error:
        return error
    error = validate_numeric_2d_list(y, "y")
    if error:
        return error
    error = validate_numeric_2d_list(x_new, "x_new")
    if error:
        return error

    # Flatten input arrays
    x_flat = flatten(x)
    y_flat = flatten(y)
    x_new_flat = flatten(x_new)

    # Check that x and y have the same length
    if len(x_flat) != len(y_flat):
        return f"Invalid input: x and y must have the same length (got {len(x_flat)} and {len(y_flat)})."

    # Check minimum data points for degree k
    if len(x_flat) < k + 1:
        return f"Invalid input: need at least {k + 1} data points for degree {k} spline (got {len(x_flat)})."

    # Validate and process weights if provided
    w_flat = None
    if w is not None:
        w = to2d(w)
        error = validate_numeric_2d_list(w, "w")
        if error:
            return error
        w_flat = flatten(w)
        if len(w_flat) != len(x_flat):
            return f"Invalid input: w must have the same length as x and y (got {len(w_flat)} and {len(x_flat)})."
        # Check for non-positive weights
        for i, weight in enumerate(w_flat):
            if weight <= 0:
                return f"Invalid input: all weights must be positive (w[{i}] = {weight})."

    # Sort data by x to ensure increasing order required by InterpolatedUnivariateSpline
    if w_flat is not None:
        data = sorted(zip(x_flat, y_flat, w_flat), key=lambda p: p[0])
        x_flat, y_flat, w_flat = zip(*data)
        x_flat, y_flat, w_flat = list(x_flat), list(y_flat), list(w_flat)
    else:
        data = sorted(zip(x_flat, y_flat), key=lambda p: p[0])
        x_flat, y_flat = zip(*data)
        x_flat, y_flat = list(x_flat), list(y_flat)

    # Validate k parameter
    if not isinstance(k, (int, float)):
        return "Invalid input: k must be an integer."
    k = int(k)
    if k < 1 or k > 5:
        return "Invalid input: k must be between 1 and 5."

    # Validate ext parameter
    valid_ext_names = ["extrapolate", "zeros", "raise", "const"]

    if not isinstance(ext, str):
        return f"Invalid input: ext must be a string (got {type(ext).__name__})."

    if ext not in valid_ext_names:
        return f"Invalid input: ext must be one of {valid_ext_names} (got '{ext}')."

    # Create and evaluate spline
    try:
        spline = scipy_InterpolatedUnivariateSpline(x_flat, y_flat, w=w_flat, k=k, ext=ext)
        result = spline(x_new_flat)
    except ValueError as e:
        return f"Invalid input: {e}"
    except Exception as e:
        return f"scipy.interpolate.InterpolatedUnivariateSpline error: {e}"

    # Validate result
    if not hasattr(result, "__iter__"):
        return "scipy.interpolate.InterpolatedUnivariateSpline error: result is not iterable."

    # Convert result to 2D list (column vector)
    output = []
    for val in result:
        if not isinstance(val, (int, float)):
            return "scipy.interpolate.InterpolatedUnivariateSpline error: non-numeric result."
        val_float = float(val)
        if math.isnan(val_float) or math.isinf(val_float):
            return "scipy.interpolate.InterpolatedUnivariateSpline error: result contains non-finite values."
        output.append([val_float])

    return output

Online Calculator