问题
I'm implementing a peak detection algorithm in Python that detects only those peaks that are above a threshold magnitude. I don't want to use the inbuilt function as I have to extend this simulation to Hardware implementation also.
from math import sin,isnan
from pylab import *
def peakdet(v, delta,thresh,x):
delta=abs(delta)
maxtab = []
mintab = []
v = asarray(v)
mn, mx = v[0], v[0]
mnpos, mxpos = NaN, NaN
lookformax = True
for i in arange(len(v)):
this = v[i]
if abs(this)>thresh:
if this > mx:
mx = this
mxpos = x[i]
if this < mn:
mn = this
mnpos = x[i]
if lookformax:
if (this < mx-delta):
if (mx>abs(thresh)) and not isnan(mxpos):
maxtab.append((mxpos, mx))
mn = this
mnpos = x[i]
lookformax = False
else:
if (this > mn+delta):
if (mn<-abs(thresh)) and not isnan(mnpos):
mintab.append((mnpos, mn))
mx = this
mxpos = x[i]
lookformax = True
return array(maxtab), array(mintab)
#Input Signal
t=array(range(100))
series=0.3*sin(t)+0.7*cos(2*t)-0.5*sin(1.2*t)
thresh=0.95 #Threshold value
delta=0.0 #
a=zeros(len(t)) #
a[:]=thresh #
maxtab, mintab = peakdet(series,delta,thresh,t)
#Plotting output
scatter(array(maxtab)[:,0], array(maxtab)[:,1], color='red')
scatter(array(mintab)[:,0], array(mintab)[:,1], color='blue')
xlim([0,t[-1]])
title('Peak Detector')
grid(True)
plot(t,a,color='green',linestyle='--',dashes=(5,3))
plot(t,-a,color='green',linestyle='--',dashes=(5,3))
annotate('Threshold',xy=(t[-1],thresh),fontsize=9)
plot(t,series,'k')
show()
The problem with this program is that it is unable to detect some peaks even though they are above the threshold. This is the output I got:

I saw other posts with peak detection problems but couldn't find any solution. Please help and suggest corrections.
回答1:
these code
if lookformax:
if (this < mx-delta):
if (mx>abs(thresh)) and not isnan(mxpos):
maxtab.append((mxpos, mx))
mn = this
mnpos = x[i]
lookformax = False
else:
if (this > mn+delta):
if (mn<-abs(thresh)) and not isnan(mnpos):
mintab.append((mnpos, mn))
mx = this
mxpos = x[i]
lookformax = True
only run under the condition
if abs(this)>thresh:
so your can only find a peak when the next point above the thresh is smaller than it.
put it out the condition
回答2:
Your function uses quite a lot of parameters. You can decompose the problem to a few steps:
- First detect all points above the threshold. Add those points to a
maxthresh
andminthresh
list. - Iterate through the
maxthresh
list and if the y value prior to the point is less than the point, and the y value after the point is less than the point, then the point is a peak. - Iterate through the
minthresh
list and if the y value prior to the point is greater than the point, and the y value after the point is greather than the point, then the point is a peak.
Code implementation:
from math import sin
from matplotlib import pylab
from pylab import *
def peakdet(v, thresh):
maxthresh = []
minthresh = []
peaks = []
valleys = []
for x, y in v:
if y > thresh:
maxthresh.append((x, y))
elif y < -thresh:
minthresh.append((x, y))
for x, y in maxthresh:
try:
if (v[x - 1][1] < y) & (v[x + 1][1] < y):
peaks.append((x, y))
except Exception:
pass
for x, y in minthresh:
try:
if (v[x - 1][1] > y) & (v[x + 1][1] > y):
valleys.append((x, y))
except Exception:
pass
return peaks, valleys
Testing the code:
# input signal
t = array(range(100))
series = 0.3 * sin(t) + 0.7 * cos(2 * t) - 0.5 * sin(1.2 * t)
arr = [*zip(t, series)] # create a list of tuples where the tuples represent the (x, y) values of the function
thresh = 0.95
peaks, valleys = peakdet(arr, thresh)
scatter([x for x, y in peaks], [y for x, y in peaks], color = 'red')
scatter([x for x, y in valleys], [y for x, y in valleys], color = 'blue')
plot(t, 100 * [thresh], color='green', linestyle='--', dashes=(5, 3))
plot(t, 100 * [-thresh], color='green', linestyle='--', dashes=(5, 3))
plot(t, series, 'k')
show()
Additional test to make sure peak is detected when multiple points above threshold:
# input signal
t = array(range(100))
series = 6.3 * sin(t) + 4.7 * cos(2 * t) - 3.5 * sin(1.2 * t)
arr = [*zip(t, series)]
thresh = 0.95
peaks, valleys = peakdet(arr, thresh)
scatter([x for x, y in peaks], [y for x, y in peaks], color = 'red')
scatter([x for x, y in valleys], [y for x, y in valleys], color = 'blue')
plot(t, 100 * [thresh], color='green', linestyle='--', dashes=(5, 3))
plot(t, 100 * [-thresh], color='green', linestyle='--', dashes=(5, 3))
plot(t, series, 'k')
show()
回答3:
So, here you have a numpythonic solution (which is much better than do a loop explicitly).
I use the roll function to shift the numbers +1 or -1 in the position. Also a "peak" is defined as a local maximum, where the previous and posterior number are smaller then the central value.
The full code is above:
import numpy as np
import matplotlib.pyplot as plt
# input signal
x = np.arange(1,100,1)
y = 0.3 * np.sin(t) + 0.7 * np.cos(2 * t) - 0.5 * np.sin(1.2 * t)
threshold = 0.95
# max
maxi = np.where(np.where([(y - np.roll(y,1) > 0) & (y - np.roll(y,-1) > 0)],y, 0)> threshold, y,np.nan)
# min
mini = np.where(np.where([(y - np.roll(y,1) < 0) & (y - np.roll(y,-1) < 0)],y, 0)< -threshold, y,np.nan)
if you plot it, you get:
来源:https://stackoverflow.com/questions/50756793/peak-detection-algorithm-in-python