Multivariate

Overview

Multivariate interpolation extends the concept of one-dimensional interpolation to functions of multiple variables. For technical users and data scientists, this often involves using tools like scipy.interpolate.griddata to reconstruct a continuous function f(\mathbf{x}) from discrete observations. Whether your data represents temperature across geographic coordinates (x, y) or material properties depending on pressure and temperature (P, T), multivariate methods allow you to estimate values at arbitrary query points.

Applications are widespread across engineering and science: - Geospatial Analysis: Creating digital elevation models from scattered survey points. - Computational Fluid Dynamics: Reconstructing flow fields from discrete sensor readings. - Materials Science: Building response surfaces from experimental design data. - Climate Modeling: Interpolating irregular measurements to create continuous spatial fields.

Implementation Framework: Modern multivariate interpolation relies on high-performance libraries like SciPy and NumPy. These tools handle the computational complexity of multidimensional Delaunay triangulation and radial basis function (RBF) networks while providing trade-offs between accuracy, smoothness, and speed.

How It Works: Scattered vs. Gridded Data

The fundamental challenge in multivariate interpolation is the curse of dimensionality: the number of data points needed grows exponentially with the dimensions. Your choice of method depends primarily on how your data is arranged.

Structured (Gridded) Data

When data lies on a regular lattice, specialized methods like GRID_INTERP and INTERPN exploit the structure for maximum efficiency. These tools perform sequential 1-D interpolations along each axis, which is significantly faster and more stable than generic scattered methods.

Unstructured (Scattered) Data

If your measurements arrive at arbitrary, irregular locations, you must use triangulation-based or neighbor-search methods. - Linear and Cubic: The GRIDDATA and LINEAR_ND_INTERP tools perform piecewise interpolation on a simplicial tessellation (Delaunay triangulation). - Nearest-neighbor: The NEAREST_ND_INTERP tool provides fast approximation by using the value of the closest known point, trading smoothness for speed. - Radial Basis Functions: The RBF_INTERPOLATOR tool constructs global, infinitely differentiable interpolants, ideal for high-accuracy scientific modeling.

Important: The Convex Hull Boundary

A common issue in multivariate interpolation (particularly with linear and cubic methods in griddata) is the behavior at the boundaries.

By default, these methods generate an interpolant confined to the convex hull of the input points—the smallest convex set containing all data points. Any query point (\mathbf{x}) located outside this hull cannot be interpolated using the interior triangles and will typically return NaN (Not a Number).

How to handle points outside the hull: 1. Extrapolation: If you need values outside the original data range, consider using RBF_INTERPOLATOR, which naturally supports global extrapolation. 2. Nearest-Neighbor Fallback: The nearest method does not suffer from convex hull restrictions; it will always return the value of the single closest data point, regardless of location. 3. Fill Value: When using GRIDDATA, you can specify a fill_value (e.g., 0 or the global mean) to be returned for any points outside the hull, preventing NaN values from breaking downstream calculations.

Figure 1: Multivariate Interpolation Data Configurations: (A) Structured gridded data arranged on a regular lattice can leverage efficient tensor product methods and sequential 1-D interpolations. (B) Scattered data at irregular locations requires triangulation-based, neighbor-search, or radial basis function approaches to reconstruct continuous surfaces.

CLOUGH_TOCHER

This function wraps SciPy’s Clough-Tocher interpolator to estimate values on a smooth surface from scattered points in two dimensions. The method triangulates the input points and builds a continuously differentiable piecewise-cubic interpolant.

Given data points (x_i, y_i) with values z_i, the interpolant returns an estimate \hat{z}(x, y) for each query point while approximately minimizing surface curvature.

Excel Usage

=CLOUGH_TOCHER(points, values, xi, fill_value, tol, maxiter, rescale)
  • points (list[list], required): Input point coordinates as (n_points, 2).
  • values (list[list], required): Function values at input points as (n_points, 1).
  • xi (list[list], required): Query coordinates as (n_query, 2).
  • fill_value (float, optional, default: 0): Value used outside the convex hull.
  • tol (float, optional, default: 0.000001): Tolerance used in gradient estimation.
  • maxiter (int, optional, default: 400): Maximum iterations for gradient estimation.
  • rescale (bool, optional, default: false): Rescale points to unit cube before interpolation.

Returns (list[list]): Interpolated values as a 2D list (column vector), or an error message string.

Example 1: Interpolate center point on planar data

Inputs:

points values xi
0 0 0 0.5 0.5
1 0 1
0 1 1
1 1 2

Excel formula:

=CLOUGH_TOCHER({0,0;1,0;0,1;1,1}, {0;1;1;2}, {0.5,0.5})

Expected output:

1

Example 2: Interpolate two interior points

Inputs:

points values xi
0 0 0 0.25 0.75
1 0 1 0.8 0.1
0 1 1
1 1 2

Excel formula:

=CLOUGH_TOCHER({0,0;1,0;0,1;1,1}, {0;1;1;2}, {0.25,0.75;0.8,0.1})

Expected output:

Result
1
0.9
Example 3: Use fill value outside convex hull

Inputs:

points values xi fill_value
0 0 0 2 2 -999
1 0 1
0 1 1
1 1 2

Excel formula:

=CLOUGH_TOCHER({0,0;1,0;0,1;1,1}, {0;1;1;2}, {2,2}, -999)

Expected output:

-999

Example 4: Interpolate with rescaling enabled

Inputs:

points values xi rescale
0 0 0 5 0.05 true
10 0 10
0 0.1 0.1
10 0.1 10.1

Excel formula:

=CLOUGH_TOCHER({0,0;10,0;0,0.1;10,0.1}, {0;10;0.1;10.1}, {5,0.05}, TRUE)

Expected output:

5.05

Python Code

Show Code
import math
import numpy as np
from scipy.interpolate import CloughTocher2DInterpolator as scipy_CloughTocher2DInterpolator

def clough_tocher(points, values, xi, fill_value=0, tol=1e-06, maxiter=400, rescale=False):
    """
    Piecewise cubic C1 interpolation for scattered 2D data.

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

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

    Args:
        points (list[list]): Input point coordinates as (n_points, 2).
        values (list[list]): Function values at input points as (n_points, 1).
        xi (list[list]): Query coordinates as (n_query, 2).
        fill_value (float, optional): Value used outside the convex hull. Default is 0.
        tol (float, optional): Tolerance used in gradient estimation. Default is 1e-06.
        maxiter (int, optional): Maximum iterations for gradient estimation. Default is 400.
        rescale (bool, optional): Rescale points to unit cube before interpolation. Default is False.

    Returns:
        list[list]: Interpolated values as a 2D list (column vector), or an error message string.
    """
    try:
        def to2d(x):
            return [[x]] if not isinstance(x, list) else x

        def flatten_2d(arr):
            out = []
            for row in arr:
                for val in row:
                    out.append(val)
            return out

        points = to2d(points)
        values = to2d(values)
        xi = to2d(xi)

        if not isinstance(points, list) or not all(isinstance(row, list) for row in points):
            return "Error: Invalid input: points must be a 2D list."
        if not isinstance(values, list) or not all(isinstance(row, list) for row in values):
            return "Error: Invalid input: values must be a 2D list."
        if not isinstance(xi, list) or not all(isinstance(row, list) for row in xi):
            return "Error: Invalid input: xi must be a 2D list."

        if len(points) == 0 or len(values) == 0 or len(xi) == 0:
            return "Error: Invalid input: points, values, and xi must not be empty."

        if any(len(row) != 2 for row in points):
            return "Error: Invalid input: points must have exactly 2 columns."
        if any(len(row) != 2 for row in xi):
            return "Error: Invalid input: xi must have exactly 2 columns."

        values_flat = flatten_2d(values)
        if len(values_flat) != len(points):
            return "Error: Invalid input: values must contain one value per point."

        for i, row in enumerate(points):
            for j, val in enumerate(row):
                if not isinstance(val, (int, float)):
                    return f"Error: Invalid input: points[{i}][{j}] must be numeric."
                if math.isnan(val) or math.isinf(val):
                    return f"Error: Invalid input: points[{i}][{j}] must be finite."

        for i, val in enumerate(values_flat):
            if not isinstance(val, (int, float)):
                return f"Error: Invalid input: values element {i} must be numeric."
            if math.isnan(val) or math.isinf(val):
                return f"Error: Invalid input: values element {i} must be finite."

        for i, row in enumerate(xi):
            for j, val in enumerate(row):
                if not isinstance(val, (int, float)):
                    return f"Error: Invalid input: xi[{i}][{j}] must be numeric."
                if math.isnan(val) or math.isinf(val):
                    return f"Error: Invalid input: xi[{i}][{j}] must be finite."

        if not isinstance(fill_value, (int, float)):
            return "Error: Invalid input: fill_value must be numeric."
        if not isinstance(tol, (int, float)):
            return "Error: Invalid input: tol must be numeric."
        if not isinstance(maxiter, int):
            return "Error: Invalid input: maxiter must be an integer."
        if maxiter <= 0:
            return "Error: Invalid input: maxiter must be positive."
        if not isinstance(rescale, bool):
            return "Error: Invalid input: rescale must be boolean."

        points_arr = np.array(points, dtype=float)
        values_arr = np.array(values_flat, dtype=float)
        xi_arr = np.array(xi, dtype=float)

        interp = scipy_CloughTocher2DInterpolator(
            points_arr,
            values_arr,
            fill_value=float(fill_value),
            tol=float(tol),
            maxiter=maxiter,
            rescale=rescale
        )

        result = interp(xi_arr)
        result_arr = np.asarray(result, dtype=float).reshape(-1)
        return [[float(v)] for v in result_arr]
    except Exception as e:
        return f"Error: {str(e)}"

Online Calculator

Input point coordinates as (n_points, 2).
Function values at input points as (n_points, 1).
Query coordinates as (n_query, 2).
Value used outside the convex hull.
Tolerance used in gradient estimation.
Maximum iterations for gradient estimation.
Rescale points to unit cube before interpolation.

GRID_INTERP

This function performs interpolation on a 2D rectilinear grid using either linear or nearest-neighbor rules. Grid coordinates for each axis define sample locations, and the function estimates values at requested query points.

For a query point (x, y) between surrounding grid nodes, linear interpolation computes a weighted combination of neighboring grid values, while nearest chooses the closest grid-supported value.

Excel Usage

=GRID_INTERP(points_x, points_y, values, xi, grid_interp_method, bounds_error, fill_value)
  • points_x (list[list], required): 1D array of x-coordinates of the grid points
  • points_y (list[list], required): 1D array of y-coordinates of the grid points
  • values (list[list], required): 2D array of data values on the grid
  • xi (list[list], required): Points at which to interpolate data (n_points, 2)
  • grid_interp_method (str, optional, default: “linear”): Interpolation method
  • bounds_error (bool, optional, default: true): If True, raise error for out-of-bounds points
  • fill_value (float, optional, default: null): Value for out-of-bounds points when bounds_error is False

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

Example 1: Linear interpolation at grid center

Inputs:

points_x points_y values xi
0 1 0 1 0 1 0.5 0.5
1 2

Excel formula:

=GRID_INTERP({0,1}, {0,1}, {0,1;1,2}, {0.5,0.5})

Expected output:

1

Example 2: Nearest-neighbor on 3x2 grid

Inputs:

points_x points_y values xi grid_interp_method
0 1 2 0 1 1 2 0.4 0.6 nearest
3 4
5 6

Excel formula:

=GRID_INTERP({0,1,2}, {0,1}, {1,2;3,4;5,6}, {0.4,0.6}, "nearest")

Expected output:

2

Example 3: Linear interpolation at multiple query points

Inputs:

points_x points_y values xi
0 1 0 1 0 1 0 0
2 3 1 1
0.5 0.5

Excel formula:

=GRID_INTERP({0,1}, {0,1}, {0,1;2,3}, {0,0;1,1;0.5,0.5})

Expected output:

Result
0
3
1.5
Example 4: Out-of-bounds query with custom fill value

Inputs:

points_x points_y values xi bounds_error fill_value
0 1 0 1 1 2 0.5 0.5 false -999
3 4 1.5 1.5

Excel formula:

=GRID_INTERP({0,1}, {0,1}, {1,2;3,4}, {0.5,0.5;1.5,1.5}, FALSE, -999)

Expected output:

Result
2.5
-999

Python Code

Show Code
import math
import numpy as np
from scipy.interpolate import RegularGridInterpolator as scipy_RegularGridInterpolator

def grid_interp(points_x, points_y, values, xi, grid_interp_method='linear', bounds_error=True, fill_value=None):
    """
    Interpolator on a regular grid in 2D.

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

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

    Args:
        points_x (list[list]): 1D array of x-coordinates of the grid points
        points_y (list[list]): 1D array of y-coordinates of the grid points
        values (list[list]): 2D array of data values on the grid
        xi (list[list]): Points at which to interpolate data (n_points, 2)
        grid_interp_method (str, optional): Interpolation method Valid options: Linear, Nearest. Default is 'linear'.
        bounds_error (bool, optional): If True, raise error for out-of-bounds points Default is True.
        fill_value (float, optional): Value for out-of-bounds points when bounds_error is False Default is None.

    Returns:
        list[list]: A 2D list of interpolated values, or an error message (str) if invalid.
    """
    try:
      def to2d(x):
        return [[x]] if not isinstance(x, list) else x

      def flatten(arr):
        return [item for sublist in arr for item in sublist]

      # Normalize 2D list inputs
      points_x = to2d(points_x)
      points_y = to2d(points_y)
      values = to2d(values)
      xi = to2d(xi)

      # Validate grid_interp_method parameter
      valid_methods = ["linear", "nearest"]
      if not isinstance(grid_interp_method, str):
        return "Error: Invalid input: grid_interp_method must be a string."
      if grid_interp_method not in valid_methods:
        return f"Error: Invalid input: grid_interp_method must be one of {valid_methods}."

      # Validate bounds_error parameter
      if not isinstance(bounds_error, bool):
        return "Error: Invalid input: bounds_error must be a boolean."

      # Validate fill_value parameter
      if fill_value is not None:
        try:
          fill_value = float(fill_value)
          if math.isnan(fill_value):
            # NaN is allowed as fill_value
            pass
          elif math.isinf(fill_value):
            return "Error: Invalid input: fill_value must be finite or NaN."
        except (TypeError, ValueError):
          return "Error: Invalid input: fill_value must be a number or None."
      else:
        fill_value = float("nan")

      try:
        # Flatten points_x and points_y
        points_x_flat = flatten(points_x)
        points_y_flat = flatten(points_y)

        # Validate points are numeric
        for i, val in enumerate(points_x_flat):
          if not isinstance(val, (int, float)):
            return f"Error: Invalid input: points_x[{i}] must be numeric."
          if math.isnan(val) or math.isinf(val):
            return f"Error: Invalid input: points_x[{i}] must be finite."

        for i, val in enumerate(points_y_flat):
          if not isinstance(val, (int, float)):
            return f"Error: Invalid input: points_y[{i}] must be numeric."
          if math.isnan(val) or math.isinf(val):
            return f"Error: Invalid input: points_y[{i}] must be finite."

        # Convert values to numpy array
        values_arr = np.array(values, dtype=float)

        # Validate values dimensions
        if values_arr.ndim != 2:
          return "Error: Invalid input: values must be a 2D array."

        if values_arr.shape[0] != len(points_x_flat):
          return f"Error: Invalid input: values must have shape ({len(points_x_flat)}, {len(points_y_flat)})."

        if values_arr.shape[1] != len(points_y_flat):
          return f"Error: Invalid input: values must have shape ({len(points_x_flat)}, {len(points_y_flat)})."

        # Convert xi to numpy array
        xi_arr = np.array(xi, dtype=float)

        # Validate xi dimensions
        if xi_arr.ndim != 2:
          return "Error: Invalid input: xi must be a 2D array."

        if xi_arr.shape[1] != 2:
          return "Error: Invalid input: xi must have shape (n_points, 2)."

        # Create interpolator
        points = (points_x_flat, points_y_flat)
        interp = scipy_RegularGridInterpolator(
          points,
          values_arr,
          method=grid_interp_method,
          bounds_error=bounds_error,
          fill_value=fill_value,
        )

        # Perform interpolation
        result = interp(xi_arr)

        # Convert result to 2D list (column vector)
        return [[float(val)] for val in result]

      except ValueError as e:
        return f"Error: Invalid input: {e}"
      except Exception as e:
        return f"Error: scipy.interpolate.RegularGridInterpolator error: {e}"
    except Exception as e:
      return f"Error: {str(e)}"

Online Calculator

1D array of x-coordinates of the grid points
1D array of y-coordinates of the grid points
2D array of data values on the grid
Points at which to interpolate data (n_points, 2)
Interpolation method
If True, raise error for out-of-bounds points
Value for out-of-bounds points when bounds_error is False

GRIDDATA

This function interpolates values defined at scattered points in D dimensions and evaluates the interpolant at new query points. Depending on the selected method, interpolation is nearest-neighbor, piecewise linear on simplices, or piecewise cubic (for supported dimensions).

Given sample points \mathbf{x}_i with values v_i, it computes estimates \hat{v}(\mathbf{x}) for each query point \mathbf{x} by applying the chosen interpolation rule over the point cloud.

Excel Usage

=GRIDDATA(points, values, xi, fill_value, griddata_method)
  • points (list[list], required): Data point coordinates (n_points, n_dims)
  • values (list[list], required): Data point values (n_points, 1)
  • xi (list[list], required): Points at which to interpolate (n_new_points, n_dims)
  • fill_value (float, required): Value for points outside the convex hull
  • griddata_method (str, optional, default: “linear”): Interpolation method

Returns (list[list]): 2D list of interpolated values (n_new_points, 1), or error message (str) if invalid.

Example 1: Linear interpolation on 2D unit square

Inputs:

points values xi fill_value
0 0 0 0.5 0.5 0
1 0 1
0 1 1
1 1 2

Excel formula:

=GRIDDATA({0,0;1,0;0,1;1,1}, {0;1;1;2}, {0.5,0.5}, 0)

Expected output:

1

Example 2: Nearest-neighbor interpolation in 2D

Inputs:

points values xi griddata_method fill_value
0 0 0 0.1 0.1 nearest 0
1 0 1
0 1 2

Excel formula:

=GRIDDATA({0,0;1,0;0,1}, {0;1;2}, {0.1,0.1}, "nearest", 0)

Expected output:

0

Example 3: Cubic interpolation with custom fill value

Inputs:

points values xi griddata_method fill_value
0 0 0 0.5 0.5 cubic -999
1 0 0
0 1 0
1 1 0
0.5 0.5 1

Excel formula:

=GRIDDATA({0,0;1,0;0,1;1,1;0.5,0.5}, {0;0;0;0;1}, {0.5,0.5}, "cubic", -999)

Expected output:

1

Example 4: Linear interpolation on 1D data

Inputs:

points values xi griddata_method fill_value
0 0 1.5 linear 0
1 1
2 4

Excel formula:

=GRIDDATA({0;1;2}, {0;1;4}, {1.5}, "linear", 0)

Expected output:

2.5

Python Code

Show Code
import numpy as np
from scipy.interpolate import griddata as scipy_griddata

def griddata(points, values, xi, fill_value, griddata_method='linear'):
    """
    Interpolate unstructured D-D data.

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

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

    Args:
        points (list[list]): Data point coordinates (n_points, n_dims)
        values (list[list]): Data point values (n_points, 1)
        xi (list[list]): Points at which to interpolate (n_new_points, n_dims)
        fill_value (float): Value for points outside the convex hull
        griddata_method (str, optional): Interpolation method Valid options: Linear, Nearest, Cubic. Default is 'linear'.

    Returns:
        list[list]: 2D list of interpolated values (n_new_points, 1), or error message (str) if invalid.
    """
    try:
        def to2d(x):
            return [[x]] if not isinstance(x, list) else x

        def validate_and_convert(arr, name):
            if not isinstance(arr, list):
                return None, f"Error: Invalid input: {name} must be a list."
            if len(arr) == 0:
                return None, f"Error: Invalid input: {name} must not be empty."

            try:
                np_arr = np.array(arr, dtype=float)
            except Exception as e:
                return None, f"Error: Invalid input: {name} could not be converted to numeric array. Details: {e}"

            if np_arr.ndim != 2:
                return None, f"Error: Invalid input: {name} must be a 2D array."

            return np_arr, None

        # Normalize inputs to 2D lists
        points = to2d(points)
        values = to2d(values)
        xi = to2d(xi)

        # Validate and convert to numpy arrays
        points_arr, error = validate_and_convert(points, "points")
        if error:
            return error
        values_arr, error = validate_and_convert(values, "values")
        if error:
            return error
        xi_arr, error = validate_and_convert(xi, "xi")
        if error:
            return error

        # Validate method
        valid_methods = ["linear", "nearest", "cubic"]
        if not isinstance(griddata_method, str):
            return "Error: Invalid input: griddata_method must be a string."
        if griddata_method not in valid_methods:
            return f"Error: Invalid input: griddata_method must be one of {valid_methods}."

        # Validate fill_value
        if not isinstance(fill_value, (int, float)):
            return "Error: Invalid input: fill_value must be numeric."

        n_points = points_arr.shape[0]
        n_dims = points_arr.shape[1]

        # Validate minimum points for cubic method
        if griddata_method == "cubic" and n_dims > 2:
            return "Error: Invalid input: cubic method only supports 1D and 2D data."
        if griddata_method == "cubic" and n_dims == 2 and n_points < 4:
            return "Error: Invalid input: cubic method requires at least 4 points for 2D data."

        if values_arr.shape[0] != n_points:
            return f"Error: Invalid input: points and values must have same number of rows (got {n_points} and {values_arr.shape[0]})."
        if values_arr.shape[1] != 1:
            return f"Error: Invalid input: values must have exactly 1 column (got {values_arr.shape[1]})."
        if xi_arr.shape[1] != n_dims:
            return f"Error: Invalid input: xi must have same number of columns as points (got {xi_arr.shape[1]} and {n_dims})."

        # Flatten values for scipy.interpolate.griddata
        values_flat = values_arr.flatten()

        # Check for invalid values in input data
        if np.any(np.isinf(points_arr)):
            return "Error: Invalid input: points contains infinite values."
        if np.any(np.isinf(values_flat)):
            return "Error: Invalid input: values contains infinite values."
        if np.any(np.isinf(xi_arr)):
            return "Error: Invalid input: xi contains infinite values."

        # Perform interpolation
        try:
            result = scipy_griddata(
                points_arr, values_flat, xi_arr, method=griddata_method, fill_value=fill_value
            )
        except Exception as e:
            return f"Error: scipy.interpolate.griddata error: {e}"

        # Convert result to 2D list
        if not isinstance(result, np.ndarray):
            return "Error: scipy.interpolate.griddata error: unexpected result type."

        # Handle different result shapes
        if result.ndim == 0:
            # Scalar result (single point interpolation)
            return [[float(result)]]
        elif result.ndim == 1:
            # Most common case: 1D array of interpolated values
            return [[float(val)] for val in result]
        else:
            # Higher dimensional result, flatten it
            result_flat = result.flatten()
            return [[float(val)] for val in result_flat]
    except Exception as e:
        return f"Error: {str(e)}"

Online Calculator

Data point coordinates (n_points, n_dims)
Data point values (n_points, 1)
Points at which to interpolate (n_new_points, n_dims)
Value for points outside the convex hull
Interpolation method

INTERPN

This function interpolates values on a regular or rectilinear 2D grid and evaluates the interpolant at specified query points. It supports linear, nearest-neighbor, and spline-based 2D interpolation modes supported by the underlying library.

If grid axes are x_i and y_j with tabulated values f(x_i, y_j), the function estimates \hat{f}(x, y) for each query coordinate pair (x, y) using the selected interpolation method.

Excel Usage

=INTERPN(points_x, points_y, values, xi, interpn_method, bounds_error, fill_value)
  • points_x (list[list], required): Column vector of x-coordinates of the grid points
  • points_y (list[list], required): Column vector of y-coordinates of the grid points
  • values (list[list], required): 2D array of data values on the grid
  • xi (list[list], required): Points at which to interpolate data (n_points, 2)
  • interpn_method (str, optional, default: “linear”): Interpolation method
  • bounds_error (bool, optional, default: true): If True, error on out-of-bounds points
  • fill_value (float, optional, default: null): Value for out-of-bounds points if bounds_error is False

Returns (list[list]): 2D list (column vector) of interpolated values, or error message (str) if invalid.

Example 1: Linear interpolation at grid center

Inputs:

points_x points_y values xi
0 0 0 1 0.5 0.5
1 1 1 2

Excel formula:

=INTERPN({0;1}, {0;1}, {0,1;1,2}, {0.5,0.5})

Expected output:

1

Example 2: Nearest-neighbor on 3x3 grid

Inputs:

points_x points_y values xi interpn_method
0 0 1 2 3 0.4 0.6 nearest
1 1 4 5 6
2 2 7 8 9

Excel formula:

=INTERPN({0;1;2}, {0;1;2}, {1,2,3;4,5,6;7,8,9}, {0.4,0.6}, "nearest")

Expected output:

2

Example 3: Out-of-bounds query with custom fill value

Inputs:

points_x points_y values xi interpn_method bounds_error fill_value
0 0 1 2 1.5 1.5 linear false -999
1 1 3 4

Excel formula:

=INTERPN({0;1}, {0;1}, {1,2;3,4}, {1.5,1.5}, "linear", FALSE, -999)

Expected output:

-999

Example 4: Linear interpolation at multiple query points

Inputs:

points_x points_y values xi interpn_method bounds_error
0 0 1 2 3 0.5 0.5 linear true
1 1 2 3 4 1.5 1
2 2 3 4 5 2 1.5
3 4 5 6

Excel formula:

=INTERPN({0;1;2;3}, {0;1;2}, {1,2,3;2,3,4;3,4,5;4,5,6}, {0.5,0.5;1.5,1;2,1.5}, "linear", TRUE)

Expected output:

Result
2
3.5
4.5

Python Code

Show Code
import math
import numpy as np
from scipy.interpolate import interpn as scipy_interpn

def interpn(points_x, points_y, values, xi, interpn_method='linear', bounds_error=True, fill_value=None):
    """
    Multidimensional interpolation on regular grids (2D).

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

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

    Args:
        points_x (list[list]): Column vector of x-coordinates of the grid points
        points_y (list[list]): Column vector of y-coordinates of the grid points
        values (list[list]): 2D array of data values on the grid
        xi (list[list]): Points at which to interpolate data (n_points, 2)
        interpn_method (str, optional): Interpolation method Valid options: Linear, Nearest, Spline 2D. Default is 'linear'.
        bounds_error (bool, optional): If True, error on out-of-bounds points Default is True.
        fill_value (float, optional): Value for out-of-bounds points if bounds_error is False Default is None.

    Returns:
        list[list]: 2D list (column vector) of interpolated values, or error message (str) if invalid.
    """
    try:
        def to2d(x):
            return [[x]] if not isinstance(x, list) else x

        def flatten(arr):
            return [item for sublist in arr for item in sublist]

        def validate_numeric_2d(arr, name):
            if not isinstance(arr, list):
                return f"Error: Invalid input: {name} must be a 2D list."
            if not all(isinstance(row, list) for row in arr):
                return f"Error: Invalid input: {name} must be a 2D list."
            flat = flatten(arr)
            if not flat:
                return f"Error: Invalid input: {name} must not be empty."
            for val in flat:
                if not isinstance(val, (int, float)):
                    return f"Error: Invalid input: {name} must contain only numeric values."
                if math.isnan(val) or math.isinf(val):
                    return f"Error: Invalid input: {name} must contain only finite values."
            return None

        # Normalize inputs
        points_x = to2d(points_x)
        points_y = to2d(points_y)
        values = to2d(values)
        xi = to2d(xi)

        # Validate inputs
        err = validate_numeric_2d(points_x, "points_x")
        if err:
            return err
        err = validate_numeric_2d(points_y, "points_y")
        if err:
            return err
        err = validate_numeric_2d(values, "values")
        if err:
            return err
        err = validate_numeric_2d(xi, "xi")
        if err:
            return err

        # Validate interpn_method
        valid_methods = ['linear', 'nearest', 'splinef2d']
        if not isinstance(interpn_method, str) or interpn_method not in valid_methods:
            return f"Error: Invalid input: interpn_method must be one of {valid_methods}."

        # Validate bounds_error
        if not isinstance(bounds_error, bool):
            return "Error: Invalid input: bounds_error must be a boolean."

        # Validate and set fill_value
        if fill_value is None:
            fill_value = float('nan')
        else:
            if not isinstance(fill_value, (int, float)):
                return "Error: Invalid input: fill_value must be a number."
            fill_value = float(fill_value)

        # Flatten grid coordinates
        points_x_flat = flatten(points_x)
        points_y_flat = flatten(points_y)

        # Check grid monotonicity
        if len(points_x_flat) < 2 or len(points_y_flat) < 2:
            return "Error: Invalid input: grid coordinates must have at least 2 points."

        for i in range(len(points_x_flat) - 1):
            if points_x_flat[i] >= points_x_flat[i + 1]:
                return "Error: Invalid input: points_x must be strictly increasing."

        for i in range(len(points_y_flat) - 1):
            if points_y_flat[i] >= points_y_flat[i + 1]:
                return "Error: Invalid input: points_y must be strictly increasing."

        # Validate values dimensions
        if len(values) != len(points_x_flat):
            return f"Error: Invalid input: values must have {len(points_x_flat)} rows to match points_x."

        for row in values:
            if len(row) != len(points_y_flat):
                return f"Error: Invalid input: each row in values must have {len(points_y_flat)} columns to match points_y."

        # Validate xi dimensions
        for row in xi:
            if len(row) != 2:
                return "Error: Invalid input: each row in xi must have exactly 2 columns [x, y]."

        # Convert to numpy arrays
        try:
            values_arr = np.array(values, dtype=float)
            xi_arr = np.array(xi, dtype=float)
            points = (np.array(points_x_flat, dtype=float), np.array(points_y_flat, dtype=float))
        except Exception as exc:
            return f"Error: Invalid input: unable to convert inputs to arrays: {exc}"

        # Perform interpolation
        try:
            result = scipy_interpn(
                points,
                values_arr,
                xi_arr,
                method=interpn_method,
                bounds_error=bounds_error,
                fill_value=fill_value,
            )
        except Exception as exc:
            return f"Error: scipy.interpolate.interpn error: {exc}"

        # Convert result to 2D list (column vector)
        if not isinstance(result, np.ndarray):
            return "Error: scipy.interpolate.interpn error: unexpected result type."

        result_list = [[float(val)] for val in result.flatten()]

        # Validate result
        for row in result_list:
            for val in row:
                if not isinstance(val, float):
                    return "Error: scipy.interpolate.interpn error: non-numeric result."
                # Allow NaN if fill_value is NaN, but not inf
                if math.isinf(val):
                    return "Error: scipy.interpolate.interpn error: result contains infinite values."

        return result_list
    except Exception as e:
        return f"Error: {str(e)}"

Online Calculator

Column vector of x-coordinates of the grid points
Column vector of y-coordinates of the grid points
2D array of data values on the grid
Points at which to interpolate data (n_points, 2)
Interpolation method
If True, error on out-of-bounds points
Value for out-of-bounds points if bounds_error is False

LINEAR_ND_INTERP

This function performs piecewise linear interpolation for scattered data in dimensions greater than one. It triangulates the input point set and evaluates a linear interpolant within each simplex.

For data points \mathbf{x}_i \in \mathbb{R}^N with values v_i, the estimate at a query point \mathbf{x} is computed by barycentric linear combination on the simplex containing \mathbf{x}.

Excel Usage

=LINEAR_ND_INTERP(points, values, xi, fill_value)
  • points (list[list], required): The coordinates of the data points (n_points x n_dims)
  • values (list[list], required): The values at the data points (n_points x 1)
  • xi (list[list], required): The points at which to interpolate (n_new_points x n_dims)
  • fill_value (float, optional, default: 0): Value used for points outside the convex hull

Returns (list[list]): Interpolated values as a 2D list, or error message string.

Example 1: Linear interpolation on 2D unit square

Inputs:

points values xi
0 0 0 0.5 0.5
1 0 1
0 1 1
1 1 2

Excel formula:

=LINEAR_ND_INTERP({0,0;1,0;0,1;1,1}, {0;1;1;2}, {0.5,0.5})

Expected output:

1

Example 2: 2D interpolation with out-of-hull fill value

Inputs:

points values xi fill_value
0 0 1 0.5 0 -999
1 0 2 10 10
0 1 3
1 1 4

Excel formula:

=LINEAR_ND_INTERP({0,0;1,0;0,1;1,1}, {1;2;3;4}, {0.5,0;10,10}, -999)

Expected output:

Result
1.5
-999
Example 3: Linear interpolation in 3D tetrahedron

Inputs:

points values xi
0 0 0 0 0.25 0.25 0.25
1 0 0 1
0 1 0 2
0 0 1 3

Excel formula:

=LINEAR_ND_INTERP({0,0,0;1,0,0;0,1,0;0,0,1}, {0;1;2;3}, {0.25,0.25,0.25})

Expected output:

1.5

Example 4: Multiple query points on 2D square

Inputs:

points values xi fill_value
0 0 0 1 0 0
2 0 4 0 1
0 2 4 1 1
2 2 8

Excel formula:

=LINEAR_ND_INTERP({0,0;2,0;0,2;2,2}, {0;4;4;8}, {1,0;0,1;1,1}, 0)

Expected output:

Result
2
2
4

Python Code

Show Code
import math
import numpy as np
from scipy.interpolate import LinearNDInterpolator as scipy_LinearNDInterpolator

def linear_nd_interp(points, values, xi, fill_value=0):
    """
    Piecewise linear interpolator in N > 1 dimensions.

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

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

    Args:
        points (list[list]): The coordinates of the data points (n_points x n_dims)
        values (list[list]): The values at the data points (n_points x 1)
        xi (list[list]): The points at which to interpolate (n_new_points x n_dims)
        fill_value (float, optional): Value used for points outside the convex hull Default is 0.

    Returns:
        list[list]: Interpolated values as a 2D list, or error message string.
    """
    try:
        def to2d(x):
            return [[x]] if not isinstance(x, list) else x

        # Normalize inputs to 2D lists
        points = to2d(points)
        values = to2d(values)
        xi = to2d(xi)

        # Validate that inputs are 2D lists
        if not isinstance(points, list) or not all(isinstance(row, list) for row in points):
            return "Error: Invalid input: points must be a 2D list."
        if not isinstance(values, list) or not all(isinstance(row, list) for row in values):
            return "Error: Invalid input: values must be a 2D list."
        if not isinstance(xi, list) or not all(isinstance(row, list) for row in xi):
            return "Error: Invalid input: xi must be a 2D list."

        # Validate fill_value
        if not isinstance(fill_value, (int, float)):
            return "Error: Invalid input: fill_value must be a number."
        fill_value = float(fill_value)

        # Check dimensions
        if len(points) == 0:
            return "Error: Invalid input: points must not be empty."
        if len(values) == 0:
            return "Error: Invalid input: values must not be empty."
        if len(xi) == 0:
            return "Error: Invalid input: xi must not be empty."

        if len(points) != len(values):
            return "Error: Invalid input: points and values must have the same number of rows."

        # Validate that all rows have consistent dimensions
        n_dims = len(points[0])
        if n_dims < 2:
            return "Error: Invalid input: points must have at least 2 dimensions (N > 1)."

        for i, row in enumerate(points):
            if len(row) != n_dims:
                return f"Error: Invalid input: all rows in points must have the same length (row {i} mismatch)."

        for i, row in enumerate(values):
            if len(row) != 1:
                return f"Error: Invalid input: values must be a column vector (row {i} has {len(row)} columns)."

        xi_dims = len(xi[0])
        if xi_dims != n_dims:
            return f"Error: Invalid input: xi must have same dimensions as points ({n_dims})."

        for i, row in enumerate(xi):
            if len(row) != xi_dims:
                return f"Error: Invalid input: all rows in xi must have the same length (row {i} mismatch)."

        # Validate that all values are numeric and finite
        try:
            for i, row in enumerate(points):
                for j, val in enumerate(row):
                    if not isinstance(val, (int, float)):
                        return f"Error: Invalid input: points[{i}][{j}] must be numeric."
                    if math.isinf(val) or math.isnan(val):
                        return f"Error: Invalid input: points[{i}][{j}] must be finite."

            for i, row in enumerate(values):
                val = row[0]
                if not isinstance(val, (int, float)):
                    return f"Error: Invalid input: values[{i}][0] must be numeric."
                if math.isinf(val) or math.isnan(val):
                    return f"Error: Invalid input: values[{i}][0] must be finite."

            for i, row in enumerate(xi):
                for j, val in enumerate(row):
                    if not isinstance(val, (int, float)):
                        return f"Error: Invalid input: xi[{i}][{j}] must be numeric."
                    if math.isinf(val) or math.isnan(val):
                        return f"Error: Invalid input: xi[{i}][{j}] must be finite."
        except Exception as e:
            return f"Error: Invalid input: error validating numeric values: {e}"

        # Convert to numpy arrays
        try:
            points_arr = np.array(points, dtype=float)
            values_arr = np.array(values, dtype=float).flatten()
            xi_arr = np.array(xi, dtype=float)
        except Exception as e:
            return f"Error: Invalid input: error converting to arrays: {e}"

        # Perform interpolation
        try:
            interp = scipy_LinearNDInterpolator(points_arr, values_arr, fill_value=fill_value)
            result = interp(xi_arr)
        except Exception as e:
            return f"Error: scipy.interpolate.LinearNDInterpolator error: {e}"

        # Convert result to 2D list
        try:
            result_2d = [[float(val)] for val in result]
        except Exception as e:
            return f"Error: Error converting result to 2D list: {e}"

        return result_2d
    except Exception as e:
        return f"Error: {str(e)}"

Online Calculator

The coordinates of the data points (n_points x n_dims)
The values at the data points (n_points x 1)
The points at which to interpolate (n_new_points x n_dims)
Value used for points outside the convex hull

LSQ_BIVAR_SPL

This function wraps SciPy’s least-squares bivariate spline class to fit a smooth surface to scattered 2D data with explicit interior knot placement. It provides more direct control of spline flexibility than automatic-knot methods.

For observations (x_i, y_i, z_i) and knot vectors in each axis, the fitted spline minimizes a weighted least-squares objective and then evaluates \hat{z}(x, y) at requested query pairs.

Excel Usage

=LSQ_BIVAR_SPL(x, y, z, tx, ty, xi, yi, w, kx, ky, eps)
  • x (list[list], required): x-coordinates of observed points.
  • y (list[list], required): y-coordinates of observed points.
  • z (list[list], required): Observed values at (x, y) points.
  • tx (list[list], required): Strictly increasing interior knots in x-direction.
  • ty (list[list], required): Strictly increasing interior knots in y-direction.
  • xi (list[list], required): x-coordinates of query points.
  • yi (list[list], required): y-coordinates of query points.
  • w (list[list], optional, default: null): Positive observation weights.
  • kx (int, optional, default: 3): Spline degree in x-direction.
  • ky (int, optional, default: 3): Spline degree in y-direction.
  • eps (float, optional, default: null): Rank threshold for linear system solve.

Returns (list[list]): Evaluated spline values as a 2D list (column vector), or an error message string.

Example 1: LSQ spline fit on planar surface with linear degrees

Inputs:

x y z tx ty xi yi kx ky
0 0 0 1.5 1.5 0.5 0.5 1 1
1 0 1 2.5 2.5
2 0 2
3 0 3
0 1 1
1 1 2
2 1 3
3 1 4
0 2 2
1 2 3
2 2 4
3 2 5
0 3 3
1 3 4
2 3 5
3 3 6

Excel formula:

=LSQ_BIVAR_SPL({0;1;2;3;0;1;2;3;0;1;2;3;0;1;2;3}, {0;0;0;0;1;1;1;1;2;2;2;2;3;3;3;3}, {0;1;2;3;1;2;3;4;2;3;4;5;3;4;5;6}, {1.5}, {1.5}, {0.5;2.5}, {0.5;2.5}, 1, 1)

Expected output:

Result
1
5
Example 2: Weighted LSQ spline fit

Inputs:

x y z tx ty xi yi w kx ky
0 0 0 1.5 1.5 1 1 1 1 1
1 0 1 2 2 1
2 0 2 1
3 0 3 1
0 1 1 1
1 1 2 2
2 1 3 2
3 1 4 1
0 2 2 1
1 2 3 2
2 2 4 2
3 2 5 1
0 3 3 1
1 3 4 1
2 3 5 1
3 3 6 1

Excel formula:

=LSQ_BIVAR_SPL({0;1;2;3;0;1;2;3;0;1;2;3;0;1;2;3}, {0;0;0;0;1;1;1;1;2;2;2;2;3;3;3;3}, {0;1;2;3;1;2;3;4;2;3;4;5;3;4;5;6}, {1.5}, {1.5}, {1;2}, {1;2}, {1;1;1;1;1;2;2;1;1;2;2;1;1;1;1;1}, 1, 1)

Expected output:

Result
2
4
Example 3: LSQ spline with two interior knots per axis

Inputs:

x y z tx ty xi yi kx ky
0 0 0 1 1 0.75 1.25 1 1
1 0 1 2 2 2.25 2.75
2 0 2
3 0 3
0 1 1
1 1 2
2 1 3
3 1 4
0 2 2
1 2 3
2 2 4
3 2 5
0 3 3
1 3 4
2 3 5
3 3 6

Excel formula:

=LSQ_BIVAR_SPL({0;1;2;3;0;1;2;3;0;1;2;3;0;1;2;3}, {0;0;0;0;1;1;1;1;2;2;2;2;3;3;3;3}, {0;1;2;3;1;2;3;4;2;3;4;5;3;4;5;6}, {1;2}, {1;2}, {0.75;2.25}, {1.25;2.75}, 1, 1)

Expected output:

Result
2
5
Example 4: LSQ spline with custom eps threshold

Inputs:

x y z tx ty xi yi kx ky eps
0 0 0 1.5 1.5 1.2 0.8 1 1 1e-12
1 0 1 2.2 2.1
2 0 2
3 0 3
0 1 1
1 1 2
2 1 3
3 1 4
0 2 2
1 2 3
2 2 4
3 2 5
0 3 3
1 3 4
2 3 5
3 3 6

Excel formula:

=LSQ_BIVAR_SPL({0;1;2;3;0;1;2;3;0;1;2;3;0;1;2;3}, {0;0;0;0;1;1;1;1;2;2;2;2;3;3;3;3}, {0;1;2;3;1;2;3;4;2;3;4;5;3;4;5;6}, {1.5}, {1.5}, {1.2;2.2}, {0.8;2.1}, 1, 1, 1e-12)

Expected output:

Result
2
4.3

Python Code

Show Code
import math
import numpy as np
from scipy.interpolate import LSQBivariateSpline as scipy_LSQBivariateSpline

def lsq_bivar_spl(x, y, z, tx, ty, xi, yi, w=None, kx=3, ky=3, eps=None):
    """
    Weighted least-squares bivariate spline with user-specified knots.

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

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

    Args:
        x (list[list]): x-coordinates of observed points.
        y (list[list]): y-coordinates of observed points.
        z (list[list]): Observed values at (x, y) points.
        tx (list[list]): Strictly increasing interior knots in x-direction.
        ty (list[list]): Strictly increasing interior knots in y-direction.
        xi (list[list]): x-coordinates of query points.
        yi (list[list]): y-coordinates of query points.
        w (list[list], optional): Positive observation weights. Default is None.
        kx (int, optional): Spline degree in x-direction. Default is 3.
        ky (int, optional): Spline degree in y-direction. Default is 3.
        eps (float, optional): Rank threshold for linear system solve. Default is None.

    Returns:
        list[list]: Evaluated spline values as a 2D list (column vector), or an error message string.
    """
    try:
        def to2d(x):
            return [[x]] if not isinstance(x, list) else x

        def flatten_2d(arr):
            out = []
            for row in arr:
                if not isinstance(row, list):
                    return None
                for val in row:
                    out.append(val)
            return out

        def validate_finite_numeric(values, name):
            for i, val in enumerate(values):
                if not isinstance(val, (int, float)):
                    return f"Error: Invalid input: {name} element {i} must be numeric."
                if math.isnan(val) or math.isinf(val):
                    return f"Error: Invalid input: {name} element {i} must be finite."
            return None

        x = to2d(x)
        y = to2d(y)
        z = to2d(z)
        tx = to2d(tx)
        ty = to2d(ty)
        xi = to2d(xi)
        yi = to2d(yi)

        x_flat = flatten_2d(x)
        y_flat = flatten_2d(y)
        z_flat = flatten_2d(z)
        tx_flat = flatten_2d(tx)
        ty_flat = flatten_2d(ty)
        xi_flat = flatten_2d(xi)
        yi_flat = flatten_2d(yi)

        if x_flat is None or y_flat is None or z_flat is None or tx_flat is None or ty_flat is None or xi_flat is None or yi_flat is None:
            return "Error: Invalid input: all array inputs must be 2D lists."

        if len(x_flat) == 0 or len(y_flat) == 0 or len(z_flat) == 0:
            return "Error: Invalid input: x, y, and z must not be empty."
        if len(xi_flat) == 0 or len(yi_flat) == 0:
            return "Error: Invalid input: xi and yi must not be empty."

        if len(x_flat) != len(y_flat) or len(x_flat) != len(z_flat):
            return "Error: Invalid input: x, y, and z must have the same length."
        if len(xi_flat) != len(yi_flat):
            return "Error: Invalid input: xi and yi must have the same length."

        error = validate_finite_numeric(x_flat, "x")
        if error:
            return error
        error = validate_finite_numeric(y_flat, "y")
        if error:
            return error
        error = validate_finite_numeric(z_flat, "z")
        if error:
            return error
        error = validate_finite_numeric(tx_flat, "tx")
        if error:
            return error
        error = validate_finite_numeric(ty_flat, "ty")
        if error:
            return error
        error = validate_finite_numeric(xi_flat, "xi")
        if error:
            return error
        error = validate_finite_numeric(yi_flat, "yi")
        if error:
            return error

        if not isinstance(kx, int) or not isinstance(ky, int):
            return "Error: Invalid input: kx and ky must be integers."
        if kx < 1 or ky < 1:
            return "Error: Invalid input: kx and ky must be at least 1."

        min_points = (kx + 1) * (ky + 1)
        if len(x_flat) < min_points:
            return f"Error: Invalid input: need at least {min_points} points for kx={kx}, ky={ky}."

        for i in range(len(tx_flat) - 1):
            if tx_flat[i] >= tx_flat[i + 1]:
                return "Error: Invalid input: tx must be strictly increasing."
        for i in range(len(ty_flat) - 1):
            if ty_flat[i] >= ty_flat[i + 1]:
                return "Error: Invalid input: ty must be strictly increasing."

        x_min = min(x_flat)
        x_max = max(x_flat)
        y_min = min(y_flat)
        y_max = max(y_flat)

        for i, knot in enumerate(tx_flat):
            if knot <= x_min or knot >= x_max:
                return f"Error: Invalid input: tx element {i} must lie strictly inside the x data range."
        for i, knot in enumerate(ty_flat):
            if knot <= y_min or knot >= y_max:
                return f"Error: Invalid input: ty element {i} must lie strictly inside the y data range."

        w_arr = None
        if w is not None:
            w = to2d(w)
            w_flat = flatten_2d(w)
            if w_flat is None:
                return "Error: Invalid input: w must be a 2D list."
            if len(w_flat) != len(x_flat):
                return "Error: Invalid input: w must match length of x, y, and z."
            error = validate_finite_numeric(w_flat, "w")
            if error:
                return error
            for i, val in enumerate(w_flat):
                if val <= 0:
                    return f"Error: Invalid input: w element {i} must be positive."
            w_arr = np.array(w_flat, dtype=float)

        if eps is not None:
            if not isinstance(eps, (int, float)):
                return "Error: Invalid input: eps must be numeric when provided."
            if eps <= 0 or eps >= 1:
                return "Error: Invalid input: eps must be in the interval (0, 1)."

        x_arr = np.array(x_flat, dtype=float)
        y_arr = np.array(y_flat, dtype=float)
        z_arr = np.array(z_flat, dtype=float)
        tx_arr = np.array(tx_flat, dtype=float)
        ty_arr = np.array(ty_flat, dtype=float)
        xi_arr = np.array(xi_flat, dtype=float)
        yi_arr = np.array(yi_flat, dtype=float)

        spline = scipy_LSQBivariateSpline(
            x_arr,
            y_arr,
            z_arr,
            tx_arr,
            ty_arr,
            w=w_arr,
            kx=kx,
            ky=ky,
            eps=eps
        )

        result = spline.ev(xi_arr, yi_arr)
        result_arr = np.asarray(result, dtype=float).reshape(-1)
        return [[float(v)] for v in result_arr]
    except Exception as e:
        return f"Error: {str(e)}"

Online Calculator

x-coordinates of observed points.
y-coordinates of observed points.
Observed values at (x, y) points.
Strictly increasing interior knots in x-direction.
Strictly increasing interior knots in y-direction.
x-coordinates of query points.
y-coordinates of query points.
Positive observation weights.
Spline degree in x-direction.
Spline degree in y-direction.
Rank threshold for linear system solve.

NEAREST_ND_INTERP

This function interpolates scattered multidimensional data by assigning each query point the value of the nearest known sample point. It is suitable when a piecewise constant approximation is acceptable.

For each query point \mathbf{x}, the output is \hat{v}(\mathbf{x}) = v_{i^*} where i^* = \arg\min_i \|\mathbf{x} - \mathbf{x}_i\|.

Excel Usage

=NEAREST_ND_INTERP(points, values, xi)
  • points (list[list], required): Data point coordinates (n_points, n_dims)
  • values (list[list], required): Data values (n_points, 1)
  • xi (list[list], required): Query points (n_new_points, n_dims)

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

Example 1: Nearest-neighbor on 2D unit square

Inputs:

points values xi
0 0 0 0.1 0.1
1 0 1
0 1 1
1 1 2

Excel formula:

=NEAREST_ND_INTERP({0,0;1,0;0,1;1,1}, {0;1;1;2}, {0.1,0.1})

Expected output:

0

Example 2: Multiple query points in 2D

Inputs:

points values xi
0 0 0 0.1 0.1
1 0 1 0.9 0.9
0 1 1
1 1 2

Excel formula:

=NEAREST_ND_INTERP({0,0;1,0;0,1;1,1}, {0;1;1;2}, {0.1,0.1;0.9,0.9})

Expected output:

Result
0
2
Example 3: Nearest-neighbor in 3D tetrahedron

Inputs:

points values xi
0 0 0 10 0.2 0.1 0.1
1 0 0 20
0 1 0 30
0 0 1 40

Excel formula:

=NEAREST_ND_INTERP({0,0,0;1,0,0;0,1,0;0,0,1}, {10;20;30;40}, {0.2,0.1,0.1})

Expected output:

10

Example 4: Multiple queries on 2D grid pattern

Inputs:

points values xi
0 0 5 0.4 0.4
2 0 10 1.6 0.4
0 2 15 1.6 1.6
2 2 20

Excel formula:

=NEAREST_ND_INTERP({0,0;2,0;0,2;2,2}, {5;10;15;20}, {0.4,0.4;1.6,0.4;1.6,1.6})

Expected output:

Result
5
10
20

Python Code

Show Code
import numpy as np
from scipy.interpolate import NearestNDInterpolator as scipy_NearestNDInterpolator

def nearest_nd_interp(points, values, xi):
    """
    Nearest neighbor interpolation in N > 1 dimensions.

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

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

    Args:
        points (list[list]): Data point coordinates (n_points, n_dims)
        values (list[list]): Data values (n_points, 1)
        xi (list[list]): Query points (n_new_points, n_dims)

    Returns:
        list[list]: A 2D list of interpolated values, or an error message (str) if invalid.
    """
    try:
        def to2d(x):
            return [[x]] if not isinstance(x, list) else x

        # Normalize inputs to 2D lists
        points = to2d(points)
        values = to2d(values)
        xi = to2d(xi)

        # Validate inputs are lists
        if not isinstance(points, list) or not isinstance(values, list) or not isinstance(xi, list):
            return "Error: Invalid input: points, values, and xi must be 2D lists."

        # Validate all elements are lists
        if not all(isinstance(row, list) for row in points):
            return "Error: Invalid input: points must be a 2D list."
        if not all(isinstance(row, list) for row in values):
            return "Error: Invalid input: values must be a 2D list."
        if not all(isinstance(row, list) for row in xi):
            return "Error: Invalid input: xi must be a 2D list."

        # Validate non-empty
        if len(points) == 0 or len(values) == 0 or len(xi) == 0:
            return "Error: Invalid input: points, values, and xi must be non-empty."

        # Validate consistent dimensions
        if len(points) != len(values):
            return "Error: Invalid input: points and values must have the same number of rows."

        # Validate all rows have same length
        n_dims = len(points[0])
        if n_dims == 0:
            return "Error: Invalid input: points must have at least one column."
        if not all(len(row) == n_dims for row in points):
            return "Error: Invalid input: all rows in points must have the same length."

        n_dims_xi = len(xi[0])
        if n_dims_xi == 0:
            return "Error: Invalid input: xi must have at least one column."
        if not all(len(row) == n_dims_xi for row in xi):
            return "Error: Invalid input: all rows in xi must have the same length."

        if n_dims != n_dims_xi:
            return "Error: Invalid input: points and xi must have the same number of columns."

        # Validate values is single column
        if not all(len(row) == 1 for row in values):
            return "Error: Invalid input: values must have exactly one column."

        # Convert to numpy arrays and validate numeric values
        try:
            points_arr = np.array(points, dtype=float)
            values_arr = np.array(values, dtype=float).flatten()
            xi_arr = np.array(xi, dtype=float)
        except (ValueError, TypeError):
            return "Error: Invalid input: all elements must be numeric."

        # Check for NaN or inf values
        if np.any(np.isnan(points_arr)) or np.any(np.isinf(points_arr)):
            return "Error: Invalid input: points contains non-finite values."
        if np.any(np.isnan(values_arr)) or np.any(np.isinf(values_arr)):
            return "Error: Invalid input: values contains non-finite values."
        if np.any(np.isnan(xi_arr)) or np.any(np.isinf(xi_arr)):
            return "Error: Invalid input: xi contains non-finite values."

        # Perform interpolation
        try:
            interp = scipy_NearestNDInterpolator(points_arr, values_arr)
            result = interp(xi_arr)
        except Exception as exc:
            return f"Error: scipy.interpolate.NearestNDInterpolator error: {exc}"

        # Validate result
        if not isinstance(result, np.ndarray):
            return "Error: scipy.interpolate.NearestNDInterpolator error: unexpected result type."

        # Check for NaN or inf in result
        if np.any(np.isnan(result)) or np.any(np.isinf(result)):
            return "Error: scipy.interpolate.NearestNDInterpolator error: result contains non-finite values."

        # Convert to 2D list
        result_2d = [[float(val)] for val in result]

        return result_2d
    except Exception as e:
        return f"Error: {str(e)}"

Online Calculator

Data point coordinates (n_points, n_dims)
Data values (n_points, 1)
Query points (n_new_points, n_dims)

RBF_INTERPOLATOR

This function performs radial basis function (RBF) interpolation for scattered points in N dimensions, with configurable kernel, smoothing, shape parameter, and polynomial degree. It builds a smooth interpolant and evaluates it at new query points.

The interpolant is of the form

f(\mathbf{x}) = \sum_{i=1}^{n} a_i\,\phi\big(\|\mathbf{x}-\mathbf{y}_i\|\big) + p(\mathbf{x}),

where \phi is the selected radial kernel and p(\mathbf{x}) is an optional polynomial term.

Excel Usage

=RBF_INTERPOLATOR(y, d, xi, smoothing, kernel, epsilon, degree)
  • y (list[list], required): Coordinates of the data points (n_points, n_dims)
  • d (list[list], required): Values of the data points (n_points, 1)
  • xi (list[list], required): Points at which to interpolate (n_new_points, n_dims)
  • smoothing (float, optional, default: 0): Smoothing parameter (non-negative)
  • kernel (str, optional, default: “thin_plate_spline”): Radial basis function kernel
  • epsilon (float, optional, default: 1): Shape parameter for some kernels (positive)
  • degree (int, optional, default: 1): Degree of polynomial term (non-negative)

Returns (list[list]): A 2D list of interpolated values (n_new_points, 1), or str error message if invalid.

Example 1: RBF interpolation on 1D quadratic data

Inputs:

y d xi
0 0 0.5
1 1
2 4

Excel formula:

=RBF_INTERPOLATOR({0;1;2}, {0;1;4}, {0.5})

Expected output:

0.391541

Example 2: 1D RBF with smoothing parameter

Inputs:

y d xi smoothing
0 0 1.5 0.1
1 1
2 4

Excel formula:

=RBF_INTERPOLATOR({0;1;2}, {0;1;4}, {1.5}, 0.1)

Expected output:

2.4184

Example 3: 2D RBF with cubic kernel

Inputs:

y d xi kernel
0 0 0 0.5 0.5 cubic
1 0 1
0 1 1
1 1 2

Excel formula:

=RBF_INTERPOLATOR({0,0;1,0;0,1;1,1}, {0;1;1;2}, {0.5,0.5}, "cubic")

Expected output:

1

Example 4: RBF with gaussian kernel and custom epsilon

Inputs:

y d xi smoothing kernel epsilon degree
0 0 1 0 gaussian 2 0
1 1 2
2 0
3 1

Excel formula:

=RBF_INTERPOLATOR({0;1;2;3}, {0;1;0;1}, {1;2}, 0, "gaussian", 2, 0)

Expected output:

Result
1
0

Python Code

Show Code
import numpy as np
from scipy.interpolate import RBFInterpolator as scipy_RBFInterpolator

def rbf_interpolator(y, d, xi, smoothing=0, kernel='thin_plate_spline', epsilon=1, degree=1):
    """
    Radial basis function interpolation in N dimensions.

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

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

    Args:
        y (list[list]): Coordinates of the data points (n_points, n_dims)
        d (list[list]): Values of the data points (n_points, 1)
        xi (list[list]): Points at which to interpolate (n_new_points, n_dims)
        smoothing (float, optional): Smoothing parameter (non-negative) Default is 0.
        kernel (str, optional): Radial basis function kernel Valid options: Linear, Thin Plate Spline, Cubic, Quintic, Multiquadric, Inverse Multiquadric, Inverse Quadratic, Gaussian. Default is 'thin_plate_spline'.
        epsilon (float, optional): Shape parameter for some kernels (positive) Default is 1.
        degree (int, optional): Degree of polynomial term (non-negative) Default is 1.

    Returns:
        list[list]: A 2D list of interpolated values (n_new_points, 1), or str error message if invalid.
    """
    try:
      def to2d(x):
        return [[x]] if not isinstance(x, list) else x

      # Normalize inputs to 2D lists
      y = to2d(y)
      d = to2d(d)
      xi = to2d(xi)

      # Validate kernel parameter
      valid_kernels = [
        'linear', 'thin_plate_spline', 'cubic', 'quintic',
        'multiquadric', 'inverse_multiquadric', 'inverse_quadratic', 'gaussian'
      ]
      if kernel not in valid_kernels:
        return f"Error: Invalid kernel: '{kernel}'. Must be one of {valid_kernels}."

      # Validate smoothing parameter
      if not isinstance(smoothing, (int, float)):
        return "Error: Invalid smoothing: must be a number."
      if smoothing < 0:
        return "Error: Invalid smoothing: must be non-negative."

      # Validate epsilon parameter
      if not isinstance(epsilon, (int, float)):
        return "Error: Invalid epsilon: must be a number."
      if epsilon <= 0:
        return "Error: Invalid epsilon: must be positive."

      # Validate degree parameter
      if not isinstance(degree, int):
        return "Error: Invalid degree: must be an integer."
      if degree < 0:
        return "Error: Invalid degree: must be non-negative."

      try:
        # Convert to numpy arrays
        y_arr = np.array(y, dtype=float)
        d_arr = np.array(d, dtype=float).flatten()
        xi_arr = np.array(xi, dtype=float)

        # Validate shapes
        if y_arr.ndim != 2:
          return "Error: Invalid y: must be a 2D list."
        if xi_arr.ndim != 2:
          return "Error: Invalid xi: must be a 2D list."
        if y_arr.shape[0] != len(d_arr):
          return "Error: Invalid d: number of values must match number of points in y."
        if y_arr.shape[1] != xi_arr.shape[1]:
          return "Error: Invalid xi: dimensions must match y."

        # Create interpolator
        interp = scipy_RBFInterpolator(
          y_arr, d_arr,
          smoothing=smoothing,
          kernel=kernel,
          epsilon=epsilon,
          degree=degree
        )

        # Perform interpolation
        result = interp(xi_arr)

        # Return as 2D list (column vector)
        return [[float(val)] for val in result]

      except ValueError as e:
        return f"Error: Invalid input: {str(e)}"
      except Exception as e:
        return f"Error: {str(e)}"
    except Exception as e:
      return f"Error: {str(e)}"

Online Calculator

Coordinates of the data points (n_points, n_dims)
Values of the data points (n_points, 1)
Points at which to interpolate (n_new_points, n_dims)
Smoothing parameter (non-negative)
Radial basis function kernel
Shape parameter for some kernels (positive)
Degree of polynomial term (non-negative)

SMOOTH_BIVAR_SPL

This function wraps SciPy’s smoothing bivariate spline class to fit a smooth surface to scattered points in two dimensions. It can reduce noise while preserving broad surface structure.

The fitted spline balances data fidelity and smoothness by minimizing a weighted residual objective subject to a smoothing constraint, then evaluates \hat{z}(x, y) at requested query pairs.

Excel Usage

=SMOOTH_BIVAR_SPL(x, y, z, xi, yi, w, kx, ky, s, eps)
  • x (list[list], required): x-coordinates of observed points.
  • y (list[list], required): y-coordinates of observed points.
  • z (list[list], required): Observed values at (x, y) points.
  • xi (list[list], required): x-coordinates of query points.
  • yi (list[list], required): y-coordinates of query points.
  • w (list[list], optional, default: null): Positive observation weights.
  • kx (int, optional, default: 3): Spline degree in x-direction.
  • ky (int, optional, default: 3): Spline degree in y-direction.
  • s (float, optional, default: null): Smoothing factor.
  • eps (float, optional, default: 1e-16): Rank threshold for linear system solve.

Returns (list[list]): Evaluated spline values as a 2D list (column vector), or an error message string.

Example 1: Fit smooth spline to planar surface

Inputs:

x y z xi yi
0 0 0 0.5 0.5
1 0 1 1.5 1.5
2 0 2 2.5 2.5
3 0 3
0 1 1
1 1 2
2 1 3
3 1 4
0 2 2
1 2 3
2 2 4
3 2 5
0 3 3
1 3 4
2 3 5
3 3 6

Excel formula:

=SMOOTH_BIVAR_SPL({0;1;2;3;0;1;2;3;0;1;2;3;0;1;2;3}, {0;0;0;0;1;1;1;1;2;2;2;2;3;3;3;3}, {0;1;2;3;1;2;3;4;2;3;4;5;3;4;5;6}, {0.5;1.5;2.5}, {0.5;1.5;2.5})

Expected output:

Result
1
3
5
Example 2: Fit with linear spline degrees

Inputs:

x y z xi yi kx ky s
0 0 0 0.25 0.5 1 1 0
1 0 1 1.25 0.5
2 0 2
0 1 1
1 1 2
2 1 3

Excel formula:

=SMOOTH_BIVAR_SPL({0;1;2;0;1;2}, {0;0;0;1;1;1}, {0;1;2;1;2;3}, {0.25;1.25}, {0.5;0.5}, 1, 1, 0)

Expected output:

Result
0.75
1.75
Example 3: Weighted smoothing fit

Inputs:

x y z xi yi w s
0 0 0 1 1 1 0.5
1 0 1 2 2 1
2 0 2 1
3 0 3 1
0 1 1 1
1 1 2 2
2 1 3 2
3 1 4 1
0 2 2 1
1 2 3 2
2 2 4 2
3 2 5 1
0 3 3 1
1 3 4 1
2 3 5 1
3 3 6 1

Excel formula:

=SMOOTH_BIVAR_SPL({0;1;2;3;0;1;2;3;0;1;2;3;0;1;2;3}, {0;0;0;0;1;1;1;1;2;2;2;2;3;3;3;3}, {0;1;2;3;1;2;3;4;2;3;4;5;3;4;5;6}, {1;2}, {1;2}, {1;1;1;1;1;2;2;1;1;2;2;1;1;1;1;1}, 0.5)

Expected output:

Result
2
4
Example 4: Fit with custom eps threshold

Inputs:

x y z xi yi eps
0 0 0 0.75 1.25 1e-12
1 0 1 2.25 2.75
2 0 2
3 0 3
0 1 1
1 1 2
2 1 3
3 1 4
0 2 2
1 2 3
2 2 4
3 2 5
0 3 3
1 3 4
2 3 5
3 3 6

Excel formula:

=SMOOTH_BIVAR_SPL({0;1;2;3;0;1;2;3;0;1;2;3;0;1;2;3}, {0;0;0;0;1;1;1;1;2;2;2;2;3;3;3;3}, {0;1;2;3;1;2;3;4;2;3;4;5;3;4;5;6}, {0.75;2.25}, {1.25;2.75}, 1e-12)

Expected output:

Result
2
5

Python Code

Show Code
import math
import numpy as np
from scipy.interpolate import SmoothBivariateSpline as scipy_SmoothBivariateSpline

def smooth_bivar_spl(x, y, z, xi, yi, w=None, kx=3, ky=3, s=None, eps=1e-16):
    """
    Smooth bivariate spline fit for scattered 2D observations.

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

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

    Args:
        x (list[list]): x-coordinates of observed points.
        y (list[list]): y-coordinates of observed points.
        z (list[list]): Observed values at (x, y) points.
        xi (list[list]): x-coordinates of query points.
        yi (list[list]): y-coordinates of query points.
        w (list[list], optional): Positive observation weights. Default is None.
        kx (int, optional): Spline degree in x-direction. Default is 3.
        ky (int, optional): Spline degree in y-direction. Default is 3.
        s (float, optional): Smoothing factor. Default is None.
        eps (float, optional): Rank threshold for linear system solve. Default is 1e-16.

    Returns:
        list[list]: Evaluated spline values as a 2D list (column vector), or an error message string.
    """
    try:
        def to2d(x):
            return [[x]] if not isinstance(x, list) else x

        def flatten_2d(arr):
            out = []
            for row in arr:
                if not isinstance(row, list):
                    return None
                for val in row:
                    out.append(val)
            return out

        def as_int(name, value):
            if not isinstance(value, int):
                return None, f"Error: Invalid input: {name} must be an integer."
            return value, None

        x = to2d(x)
        y = to2d(y)
        z = to2d(z)
        xi = to2d(xi)
        yi = to2d(yi)

        x_flat = flatten_2d(x)
        y_flat = flatten_2d(y)
        z_flat = flatten_2d(z)
        xi_flat = flatten_2d(xi)
        yi_flat = flatten_2d(yi)

        if x_flat is None or y_flat is None or z_flat is None or xi_flat is None or yi_flat is None:
            return "Error: Invalid input: all array inputs must be 2D lists."

        if len(x_flat) == 0 or len(y_flat) == 0 or len(z_flat) == 0:
            return "Error: Invalid input: x, y, and z must not be empty."
        if len(xi_flat) == 0 or len(yi_flat) == 0:
            return "Error: Invalid input: xi and yi must not be empty."

        if len(x_flat) != len(y_flat) or len(x_flat) != len(z_flat):
            return "Error: Invalid input: x, y, and z must have the same length."
        if len(xi_flat) != len(yi_flat):
            return "Error: Invalid input: xi and yi must have the same length."

        for i, val in enumerate(x_flat):
            if not isinstance(val, (int, float)) or math.isnan(val) or math.isinf(val):
                return f"Error: Invalid input: x element {i} must be finite numeric."
        for i, val in enumerate(y_flat):
            if not isinstance(val, (int, float)) or math.isnan(val) or math.isinf(val):
                return f"Error: Invalid input: y element {i} must be finite numeric."
        for i, val in enumerate(z_flat):
            if not isinstance(val, (int, float)) or math.isnan(val) or math.isinf(val):
                return f"Error: Invalid input: z element {i} must be finite numeric."
        for i, val in enumerate(xi_flat):
            if not isinstance(val, (int, float)) or math.isnan(val) or math.isinf(val):
                return f"Error: Invalid input: xi element {i} must be finite numeric."
        for i, val in enumerate(yi_flat):
            if not isinstance(val, (int, float)) or math.isnan(val) or math.isinf(val):
                return f"Error: Invalid input: yi element {i} must be finite numeric."

        kx_value, kx_error = as_int("kx", kx)
        if kx_error:
            return kx_error
        ky_value, ky_error = as_int("ky", ky)
        if ky_error:
            return ky_error

        if kx_value < 1 or ky_value < 1:
            return "Error: Invalid input: kx and ky must be at least 1."

        min_points = (kx_value + 1) * (ky_value + 1)
        if len(x_flat) < min_points:
            return f"Error: Invalid input: need at least {min_points} points for kx={kx_value}, ky={ky_value}."

        if s is not None:
            if not isinstance(s, (int, float)):
                return "Error: Invalid input: s must be numeric when provided."
            if s < 0:
                return "Error: Invalid input: s must be non-negative."

        if not isinstance(eps, (int, float)):
            return "Error: Invalid input: eps must be numeric."
        if eps <= 0 or eps >= 1:
            return "Error: Invalid input: eps must be in the interval (0, 1)."

        w_arr = None
        if w is not None:
            w = to2d(w)
            w_flat = flatten_2d(w)
            if w_flat is None:
                return "Error: Invalid input: w must be a 2D list."
            if len(w_flat) != len(x_flat):
                return "Error: Invalid input: w must match length of x, y, and z."
            for i, val in enumerate(w_flat):
                if not isinstance(val, (int, float)) or math.isnan(val) or math.isinf(val):
                    return f"Error: Invalid input: w element {i} must be finite numeric."
                if val <= 0:
                    return f"Error: Invalid input: w element {i} must be positive."
            w_arr = np.array(w_flat, dtype=float)

        x_arr = np.array(x_flat, dtype=float)
        y_arr = np.array(y_flat, dtype=float)
        z_arr = np.array(z_flat, dtype=float)
        xi_arr = np.array(xi_flat, dtype=float)
        yi_arr = np.array(yi_flat, dtype=float)

        spline = scipy_SmoothBivariateSpline(
            x_arr,
            y_arr,
            z_arr,
            w=w_arr,
            kx=kx_value,
            ky=ky_value,
            s=s,
            eps=float(eps)
        )

        result = spline.ev(xi_arr, yi_arr)
        result_arr = np.asarray(result, dtype=float).reshape(-1)
        return [[float(v)] for v in result_arr]
    except Exception as e:
        return f"Error: {str(e)}"

Online Calculator

x-coordinates of observed points.
y-coordinates of observed points.
Observed values at (x, y) points.
x-coordinates of query points.
y-coordinates of query points.
Positive observation weights.
Spline degree in x-direction.
Spline degree in y-direction.
Smoothing factor.
Rank threshold for linear system solve.