LINEAR_ND_INTERP
Overview
The LINEAR_ND_INTERP function performs piecewise linear interpolation for scattered data in N dimensions (where N > 1). Given a set of known data points with their corresponding values, it estimates values at new, unsampled locations using linear interpolation within a triangulated mesh. This is particularly useful for reconstructing surfaces or multidimensional fields from irregularly spaced measurements.
This implementation uses SciPy’s LinearNDInterpolator class from the scipy.interpolate module. The interpolator constructs a Delaunay triangulation of the input points using Qhull, a computational geometry library. This triangulation partitions the space into simplices (triangles in 2D, tetrahedra in 3D, and higher-dimensional analogues).
For each query point, the algorithm first locates which simplex contains the point. It then computes the interpolated value using barycentric interpolation. Given a point \mathbf{r} inside a simplex with vertices \mathbf{r}_1, \mathbf{r}_2, \ldots, \mathbf{r}_{n+1} and corresponding known values f(\mathbf{r}_1), f(\mathbf{r}_2), \ldots, f(\mathbf{r}_{n+1}), the interpolated value is:
f(\mathbf{r}) \approx \sum_{i=1}^{n+1} \lambda_i \, f(\mathbf{r}_i)
where \lambda_i are the barycentric coordinates of \mathbf{r} with respect to the simplex, satisfying \sum \lambda_i = 1 and 0 \le \lambda_i \le 1 for points inside the simplex.
Points that fall outside the convex hull of the input data cannot be interpolated using this method. The function returns a user-specified fill_value (defaulting to 0) for such points. For alternative approaches that handle extrapolation or use different interpolation schemes, see NearestNDInterpolator for nearest-neighbor interpolation or CloughTocher2DInterpolator for smooth cubic interpolation in 2D.
This example function is provided as-is without any representation of accuracy.
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.
Examples
Example 1: Demo case 1
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:
| Result |
|---|
| 1 |
Example 2: Demo case 2
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: Demo case 3
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:
| Result |
|---|
| 1.5 |
Example 4: Demo case 4
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
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.
"""
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 "Invalid input: points must be a 2D list."
if not isinstance(values, list) or not all(isinstance(row, list) for row in values):
return "Invalid input: values must be a 2D list."
if not isinstance(xi, list) or not all(isinstance(row, list) for row in xi):
return "Invalid input: xi must be a 2D list."
# Validate fill_value
if not isinstance(fill_value, (int, float)):
return "Invalid input: fill_value must be a number."
fill_value = float(fill_value)
# Check dimensions
if len(points) == 0:
return "Invalid input: points must not be empty."
if len(values) == 0:
return "Invalid input: values must not be empty."
if len(xi) == 0:
return "Invalid input: xi must not be empty."
if len(points) != len(values):
return "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 "Invalid input: points must have at least 2 dimensions (N > 1)."
for i, row in enumerate(points):
if len(row) != n_dims:
return f"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"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"Invalid input: xi must have same dimensions as points ({n_dims})."
for i, row in enumerate(xi):
if len(row) != xi_dims:
return f"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"Invalid input: points[{i}][{j}] must be numeric."
if math.isinf(val) or math.isnan(val):
return f"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"Invalid input: values[{i}][0] must be numeric."
if math.isinf(val) or math.isnan(val):
return f"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"Invalid input: xi[{i}][{j}] must be numeric."
if math.isinf(val) or math.isnan(val):
return f"Invalid input: xi[{i}][{j}] must be finite."
except Exception as e:
return f"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"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"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 converting result to 2D list: {e}"
return result_2d