Traktor/myenv/Lib/site-packages/scipy/interpolate/_ndgriddata.py
2024-05-23 01:57:24 +02:00

333 lines
12 KiB
Python

"""
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']
#------------------------------------------------------------------------------
# Nearest-neighbor interpolation
#------------------------------------------------------------------------------
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
"""
# For the sake of enabling subclassing, NDInterpolatorBase._set_xi performs
# some operations which are not required by NearestNDInterpolator.__call__,
# hence here we operate on xi directly, without calling a parent class function.
xi = _ndim_coords_from_arrays(args, ndim=self.points.shape[1])
xi = self._check_call_shape(xi)
xi = self._scale_x(xi)
# We need to handle two important cases:
# (1) the case where xi has trailing dimensions (..., ndim), and
# (2) the case where y has trailing dimensions
# We will first flatten xi to deal with case (1),
# do the computation in flattened array while retaining y's dimensionality,
# and then reshape the interpolated values back to match xi's shape.
# Flatten xi for the query
xi_flat = xi.reshape(-1, xi.shape[-1])
original_shape = xi.shape
flattened_shape = xi_flat.shape
# if distance_upper_bound is set to not be infinite,
# then we need to consider the case where cKDtree
# does not find any points within distance_upper_bound to return.
# It marks those points as having infinte distance, which is what will be used
# below to mask the array and return only the points that were deemed
# to have a close enough neighbor to return something useful.
dist, i = self.tree.query(xi_flat, **query_options)
valid_mask = np.isfinite(dist)
# create a holder interp_values array and fill with nans.
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
#------------------------------------------------------------------------------
# Convenience interface function
#------------------------------------------------------------------------------
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()
""" # numpy/numpydoc#87 # noqa: E501
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
# Sort points/values together, necessary as input for interp1d
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))