| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330 |
- """
- 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):
- """Nearest-neighbor interpolator in N > 1 dimensions.
- Methods
- -------
- __call__
- Parameters
- ----------
- x : (npoints, ndims) 2-D ndarray of floats
- Data point coordinates.
- y : (npoints, ...) N-D ndarray of float or complex
- Data values. The length of `y` along the first axis must be equal to
- the length of `x`.
- 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):
- """
- Convenience function for interpolating unstructured data in multiple dimensions.
- 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(
- f"Unknown interpolation method {method!r} for {ndim} dimensional data"
- )
|