interp_scattered — MATH interp_poly op

• Data kinds: points × signal × points → table

• Call: import fullseye as fs; fs.ledger.interp_scattered(points, values, query, method='linear', fill_value=nan, rescale=False, neighbors=None) (to call the implementation directly, import mathops; mathops.interp_scattered(points, values, query, method='linear', fill_value=nan, rescale=False, neighbors=None); from the registry, opsmath.get("interp_scattered"))

Usage

Values at *query* from scattered samples — sensor nets, boreholes, weather.

interp_linear and interp_cubic need samples on a sorted 1-D axis. A great deal of measurement does not arrive that way: temperature sensors bolted wherever a rack allowed, boreholes drilled where access permitted, weather stations placed by history. This is the N-D scattered entry point (scipy.interpolate), and it returns how much of the answer was not interpolation at all.

*method*:

"nearest"

the value of the closest sample. Defined everywhere, and never

overshoots, but it is a staircase: on a smooth field the step itself

becomes a false feature. Measured on a smooth 3-D field sampled at 0.60,

the nearest-neighbour reconstruction leaves a residual of 0.975 units

where the sensor noise is only 0.15 — 6.5 times the noise, and none of

it is noise.

"linear"

barycentric interpolation on a Delaunay triangulation. Never exceeds the

surrounding samples, and is undefined outside their convex hull.

"rbf"

a thin-plate radial basis function through every sample. Smooth and

defined everywhere, but it overshoots its own nodes: measured on the

same field it returns peaks 1.372 times the sampled height, which is a

37 % over-statement of a hot spot that no interpolation of the data can

justify.

The point of the outside return value. Sensors sit inside a room, a site, a country; the corners are always outside their hull. Ask a linear interpolator there and it returns fill_value, or, if a caller quietly falls back to nearest, it returns a different method's answer under the first method's name. Measured on a 12 x 8.4 x 3.0 m room sampled at 1.20 m spacing, 71.2 % of the evaluation grid lay outside the hull. A number that large has to be visible, so it is returned rather than logged.

Parameters

points : (n, d) array_like

Sample coordinates. 1-D input is accepted and treated as (n, 1).

values : (n,) array_like

query : (m, d) or (..., d) array_like

Where to evaluate. The leading shape is preserved in the result.

method : {"linear", "nearest", "rbf"}

fill_value : float

Returned outside the convex hull for "linear". "nearest" and

"rbf" are defined everywhere and ignore it.

rescale : bool

Normalise each axis before triangulating. Needed when the axes have very

different units (metres against millimetres); ignored by "rbf".

neighbors : int or None

"rbf" only: solve against the *k* nearest samples instead of all of

them. The global solve is O(n^3); measured on 5000 query points in 3-D,

it costs 0.55 / 1.76 / 7.53 s at 1400 / 4000 / 8000 samples, while

neighbors=48 costs 0.38 / 0.55 / 0.81 s. Below a few thousand

samples the global solve is fine and exact — the knob earns its place

above that. None keeps the exact global solution.

Returns

dict

value (query shape), outside (bool mask, query shape, of query

points beyond the convex hull of the samples), outside_fraction,

method, n_points.

Fail-closed: fewer samples than d + 1 cannot define a simplex, and raises ValueError rather than returning a field made of fill_value.

See also

interp_linear : the sorted 1-D case, which is cheaper and needs no hull.

Family-wide input contract (fail-closed)

Every mathops op validates its input before computing (nothing slips through silently):

• complex input raises ValueError — coercing to float64 silently discards the imaginary part (numpy only emits a ComplexWarning and returns a plausible-looking wrong real number). State .real/.imag/abs() explicitly, or use complexops, which handles complex data.

• masked arrays with masked elements raise ValueError — the implicit conversion that peels off the mask and uses the raw values underneath is refused. Say explicitly whether to fill or to drop.

• NaN/Inf raises ValueError on every input (refused with the count stated — it propagates through the whole result).

• Shapes are strict: 1-D and 2-D are never implicitly promoted or broadcast (a matrix in a vector slot, or a vector in a matrix slot, raises ValueError; reshape explicitly).

• Size cap: ops that take a matrix, and the stat_histogram bins, raise ValueError beyond mathops.MAX_ELEMENTS (2^26 ≈ 67 million elements).

Detailed usage guide

• math_metrology family guide

References (sample data, literature)

• Sample-data catalog (download URLs / licences) — 2-D uses skimage.data (BSD/public domain) plus synthetic images; 3-D lists download URLs for real data sources (Stanford, PDS, …).

• Operator provenance and references — the sources of the research/methods this op family came from.

• The canonical algorithm (author, year) and its uses are named in the family usage guide above.

Runnable examples (verified samples that actually call this op)

• poc_datacenter_thermal_field — py -3.11 examples/poc_datacenter_thermal_field.py

• poc_multibeam_bathymetry — py -3.11 examples/poc_multibeam_bathymetry.py

• poc_stockpile_volume — py -3.11 examples/poc_stockpile_volume.py

Ops the type connects to (they accept table as input)

dynsys_poincare_section · dwt_inverse · chebyshev_eval_nd

Same category (interp_poly)

interp_linear · interp_cubic · poly_fit · poly_eval · poly_roots


*Provenance: mathops.py — MATH operator registry. This per-op note is generated by tools/opdocs.py md (do not hand-edit).*

© 2026 Kazufumi Furuse — Fullseye operator documentation. Licensed under Apache-2.0.