scijit.interpolate.SmoothBivariateSpline

scijit.interpolate.SmoothBivariateSpline(x, y, z, w=array([], dtype=float64), bbox=None, kx=3, ky=3, s=None, eps=1e-16)

Build a bivariate smoothing spline over scattered data.

Parameters:
x1-D float64 ndarray, length m

Abscissae of the data points; no grid structure required.

y1-D float64 ndarray, length m

Ordinates.

z1-D float64 ndarray, length m

Values at those points.

w1-D array_like of float, optional

Weights, length m, each >= 0. None and a ZERO-LENGTH array both mean unit weights.

bbox(4,) array_like of float, optional

The rectangle the fit is made over, [xb, xe, yb, ye]. None, in place of the four or in one slot, means min(x), max(x), min(y), max(y), and is the default. The literal default [None, None, None, None] is accepted, from Python and from inside @njit.

kxint, optional

Degree in x, 1 <= kx <= 5. Default 3.

kyint, optional

Degree in y, 1 <= ky <= 5. Default 3.

sfloat, optional

Smoothing factor. None, the default, means s = m. A negative or NaN s raises ValueError. s = 0 requests interpolation, which scattered data often cannot support – expect a positive ier then.

epsfloat, optional

Rank-determination threshold, strictly between 0 and 1. Default 1e-16. It changes the coefficients, so it is not cosmetic.

Attributes:
tx, ty1-D float64 ndarray

Knot vectors. With c these are the tck.

c1-D float64 ndarray

Coefficients in FITPACK’s flat bivariate layout.

kx, kyint

The two degrees.

fpfloat

Weighted sum of squared residuals, also returned by get_residual().

ierint

FITPACK status: 0 smoothing achieved, -1 interpolating surface, -2 least-squares polynomial, positive a failure. Anything outside {0, -1, -2} is warned about and NOT raised on, so the object is still returned and usable.

Methods

eval_grid(x, y, dx, dy)

Grid evaluation, the equivalent of spl(x, y).

ev(xi, yi, dx, dy), ev_one(x, y, dx, dy),

partial_derivative(x, y, dx, dy), integral(xa, xb, ya, yb),

get_knots(), get_coeffs(), get_residual()

Returns:
spl_SmoothBivariateSpline

A jitclass instance carrying the attributes and methods below.

Raises:
ValueError

x, y, z of differing lengths; a w of the wrong length or holding a negative entry; eps outside (0, 1); m < (kx+1)*(ky+1); a bbox that is not length 4; and a supplied s < 0.

Warns:
UserWarning

ier outside {0, -1, -2}. The fit is returned and the matching _surfit_messages text is warned. This is the one class here that warns rather than raises: RectBivariateSpline and both sphere classes raise. The warning is issued through a numba.objmode block, which runs its body in the interpreter, so warnings.catch_warnings and -W see it from compiled and uncompiled callers alike.

See also

scipy.interpolate.SmoothBivariateSpline

The scipy class this mirrors.

Notes

  • spl(x, y) runs eval_grid, scipy’s grid=True default, for every pair of scalars and arrays, so two scalars give a (1, 1) grid. ev(xi, yi) is scipy’s grid=False and ev_one(x, y) the single-point scalar spelling, both reached by name.

  • .partial_derivative(dx, dy) returns a new object in scipy; here it evaluates on a grid directly.

  • A complex z raises TypeError. scipy 1.18 accepts it, emits a ComplexWarning and discards the imaginary part, returning a float64 spline.

  • ev_one returns a SCALAR where scipy’s spl.ev(0.5, 0.5) returns a length-1 array.

  • A NaN in a bbox slot is a literal edge on both sides and both return an all-NaN spline, but the two knot vectors have different LENGTHS: 9 and 9 here against scipy’s 8 and 8 on a 60-point fit at s = 0.0, 0.5 and 5.0. FITPACK reports ier = 1 for it and scipy’s C translation reports 5. A finite bbox agrees exactly at all three.

Defaults work in both worlds. SmoothBivariateSpline is a plain @njit factory, not the jitclass itself, so SmoothBivariateSpline(x, y, z) compiles and runs inside @njit as well as from Python. The class it returns, _SmoothBivariateSpline, takes every argument explicitly, because a jitclass constructor’s defaults are Python-only.

Accuracy against scipy.interpolate.SmoothBivariateSpline on scipy 1.18.0, 200 scattered points, kx = ky = 3. INTERPOLATING fits (s = 0) are exactly 0.0 in knots, coefficients, fp and grid values.

A SMOOTHING fit is a different matter. scipy 1.18 reaches FITPACK through a C translation whose smoothing iteration rounds differently, so on data where the knot search is sensitive the two libraries place knots in DIFFERENT POSITIONS. The surface gap is then set by the data rather than by floating point: measured up to 1.8e-02 on a 13x14 noisy grid at s = 0.01, with both sides reporting ier = 0 and both inside FITPACK’s own |fp-s|/s <= 1e-3 test. Two valid smoothing splines, not one right and one wrong. Where the knot search agrees the surfaces agree. Compare knot counts before comparing values.

prange-safe: yes.

Examples

>>> import numpy as np
>>> from numba import njit
>>> from scijit.interpolate import SmoothBivariateSpline
>>> rng = np.random.default_rng(0)
>>> x = rng.uniform(0, 1, 200)
>>> y = rng.uniform(0, 1, 200)
>>> z = np.sin(3 * x) * np.cos(2 * y)
>>> spl = SmoothBivariateSpline(x, y, z, s=0.05)     # fit once
>>> spl.ier
-2
>>> float(np.round(spl(0.5, 0.5)[0, 0], 6))     # spl(x, y) is the grid form; [0, 0] takes the point
0.525083

Inside compiled code, the largest value over a coarse query grid:

>>> @njit
... def gridmax(spl):
...     return np.max(spl(np.linspace(0.0, 1.0, 5), np.linspace(0.0, 1.0, 5)))
>>> float(np.round(gridmax(spl), 6))
0.987065