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
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.