Peak detection algorithm in Python

一世执手 提交于 2019-11-29 08:54:01

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

Your function uses quite a lot of parameters. You can decompose the problem to a few steps:

  1. First detect all points above the threshold. Add those points to a maxthresh and minthresh list.
  2. 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.
  3. 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()

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:

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!