Skip to content

Commit 8fe6b99

Browse files
authored
Merge pull request #221 from bobleesj/cleanup
Improve `wsinterp` docstrings and test function
2 parents 84b402f + 91594ed commit 8fe6b99

File tree

9 files changed

+45
-60
lines changed

9 files changed

+45
-60
lines changed

Diff for: README.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ General purpose shared utilities for the diffpy libraries.
4343
The diffpy.utils package provides functions for extracting array data from
4444
variously formatted text files, an interpolation function based on the
4545
Whittaker-Shannon formula that can be used to resample a PDF or other profile
46-
function over a new grid, `Diffraction_objects` for conveniently manipulating
46+
function over a new grid, `DiffractionObject` for conveniently manipulating
4747
diffraction data, and some wx GUI utilities used by the PDFgui
4848
program.
4949

Diff for: src/diffpy/utils/__init__.py

-2
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,3 @@
2020

2121
# silence the pyflakes syntax checker
2222
assert __version__ or True
23-
24-
# End of file

Diff for: src/diffpy/utils/parsers/__init__.py

-2
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,3 @@
1515

1616
"""Various utilities related to data parsing and manipulation.
1717
"""
18-
19-
# End of file

Diff for: src/diffpy/utils/parsers/loaddata.py

-3
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,3 @@ def isfloat(s):
344344
except ValueError:
345345
pass
346346
return False
347-
348-
349-
# End of file

Diff for: src/diffpy/utils/resampler.py

+29-32
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,26 @@
1515

1616
"""Various utilities related to data parsing and manipulation."""
1717

18-
import numpy
18+
import numpy as np
1919

2020

21-
# NOTE - this should be faster than resample below and conforms more closely to
22-
# numpy.interp. I'm keeping resample for legacy reasons.
2321
def wsinterp(x, xp, fp, left=None, right=None):
2422
"""One-dimensional Whittaker-Shannon interpolation.
2523
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).
2829
2930
Parameters
3031
----------
3132
x: ndarray
32-
Desired range for interpolation.
33+
The x values at which interpolation is computed.
3334
xp: ndarray
34-
Defined range for fp.
35+
The array of known x values.
3536
fp: ndarray
36-
Function to be interpolated.
37+
The array of y values associated with xp.
3738
left: float
3839
If given, set fp for x < xp[0] to left. Otherwise, if left is None (default) or not given,
3940
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):
4344
4445
Returns
4546
-------
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.
5050
"""
51-
scalar = numpy.isscalar(x)
51+
scalar = np.isscalar(x)
5252
if scalar:
53-
x = numpy.array(x)
53+
x = np.array(x)
5454
x.resize(1)
5555
# 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)))
5757
# Must take transpose of u for proper broadcasting with xp.
5858
# shape = (nx, nxp), v(xp) data spans axis 1
5959
v = (xp - u.T) / (xp[1] - xp[0])
6060
# shape = (nx, nxp), m(v) data spans axis 1
61-
m = fp * numpy.sinc(v)
61+
m = fp * np.sinc(v)
6262
# Sum over m(v) (axis 1)
63-
fp_at_x = numpy.sum(m, axis=1)
63+
fp_at_x = np.sum(m, axis=1)
6464

6565
# Enforce left and right
6666
if left is None:
@@ -100,36 +100,33 @@ def resample(r, s, dr):
100100
dr0 = r[1] - r[0] # Constant timestep
101101

102102
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)
105105
return rnew, snew
106106

107107
elif dr0 > dr:
108108
# Tried to pad the end of s to dampen, but nothing works.
109109
# m = (s[-1] - s[-2]) / dr0
110110
# 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
112112
# 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)
116116
# Accommodate for the fact that r[0] might not be 0
117117
# u = (rnew-r[0]) / dr0
118118
# for n in range(len(spad)):
119-
# snew += spad[n] * numpy.sinc(u - n)
119+
# snew += spad[n] * np.sinc(u - n)
120120

121-
# sel = numpy.logical_and(rnew >= r[0], rnew <= r[-1])
121+
# sel = np.logical_and(rnew >= r[0], rnew <= r[-1])
122122

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)
125125
u = (rnew - r[0]) / dr0
126126
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])
129129
return rnew[sel], snew[sel]
130130

131131
# If we got here, then no resampling is required
132132
return r.copy(), s.copy()
133-
134-
135-
# End of file

Diff for: src/diffpy/utils/version.py

-2
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,3 @@
2222
from importlib.metadata import version
2323

2424
__version__ = version("diffpy.utils")
25-
26-
# End of file

Diff for: src/diffpy/utils/wx/__init__.py

-2
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,3 @@
1515

1616
"""Utilities related wx Python GUIs.
1717
"""
18-
19-
# End of file

Diff for: src/diffpy/utils/wx/gridutils.py

-3
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,3 @@ def _indicesToBlocks(indices):
163163
i0 = i
164164
rv = [tuple(ij) for ij in rngs]
165165
return rv
166-
167-
168-
# End of file

Diff for: tests/test_resample.py

+15-13
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,32 @@
1+
import random
2+
13
import numpy as np
24
import pytest
35

46
from diffpy.utils.resampler import wsinterp
57

68

79
def test_wsinterp():
8-
import random
9-
10-
# Check known points are unchanged by interpolation
1110
# FIXME: if another SW interp function exists, run comparisons for interpolated points
11+
1212
# Sampling rate
1313
ssr = 44100**-1 # Standard sampling rate for human-hearable frequencies
14-
t = ssr
14+
15+
# Creating a symmetric set of sample points around zero.
1516
n = 5
16-
xp = np.array([i * t for i in range(-n, n + 1, 1)])
17-
x = np.array([i * t for i in range(-n - 1, n + 2, 1)])
17+
xp = np.array([i * ssr for i in range(-n, n + 1, 1)])
18+
x = np.array([i * ssr for i in range(-n - 1, n + 2, 1)])
19+
assert len(xp) == 11 and len(x) == 13
1820

19-
# Interpolate a few random datasets
21+
# Generate a new set of fp values across 10 trial runs
2022
trials = 10
21-
for trial in range(trials):
22-
fp = np.array([random.random() * ssr for i in range(-n, n + 1, 1)])
23+
24+
for _ in range(trials):
25+
# Create random function values (fp) at the points defined in xp above
26+
fp = np.array([random.random() * ssr for _ in range(-n, n + 1, 1)])
2327
fp_at_x = wsinterp(x, xp, fp)
28+
29+
# Check that the known points are unchanged by interpolation
2430
assert np.allclose(fp_at_x[1:-1], fp)
2531
for i in range(len(x)):
2632
assert fp_at_x[i] == pytest.approx(wsinterp(x[i], xp, fp))
27-
28-
29-
if __name__ == "__main__":
30-
test_wsinterp()

0 commit comments

Comments
 (0)