-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathpeakdetect.py
More file actions
130 lines (111 loc) · 4.69 KB
/
peakdetect.py
File metadata and controls
130 lines (111 loc) · 4.69 KB
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
# -*- coding: utf-8 -*-
# peakdetect.py
# Copyright (C) 2017 Ashlar Ruby
# Licensed under the MIT license. See COPYING.md for details.
import numpy as np
from math import pi
i = 10000
x = np.linspace(0, 3.5 * pi, i)
y = (0.3 * np.sin(x) + np.sin(1.3 * x) + 0.9 * np.sin(4.2 * x) +
0.06 * np.random.randn(i))
def _datacheck_peakdetect(x_axis, y_axis):
if x_axis is None:
x_axis = range(len(y_axis))
if len(y_axis) != len(x_axis):
raise ValueError(
'Input vectors y_axis and x_axis must have same length')
# needs to be a numpy array
y_axis = np.array(y_axis)
x_axis = np.array(x_axis)
return x_axis, y_axis
def peakdetect(y_axis, x_axis=None, lookahead=300, delta=0):
"""
Converted from/based on a MATLAB script at:
http://billauer.co.il/peakdet.html
function for detecting local maximas and minmias in a signal.
Discovers peaks by searching for values which are surrounded by lower
or larger values for maximas and minimas respectively
keyword arguments:
y_axis -- A list containg the signal over which to find peaks
x_axis -- (optional) A x-axis whose values correspond to the y_axis list
and is used in the return to specify the postion of the peaks. If
omitted an index of the y_axis is used. (default: None)
lookahead -- (optional) distance to look ahead from a peak candidate to
determine if it is the actual peak (default: 200)
'(sample / period) / f' where '4 >= f >= 1.25' might be a good value
delta -- (optional) this specifies a minimum difference between a peak and
the following points, before a peak may be considered a peak. Useful
to hinder the function from picking up false peaks towards to end of
the signal. To work well delta should be set to delta >= RMSnoise * 5.
(default: 0)
delta function causes a 20% decrease in speed, when omitted
Correctly used it can double the speed of the function
return -- two lists [max_peaks, min_peaks] containing the positive and
negative peaks respectively. Each cell of the lists contains a tupple
of: (position, peak_value)
to get the average peak value do: np.mean(max_peaks, 0)[1] on the
results to unpack one of the lists into x, y coordinates do:
x, y = zip(*tab)
"""
max_peaks = []
min_peaks = []
dump = [] # Used to pop the first hit which almost always is false
# check input data
x_axis, y_axis = _datacheck_peakdetect(x_axis, y_axis)
# store data length for later use
length = len(y_axis)
# perform some checks
if lookahead < 1:
raise ValueError("Lookahead must be '1' or above in value")
if not (np.isscalar(delta) and delta >= 0):
raise ValueError('delta must be a positive number')
# maxima and minima candidates are temporarily stored in
# mx and mn respectively
mn, mx = np.Inf, -np.Inf
# Only detect peak if there is 'lookahead' amount of points after it
for index, (x,
y) in enumerate(zip(x_axis[:-lookahead], y_axis[:-lookahead])):
if y > mx:
mx = y
mxpos = x
if y < mn:
mn = y
mnpos = x
# look for max
if y < mx - delta and mx != np.Inf:
# Maxima peak candidate found
# look ahead in signal to ensure that this is a peak and not jitter
if y_axis[index:index + lookahead].max() < mx:
max_peaks.append([mxpos, mx])
dump.append(True)
# set algorithm to only find minima now
mx = np.Inf
mn = np.Inf
if index + lookahead >= length:
# end is within lookahead no more peaks can be found
break
continue
# look for min
if y > mn + delta and mn != -np.Inf:
# Minima peak candidate found
# look ahead in signal to ensure that this is a peak and not jitter
if y_axis[index:index + lookahead].min() > mn:
min_peaks.append([mnpos, mn])
dump.append(False)
# set algorithm to only find maxima now
mn = -np.Inf
mx = -np.Inf
if index + lookahead >= length:
# end is within lookahead no more peaks can be found
break
# Remove the false hit on the first value of the y_axis
try:
if dump[0]:
max_peaks.pop(0)
else:
min_peaks.pop(0)
del dump
except IndexError:
# no peaks were found, should the function return empty lists?
pass
return [max_peaks, min_peaks]