Completed
Pull Request — master (#326)
by
unknown
01:49
created

interpolate_nans()   B

Complexity

Conditions 3

Size

Total Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
c 1
b 0
f 0
dl 0
loc 30
rs 8.8571
1
# Copyright (c) 2008-2015 MetPy Developers.
2
# Distributed under the terms of the BSD 3-Clause License.
3
# SPDX-License-Identifier: BSD-3-Clause
4
"""Contains a collection of generally useful calculation tools."""
5
6
import numpy as np
7
8
from ..package_tools import Exporter
9
10
exporter = Exporter(globals())
11
12
13
@exporter.export
14
def resample_nn_1d(a, centers):
15
    """Return one-dimensional nearest-neighbor indexes based on user-specified centers.
16
17
    Parameters
18
    ----------
19
    a : array-like
20
        1-dimensional array of numeric values from which to
21
        extract indexes of nearest-neighbors
22
    centers : array-like
23
        1-dimensional array of numeric values representing a subset of values to approximate
24
25
    Returns
26
    -------
27
        An array of indexes representing values closest to given array values
28
    """
29
    ix = []
30
    for center in centers:
31
        index = (np.abs(a - center)).argmin()
32
        if index not in ix:
33
            ix.append(index)
34
    return ix
35
36
37
@exporter.export
38
def nearest_intersection_idx(a, b):
39
    """Determine the index of the point just before two lines with common x values.
40
41
    Parameters
42
    ----------
43
    a : array-like
44
        1-dimensional array of y-values for line 1
45
    b : array-like
46
        1-dimensional array of y-values for line 2
47
48
    Returns
49
    -------
50
        An array of indexes representing the index of the values
51
        just before the intersection(s) of the two lines.
52
    """
53
    # Difference in the two y-value sets
54
    difference = a - b
55
56
    # Determine the point just before the intersection of the lines
57
    # Will return multiple points for multiple intersections
58
    sign_change_idx, = np.nonzero(np.diff(np.sign(difference)))
59
60
    return sign_change_idx
61
62
63
@exporter.export
64
def find_intersections(x, a, b, direction='all'):
65
    """Calculate the best estimate of intersection.
66
67
    Calculates the best estimates of the intersection of two y-value
68
    data sets that share a common x-value set.
69
70
    Parameters
71
    ----------
72
    x : array-like
73
        1-dimensional array of numeric x-values
74
    a : array-like
75
        1-dimensional array of y-values for line 1
76
    b : array-like
77
        1-dimensional array of y-values for line 2
78
    direction : string
79
        specifies direction of crossing. 'all', 'increasing' (a becoming greater than b),
80
        or 'decreasing' (b becoming greater than a).
81
82
    Returns
83
    -------
84
        A tuple (x, y) of array-like with the x and y coordinates of the
85
        intersections of the lines.
86
    """
87
    # Find the index of the points just before the intersection(s)
88
    nearest_idx = nearest_intersection_idx(a, b)
89
    next_idx = nearest_idx + 1
90
91
    # Determine the sign of the change
92
    sign_change = np.sign(a[next_idx] - b[next_idx])
93
94
    # x-values around each intersection
95
    x0 = x[nearest_idx]
96
    x1 = x[next_idx]
97
98
    # y-values around each intersection for the first line
99
    a0 = a[nearest_idx]
100
    a1 = a[next_idx]
101
102
    # y-values around each intersection for the second line
103
    b0 = b[nearest_idx]
104
    b1 = b[next_idx]
105
106
    # Calculate the x-intersection. This comes from finding the equations of the two lines,
107
    # one through (x0, a0) and (x1, a1) and the other through (x0, b0) and (x1, b1),
108
    # finding their intersection, and reducing with a bunch of algebra.
109
    delta_y0 = a0 - b0
110
    delta_y1 = a1 - b1
111
    intersect_x = (delta_y1 * x0 - delta_y0 * x1) / (delta_y1 - delta_y0)
112
113
    # Calculate the y-intersection of the lines. Just plug the x above into the equation
114
    # for the line through the a points. One could solve for y like x above, but this
115
    # causes weirder unit behavior and seems a little less good numerically.
116
    intersect_y = ((intersect_x - x0) / (x1 - x0)) * (a1 - a0) + a0
117
118
    # Make a mask based on the direction of sign change desired
119
    if direction == 'increasing':
120
        mask = sign_change > 0
121
    elif direction == 'decreasing':
122
        mask = sign_change < 0
123
    elif direction == 'all':
124
        return intersect_x, intersect_y
125
    else:
126
        raise ValueError('Unknown option for direction: {0}'.format(str(direction)))
127
    return intersect_x[mask], intersect_y[mask]
128
129
130
@exporter.export
131
def interpolate_nans(x, y, kind='linear'):
132
    """Interpolate NaN values in y.
133
134
    Interpolate NaN values in the y dimension. Works with unsorted x values.
135
136
    Parameters
137
    ----------
138
    x : array-like
139
        1-dimensional array of numeric x-values
140
    y : array-like
141
        1-dimensional array of numeric y-values
142
    kind : string
143
        specifies the kind of interpolation x coordinate - 'linear' or 'log'
144
145
    Returns
146
    -------
147
        An array of the y coordinate data with NaN values interpolated.
148
    """
149
    x_sort_args = np.argsort(x)
150
    x = x[x_sort_args]
151
    y = y[x_sort_args]
152
    nans = np.isnan(y)
153
    if kind is 'linear':
154
        y[nans] = np.interp(x[nans], x[~nans], y[~nans])
155
    elif kind is 'log':
156
        y[nans] = np.interp(np.log(x[nans]), np.log(x[~nans]), y[~nans])
157
    else:
158
        raise ValueError('Unknown option for kind: {0}'.format(str(kind)))
159
    return y[x_sort_args]
160