|
""" |
|
Convenience interface to N-D interpolation |
|
|
|
.. versionadded:: 0.9 |
|
|
|
""" |
|
import numpy as np |
|
from ._interpnd import (LinearNDInterpolator, NDInterpolatorBase, |
|
CloughTocher2DInterpolator, _ndim_coords_from_arrays) |
|
from scipy.spatial import cKDTree |
|
|
|
__all__ = ['griddata', 'NearestNDInterpolator', 'LinearNDInterpolator', |
|
'CloughTocher2DInterpolator'] |
|
|
|
|
|
|
|
|
|
|
|
|
|
class NearestNDInterpolator(NDInterpolatorBase): |
|
"""NearestNDInterpolator(x, y). |
|
|
|
Nearest-neighbor interpolator in N > 1 dimensions. |
|
|
|
.. versionadded:: 0.9 |
|
|
|
Methods |
|
------- |
|
__call__ |
|
|
|
Parameters |
|
---------- |
|
x : (npoints, ndims) 2-D ndarray of floats |
|
Data point coordinates. |
|
y : (npoints, ) 1-D ndarray of float or complex |
|
Data values. |
|
rescale : boolean, optional |
|
Rescale points to unit cube before performing interpolation. |
|
This is useful if some of the input dimensions have |
|
incommensurable units and differ by many orders of magnitude. |
|
|
|
.. versionadded:: 0.14.0 |
|
tree_options : dict, optional |
|
Options passed to the underlying ``cKDTree``. |
|
|
|
.. versionadded:: 0.17.0 |
|
|
|
See Also |
|
-------- |
|
griddata : |
|
Interpolate unstructured D-D data. |
|
LinearNDInterpolator : |
|
Piecewise linear interpolator in N dimensions. |
|
CloughTocher2DInterpolator : |
|
Piecewise cubic, C1 smooth, curvature-minimizing interpolator in 2D. |
|
interpn : Interpolation on a regular grid or rectilinear grid. |
|
RegularGridInterpolator : Interpolator on a regular or rectilinear grid |
|
in arbitrary dimensions (`interpn` wraps this |
|
class). |
|
|
|
Notes |
|
----- |
|
Uses ``scipy.spatial.cKDTree`` |
|
|
|
.. note:: For data on a regular grid use `interpn` instead. |
|
|
|
Examples |
|
-------- |
|
We can interpolate values on a 2D plane: |
|
|
|
>>> from scipy.interpolate import NearestNDInterpolator |
|
>>> import numpy as np |
|
>>> import matplotlib.pyplot as plt |
|
>>> rng = np.random.default_rng() |
|
>>> x = rng.random(10) - 0.5 |
|
>>> y = rng.random(10) - 0.5 |
|
>>> z = np.hypot(x, y) |
|
>>> X = np.linspace(min(x), max(x)) |
|
>>> Y = np.linspace(min(y), max(y)) |
|
>>> X, Y = np.meshgrid(X, Y) # 2D grid for interpolation |
|
>>> interp = NearestNDInterpolator(list(zip(x, y)), z) |
|
>>> Z = interp(X, Y) |
|
>>> plt.pcolormesh(X, Y, Z, shading='auto') |
|
>>> plt.plot(x, y, "ok", label="input point") |
|
>>> plt.legend() |
|
>>> plt.colorbar() |
|
>>> plt.axis("equal") |
|
>>> plt.show() |
|
|
|
""" |
|
|
|
def __init__(self, x, y, rescale=False, tree_options=None): |
|
NDInterpolatorBase.__init__(self, x, y, rescale=rescale, |
|
need_contiguous=False, |
|
need_values=False) |
|
if tree_options is None: |
|
tree_options = dict() |
|
self.tree = cKDTree(self.points, **tree_options) |
|
self.values = np.asarray(y) |
|
|
|
def __call__(self, *args, **query_options): |
|
""" |
|
Evaluate interpolator at given points. |
|
|
|
Parameters |
|
---------- |
|
x1, x2, ... xn : array-like of float |
|
Points where to interpolate data at. |
|
x1, x2, ... xn can be array-like of float with broadcastable shape. |
|
or x1 can be array-like of float with shape ``(..., ndim)`` |
|
**query_options |
|
This allows ``eps``, ``p``, ``distance_upper_bound``, and ``workers`` |
|
being passed to the cKDTree's query function to be explicitly set. |
|
See `scipy.spatial.cKDTree.query` for an overview of the different options. |
|
|
|
.. versionadded:: 1.12.0 |
|
|
|
""" |
|
|
|
|
|
|
|
xi = _ndim_coords_from_arrays(args, ndim=self.points.shape[1]) |
|
xi = self._check_call_shape(xi) |
|
xi = self._scale_x(xi) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
xi_flat = xi.reshape(-1, xi.shape[-1]) |
|
original_shape = xi.shape |
|
flattened_shape = xi_flat.shape |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
dist, i = self.tree.query(xi_flat, **query_options) |
|
valid_mask = np.isfinite(dist) |
|
|
|
|
|
if self.values.ndim > 1: |
|
interp_shape = flattened_shape[:-1] + self.values.shape[1:] |
|
else: |
|
interp_shape = flattened_shape[:-1] |
|
|
|
if np.issubdtype(self.values.dtype, np.complexfloating): |
|
interp_values = np.full(interp_shape, np.nan, dtype=self.values.dtype) |
|
else: |
|
interp_values = np.full(interp_shape, np.nan) |
|
|
|
interp_values[valid_mask] = self.values[i[valid_mask], ...] |
|
|
|
if self.values.ndim > 1: |
|
new_shape = original_shape[:-1] + self.values.shape[1:] |
|
else: |
|
new_shape = original_shape[:-1] |
|
interp_values = interp_values.reshape(new_shape) |
|
|
|
return interp_values |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def griddata(points, values, xi, method='linear', fill_value=np.nan, |
|
rescale=False): |
|
""" |
|
Interpolate unstructured D-D data. |
|
|
|
Parameters |
|
---------- |
|
points : 2-D ndarray of floats with shape (n, D), or length D tuple of 1-D ndarrays with shape (n,). |
|
Data point coordinates. |
|
values : ndarray of float or complex, shape (n,) |
|
Data values. |
|
xi : 2-D ndarray of floats with shape (m, D), or length D tuple of ndarrays broadcastable to the same shape. |
|
Points at which to interpolate data. |
|
method : {'linear', 'nearest', 'cubic'}, optional |
|
Method of interpolation. One of |
|
|
|
``nearest`` |
|
return the value at the data point closest to |
|
the point of interpolation. See `NearestNDInterpolator` for |
|
more details. |
|
|
|
``linear`` |
|
tessellate the input point set to N-D |
|
simplices, and interpolate linearly on each simplex. See |
|
`LinearNDInterpolator` for more details. |
|
|
|
``cubic`` (1-D) |
|
return the value determined from a cubic |
|
spline. |
|
|
|
``cubic`` (2-D) |
|
return the value determined from a |
|
piecewise cubic, continuously differentiable (C1), and |
|
approximately curvature-minimizing polynomial surface. See |
|
`CloughTocher2DInterpolator` for more details. |
|
fill_value : float, optional |
|
Value used to fill in for requested points outside of the |
|
convex hull of the input points. If not provided, then the |
|
default is ``nan``. This option has no effect for the |
|
'nearest' method. |
|
rescale : bool, optional |
|
Rescale points to unit cube before performing interpolation. |
|
This is useful if some of the input dimensions have |
|
incommensurable units and differ by many orders of magnitude. |
|
|
|
.. versionadded:: 0.14.0 |
|
|
|
Returns |
|
------- |
|
ndarray |
|
Array of interpolated values. |
|
|
|
See Also |
|
-------- |
|
LinearNDInterpolator : |
|
Piecewise linear interpolator in N dimensions. |
|
NearestNDInterpolator : |
|
Nearest-neighbor interpolator in N dimensions. |
|
CloughTocher2DInterpolator : |
|
Piecewise cubic, C1 smooth, curvature-minimizing interpolator in 2D. |
|
interpn : Interpolation on a regular grid or rectilinear grid. |
|
RegularGridInterpolator : Interpolator on a regular or rectilinear grid |
|
in arbitrary dimensions (`interpn` wraps this |
|
class). |
|
|
|
Notes |
|
----- |
|
|
|
.. versionadded:: 0.9 |
|
|
|
.. note:: For data on a regular grid use `interpn` instead. |
|
|
|
Examples |
|
-------- |
|
|
|
Suppose we want to interpolate the 2-D function |
|
|
|
>>> import numpy as np |
|
>>> def func(x, y): |
|
... return x*(1-x)*np.cos(4*np.pi*x) * np.sin(4*np.pi*y**2)**2 |
|
|
|
on a grid in [0, 1]x[0, 1] |
|
|
|
>>> grid_x, grid_y = np.mgrid[0:1:100j, 0:1:200j] |
|
|
|
but we only know its values at 1000 data points: |
|
|
|
>>> rng = np.random.default_rng() |
|
>>> points = rng.random((1000, 2)) |
|
>>> values = func(points[:,0], points[:,1]) |
|
|
|
This can be done with `griddata` -- below we try out all of the |
|
interpolation methods: |
|
|
|
>>> from scipy.interpolate import griddata |
|
>>> grid_z0 = griddata(points, values, (grid_x, grid_y), method='nearest') |
|
>>> grid_z1 = griddata(points, values, (grid_x, grid_y), method='linear') |
|
>>> grid_z2 = griddata(points, values, (grid_x, grid_y), method='cubic') |
|
|
|
One can see that the exact result is reproduced by all of the |
|
methods to some degree, but for this smooth function the piecewise |
|
cubic interpolant gives the best results: |
|
|
|
>>> import matplotlib.pyplot as plt |
|
>>> plt.subplot(221) |
|
>>> plt.imshow(func(grid_x, grid_y).T, extent=(0,1,0,1), origin='lower') |
|
>>> plt.plot(points[:,0], points[:,1], 'k.', ms=1) |
|
>>> plt.title('Original') |
|
>>> plt.subplot(222) |
|
>>> plt.imshow(grid_z0.T, extent=(0,1,0,1), origin='lower') |
|
>>> plt.title('Nearest') |
|
>>> plt.subplot(223) |
|
>>> plt.imshow(grid_z1.T, extent=(0,1,0,1), origin='lower') |
|
>>> plt.title('Linear') |
|
>>> plt.subplot(224) |
|
>>> plt.imshow(grid_z2.T, extent=(0,1,0,1), origin='lower') |
|
>>> plt.title('Cubic') |
|
>>> plt.gcf().set_size_inches(6, 6) |
|
>>> plt.show() |
|
|
|
""" |
|
|
|
points = _ndim_coords_from_arrays(points) |
|
|
|
if points.ndim < 2: |
|
ndim = points.ndim |
|
else: |
|
ndim = points.shape[-1] |
|
|
|
if ndim == 1 and method in ('nearest', 'linear', 'cubic'): |
|
from ._interpolate import interp1d |
|
points = points.ravel() |
|
if isinstance(xi, tuple): |
|
if len(xi) != 1: |
|
raise ValueError("invalid number of dimensions in xi") |
|
xi, = xi |
|
|
|
idx = np.argsort(points) |
|
points = points[idx] |
|
values = values[idx] |
|
if method == 'nearest': |
|
fill_value = 'extrapolate' |
|
ip = interp1d(points, values, kind=method, axis=0, bounds_error=False, |
|
fill_value=fill_value) |
|
return ip(xi) |
|
elif method == 'nearest': |
|
ip = NearestNDInterpolator(points, values, rescale=rescale) |
|
return ip(xi) |
|
elif method == 'linear': |
|
ip = LinearNDInterpolator(points, values, fill_value=fill_value, |
|
rescale=rescale) |
|
return ip(xi) |
|
elif method == 'cubic' and ndim == 2: |
|
ip = CloughTocher2DInterpolator(points, values, fill_value=fill_value, |
|
rescale=rescale) |
|
return ip(xi) |
|
else: |
|
raise ValueError("Unknown interpolation method %r for " |
|
"%d dimensional data" % (method, ndim)) |
|
|