Source code for verde.neighbors
# Copyright (c) 2017 The Verde Developers.
# Distributed under the terms of the BSD 3-Clause License.
# SPDX-License-Identifier: BSD-3-Clause
#
# This code is part of the Fatiando a Terra project (https://www.fatiando.org)
#
"""
Nearest neighbor interpolation
"""
import warnings
import numpy as np
from sklearn.utils.validation import check_is_fitted
from .base import BaseGridder, check_fit_input, n_1d_arrays
from .coordinates import get_region
from .utils import kdtree
[docs]
class KNeighbors(BaseGridder):
"""
Nearest neighbor interpolation.
This gridder assumes Cartesian coordinates.
Interpolation based on the values of the *k* nearest neighbors of each
interpolated point. The number of neighbors *k* can be controlled and
mostly influences the spatial smoothness of the interpolated values.
The data values of the *k* nearest neighbors are combined into a single
value by a reduction function, which defaults to the mean. This can also be
configured.
.. note::
If installed, package ``pykdtree`` will be used for the nearest
neighbors look-up instead of :class:`scipy.spatial.cKDTree` for better
performance.
Parameters
----------
k : int
The number of neighbors to use for each interpolated point. Default is
1.
reduction : function
Function used to combine the values of the *k* neighbors into a single
value. Can be any function that takes a 1D numpy array as input and
outputs a single value. Default is :func:`numpy.mean`.
Attributes
----------
tree_ : K-D tree
An instance of the K-D tree data structure for the data points that is
used to query for nearest neighbors.
data_ : 1D array
A copy of the input data as a 1D array. Used to look up values for
interpolation/prediction.
region_ : tuple
The boundaries (``[W, E, S, N]``) of the data used to fit the
interpolator. Used as the default region for the
:meth:`~verde.KNeighbors.grid`` method.
"""
def __init__(self, k=1, reduction=np.mean):
super().__init__()
self.k = k
self.reduction = reduction
[docs]
def fit(self, coordinates, data, weights=None):
"""
Fit the interpolator to the given data.
The data region is captured and used as default for the
:meth:`~verde.KNeighbors.grid` method.
Parameters
----------
coordinates : tuple of arrays
Arrays with the coordinates of each data point. Should be in the
following order: (easting, northing, vertical, ...). Only easting
and northing will be used, all subsequent coordinates will be
ignored.
data : array
The data values that will be interpolated.
weights : None or array
Data weights are **not supported** by this interpolator and will be
ignored. Only present for compatibility with other gridders.
Returns
-------
self
Returns this gridder instance for chaining operations.
"""
if weights is not None:
warnings.warn(
"{} does not support weights and they will be ignored.".format(
self.__class__.__name__
),
stacklevel=2,
)
coordinates, data, weights = check_fit_input(coordinates, data, weights)
self.region_ = get_region(coordinates[:2])
self.tree_ = kdtree(coordinates[:2])
# Make sure this is an array and not a subclass of array (pandas,
# xarray, etc) so that we can index it later during predict.
self.data_ = np.asarray(data).ravel().copy()
return self
[docs]
def predict(self, coordinates):
"""
Interpolate data on the given set of points.
Requires a fitted gridder (see :meth:`~verde.KNeighbors.fit`).
Parameters
----------
coordinates : tuple of arrays
Arrays with the coordinates of each data point. Should be in the
following order: (easting, northing, vertical, ...). Only easting
and northing will be used, all subsequent coordinates will be
ignored.
Returns
-------
data : array
The data values interpolated on the given points.
"""
check_is_fitted(self, ["tree_"])
distances, indices = self.tree_.query(
np.transpose(n_1d_arrays(coordinates, 2)), k=self.k
)
if indices.ndim == 1:
indices = np.atleast_2d(indices).T
neighbor_values = np.reshape(self.data_[indices.ravel()], indices.shape)
data = self.reduction(neighbor_values, axis=1)
shape = np.broadcast(*coordinates[:2]).shape
return data.reshape(shape)