Skip to content

Commit d8e436a

Browse files
committed
I goofed; peakdet is actually used
1 parent 0d601ab commit d8e436a

2 files changed

Lines changed: 79 additions & 0 deletions

File tree

pynumdiff/tests/test_utils.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,34 @@ def test_convolutional_smoother():
4747
assert np.allclose(utility.convolutional_smoother(x, kernel_even, num_iterations=3), np.ones(len(x)))
4848

4949

50+
def test_peakdet(request):
51+
"""Verify peakdet finds peaks and valleys"""
52+
t = np.arange(0, 10, 0.001)
53+
x = 0.3*np.sin(t) + np.sin(1.3*t) + 0.9*np.sin(4.2*t) + 0.02*np.random.randn(10000)
54+
maxtab, mintab = utility.peakdet(x, 0.5, t)
55+
56+
if request.config.getoption("--plot"):
57+
from matplotlib import pyplot
58+
pyplot.plot(t, x)
59+
pyplot.plot(mintab[:,0], mintab[:,1], 'g*')
60+
pyplot.plot(maxtab[:,0], maxtab[:,1], 'r*')
61+
pyplot.title('peakdet validataion')
62+
pyplot.show()
63+
64+
assert np.allclose(maxtab, [[0.447, 1.58575613], # these numbers validated by eye with --plot
65+
[1.818, 1.91349239],
66+
[3.316,-0.02740252],
67+
[4.976, 0.74512778],
68+
[6.338, 1.89861691],
69+
[7.765, 0.57577842],
70+
[9.402, 0.59450898]])
71+
assert np.allclose(mintab, [[1.139, 0.31325728],
72+
[2.752,-1.12769567],
73+
[4.098,-2.00326946],
74+
[5.507,-0.31714122],
75+
[7.211,-0.59708324],
76+
[8.612,-1.7118216 ]])
77+
5078
def test_slide_function():
5179
"""Verify the slide function's weighting scheme calculates as expected"""
5280
def identity(x, dt): return x, 0 # should come back the same

pynumdiff/utils/utility.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,3 +130,54 @@ def slide_function(func, x, dt, kernel, *args, stride=1, pass_weights=False, **k
130130
weight_sum[window] += w # save sum of weights for normalization at the end
131131

132132
return x_hat/weight_sum, dxdt_hat/weight_sum
133+
134+
135+
def peakdet(x, delta, t=None):
136+
"""Find peaks and valleys of 1D array. A point is considered a maximum peak if it has the maximal
137+
value, and was preceded (to the left) by a value lower by delta. Converted from MATLAB script at
138+
http://billauer.co.il/peakdet.html Eli Billauer, 3.4.05 (Explicitly not copyrighted). This function
139+
is released to the public domain; Any use is allowed.
140+
141+
:param np.array[float] x: array for which to find peaks and valleys
142+
:param float delta: threshold for finding peaks and valleys. A point is considered a maximum peak
143+
if it has the maximal value, and was preceded (to the left) by a value lower by delta.
144+
:param np.array[float] t: optional domain points where data comes from, to make indices into locations
145+
146+
:return: tuple[np.array, np.array] of\n
147+
- **maxtab** -- indices or locations (column 1) and values (column 2) of maxima
148+
- **mintab** -- indices or locations (column 1) and values (column 2) of minima
149+
"""
150+
maxtab = []
151+
mintab = []
152+
if t is None:
153+
t = np.arange(len(x))
154+
elif len(x) != len(t):
155+
raise ValueError('Input vectors x and t must have same length')
156+
if not (np.isscalar(delta) and delta > 0):
157+
raise ValueError('Input argument delta must be a positive scalar')
158+
159+
mn, mx = np.inf, -1*np.inf
160+
mnpos, mxpos = np.nan, np.nan
161+
lookformax = True
162+
for i in np.arange(len(x)):
163+
this = x[i]
164+
if this > mx:
165+
mx = this
166+
mxpos = t[i]
167+
if this < mn:
168+
mn = this
169+
mnpos = t[i]
170+
if lookformax:
171+
if this < mx-delta:
172+
maxtab.append((mxpos, mx))
173+
mn = this
174+
mnpos = t[i]
175+
lookformax = False # now searching for a min
176+
else:
177+
if this > mn+delta:
178+
mintab.append((mnpos, mn))
179+
mx = this
180+
mxpos = t[i]
181+
lookformax = True # now searching for a max
182+
183+
return np.array(maxtab), np.array(mintab)

0 commit comments

Comments
 (0)