15
15
16
16
"""Various utilities related to data parsing and manipulation."""
17
17
18
- import numpy
18
+ import numpy as np
19
19
20
20
21
- # NOTE - this should be faster than resample below and conforms more closely to
22
- # numpy.interp. I'm keeping resample for legacy reasons.
23
21
def wsinterp (x , xp , fp , left = None , right = None ):
24
22
"""One-dimensional Whittaker-Shannon interpolation.
25
23
26
- This uses the Whittaker-Shannon interpolation formula to interpolate the value of fp (array),
27
- which is defined over xp (array), at x (array or float).
24
+ Reconstruct a continuous signal from discrete data points by utilizing sinc functions
25
+ as interpolation kernels. This function interpolates the values of fp (array),
26
+ which are defined over xp (array), at new points x (array or float).
27
+ The implementation is based on E. T. Whittaker's 1915 paper
28
+ (https://doi.org/10.1017/S0370164600017806).
28
29
29
30
Parameters
30
31
----------
31
32
x: ndarray
32
- Desired range for interpolation.
33
+ The x values at which interpolation is computed .
33
34
xp: ndarray
34
- Defined range for fp .
35
+ The array of known x values .
35
36
fp: ndarray
36
- Function to be interpolated .
37
+ The array of y values associated with xp .
37
38
left: float
38
39
If given, set fp for x < xp[0] to left. Otherwise, if left is None (default) or not given,
39
40
set fp for x < xp[0] to fp evaluated at xp[-1].
@@ -43,24 +44,23 @@ def wsinterp(x, xp, fp, left=None, right=None):
43
44
44
45
Returns
45
46
-------
46
- float:
47
- If input x is a scalar (not an array), return the interpolated value at x.
48
- ndarray:
49
- If input x is an array, return the interpolated array with dimensions of x.
47
+ ndarray or float
48
+ The interpolated values at points x. Returns a single float if x is a scalar,
49
+ otherwise returns a numpy.ndarray.
50
50
"""
51
- scalar = numpy .isscalar (x )
51
+ scalar = np .isscalar (x )
52
52
if scalar :
53
- x = numpy .array (x )
53
+ x = np .array (x )
54
54
x .resize (1 )
55
55
# shape = (nxp, nx), nxp copies of x data span axis 1
56
- u = numpy .resize (x , (len (xp ), len (x )))
56
+ u = np .resize (x , (len (xp ), len (x )))
57
57
# Must take transpose of u for proper broadcasting with xp.
58
58
# shape = (nx, nxp), v(xp) data spans axis 1
59
59
v = (xp - u .T ) / (xp [1 ] - xp [0 ])
60
60
# shape = (nx, nxp), m(v) data spans axis 1
61
- m = fp * numpy .sinc (v )
61
+ m = fp * np .sinc (v )
62
62
# Sum over m(v) (axis 1)
63
- fp_at_x = numpy .sum (m , axis = 1 )
63
+ fp_at_x = np .sum (m , axis = 1 )
64
64
65
65
# Enforce left and right
66
66
if left is None :
@@ -100,36 +100,33 @@ def resample(r, s, dr):
100
100
dr0 = r [1 ] - r [0 ] # Constant timestep
101
101
102
102
if dr0 < dr :
103
- rnew = numpy .arange (r [0 ], r [- 1 ] + 0.5 * dr , dr )
104
- snew = numpy .interp (rnew , r , s )
103
+ rnew = np .arange (r [0 ], r [- 1 ] + 0.5 * dr , dr )
104
+ snew = np .interp (rnew , r , s )
105
105
return rnew , snew
106
106
107
107
elif dr0 > dr :
108
108
# Tried to pad the end of s to dampen, but nothing works.
109
109
# m = (s[-1] - s[-2]) / dr0
110
110
# b = (s[-2] * r[-1] - s[-1] * r[-2]) / dr0
111
- # rpad = r[-1] + numpy .arange(1, len(s))*dr0
111
+ # rpad = r[-1] + np .arange(1, len(s))*dr0
112
112
# spad = rpad * m + b
113
- # spad = numpy .concatenate([s,spad])
114
- # rnew = numpy .arange(0, rpad[-1], dr)
115
- # snew = numpy .zeros_like(rnew)
113
+ # spad = np .concatenate([s,spad])
114
+ # rnew = np .arange(0, rpad[-1], dr)
115
+ # snew = np .zeros_like(rnew)
116
116
# Accommodate for the fact that r[0] might not be 0
117
117
# u = (rnew-r[0]) / dr0
118
118
# for n in range(len(spad)):
119
- # snew += spad[n] * numpy .sinc(u - n)
119
+ # snew += spad[n] * np .sinc(u - n)
120
120
121
- # sel = numpy .logical_and(rnew >= r[0], rnew <= r[-1])
121
+ # sel = np .logical_and(rnew >= r[0], rnew <= r[-1])
122
122
123
- rnew = numpy .arange (0 , r [- 1 ], dr )
124
- snew = numpy .zeros_like (rnew )
123
+ rnew = np .arange (0 , r [- 1 ], dr )
124
+ snew = np .zeros_like (rnew )
125
125
u = (rnew - r [0 ]) / dr0
126
126
for n in range (len (s )):
127
- snew += s [n ] * numpy .sinc (u - n )
128
- sel = numpy .logical_and (rnew >= r [0 ], rnew <= r [- 1 ])
127
+ snew += s [n ] * np .sinc (u - n )
128
+ sel = np .logical_and (rnew >= r [0 ], rnew <= r [- 1 ])
129
129
return rnew [sel ], snew [sel ]
130
130
131
131
# If we got here, then no resampling is required
132
132
return r .copy (), s .copy ()
133
-
134
-
135
- # End of file
0 commit comments