-
Notifications
You must be signed in to change notification settings - Fork 73
/
neighbors.py
139 lines (116 loc) · 4.73 KB
/
neighbors.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# 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
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
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__
)
)
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
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)