Python / SciPy的峰值查找算法

时间:2009-11-11 05:54:48

标签: python scipy fft hough-transform

我可以通过查找一阶导数的零交叉来自己写一些东西,但它似乎是一个通用的函数,可以包含在标准库中。有人知道吗?

我的特定应用是2D阵列,但通常用于在FFT等中找到峰值。

具体而言,在这些问题中,存在多个强峰,然后是许多较小的“峰值”,这些“峰值”仅由应该忽略的噪声引起。这只是例子;不是我的实际数据:

1维峰:

FFT output with peaks

二维峰:

Radon transform output with circled peak

峰值发现算法会找到这些峰值的位置(不仅仅是它们的值),理想情况下会找到真正的样本间峰值,而不仅仅是具有最大值的索引,可能使用quadratic interpolation或其他东西

通常你只关心几个强峰,所以要么选择它们,因为它们超过某个阈值,或者因为它们是有序列表的第一个 n 峰值,按振幅排列。

正如我所说,我知道如何自己写这样的东西。我只是想知道是否有一个已知的功能或包可以很好地工作。

更新

translated a MATLAB script并且它适用于1-D案例,但可能会更好。

更新了更新:

对于1-D案例,

sixtenbe created a better version

9 个答案:

答案 0 :(得分:43)

我正在研究类似的问题,我发现一些最好的参考资料来自化学(来自质谱数据中的峰值)。有关峰值查找算法的详细审查,请阅读this。这是我遇到的最佳峰值发现技术最清晰的评论之一。 (小波最适合在噪声数据中找到此类峰值。)。

看起来您的峰值已明确定义,并且不会隐藏在噪音中。在这种情况下,我建议使用平滑的savtizky-golay衍生物来找到峰值(如果你只是区分上面的数据,那么你就会有一堆误报。)。这是一种非常有效的技术,并且很容易实现(你需要一个矩阵类w /基本操作)。如果您只是找到第一个S-G衍生物的零交叉,我想您会很高兴。

答案 1 :(得分:21)

函数scipy.signal.find_peaks顾名思义,对此很有用。但重要的是要充分了解其参数widththresholddistance,尤其是prominence ,以获得良好的峰提取。

根据我的测试和文档,突出的概念是“有用的概念”,用于保持良好的峰值并丢弃嘈杂的峰值。

什么是(topographic) prominence?它是“从山顶下降到更高地形所需的最低高度” ,如此处所示:

enter image description here

想法是:

  

突出程度越高,峰越“重要”。

测试:

enter image description here

我故意使用一个(噪声)频率变化的正弦曲线,因为它显示出很多困难。我们可以看到width参数在这里不是很有用,因为如果您将最小值width设置得太高,那么它将无法跟踪高频部分中非常接近的峰值。如果您将width设置得太低,则信号左侧会出现许多不需要的峰值。 distance也有同样的问题。 threshold仅与直接邻居进行比较,在这里没有用。 prominence是提供最佳解决方案的人。请注意,您可以结合使用许多这些参数!

代码:

import numpy as np
import matplotlib.pyplot as plt 
from scipy.signal import find_peaks

x = np.sin(2*np.pi*(2**np.linspace(2,10,1000))*np.arange(1000)/48000) + np.random.normal(0, 1, 1000) * 0.15
peaks, _ = find_peaks(x, distance=20)
peaks2, _ = find_peaks(x, prominence=1)      # BEST!
peaks3, _ = find_peaks(x, width=20)
peaks4, _ = find_peaks(x, threshold=0.4)     # Required vertical distance to its direct neighbouring samples, pretty useless
plt.subplot(2, 2, 1)
plt.plot(peaks, x[peaks], "xr"); plt.plot(x); plt.legend(['distance'])
plt.subplot(2, 2, 2)
plt.plot(peaks2, x[peaks2], "ob"); plt.plot(x); plt.legend(['prominence'])
plt.subplot(2, 2, 3)
plt.plot(peaks3, x[peaks3], "vg"); plt.plot(x); plt.legend(['width'])
plt.subplot(2, 2, 4)
plt.plot(peaks4, x[peaks4], "xk"); plt.plot(x); plt.legend(['threshold'])
plt.show()

答案 2 :(得分:19)

scipy中有一个名为scipy.signal.find_peaks_cwt的功能,听起来很适合您的需要,但我没有经验,所以我不推荐..

http://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.find_peaks_cwt.html

答案 3 :(得分:14)

对于那些不确定在Python中使用哪种峰值查找算法的人,可以快速浏览一下备选方案:https://github.com/MonsieurV/py-findpeaks

想要自己相当于MatLab findpeaks功能,我发现来自Marcos Duarte的detect_peaks function是一个很好的捕捉。

非常好用:

import numpy as np
from vector import vector, plot_peaks
from libs import detect_peaks
print('Detect peaks with minimum height and distance filters.')
indexes = detect_peaks.detect_peaks(vector, mph=7, mpd=2)
print('Peaks are: %s' % (indexes))

哪个会给你:

detect_peaks results

答案 4 :(得分:6)

以可靠的方式检测光谱中的峰值已经进行了相当多的研究,例如80年代音乐/音频信号的正弦建模的所有工作。在文献中寻找“正弦模型”。

如果您的信号与示例一样干净,那么简单的“给我一个振幅高于N个邻居的东西”应该可以很好地工作。如果您有嘈杂的信号,一种简单但有效的方法是查看您的峰值,跟踪它们:然后检测谱线而不是谱峰。 IOW,您在信号的滑动窗口上计算FFT,以获得一组时间光谱(也称为频谱图)。然后,您可以看到光谱峰值的演变(即在连续的窗口中)。

答案 5 :(得分:4)

我不认为您所寻找的是由SciPy提供的。在这种情况下,我会自己编写代码。

来自scipy.interpolate的样条插值和平滑非常好,可能非常有助于拟合峰值,然后找到它们的最大位置。

答案 6 :(得分:1)

有用于查找数据异常值的标准统计函数和方法,这可能是您在第一种情况下所需要的。使用衍生物可以解决你的问题。但是,我不确定一种解决连续函数和采样数据的方法。

答案 7 :(得分:1)

要检测正峰和负峰,PeakDetect很有帮助。

from peakdetect import peakdetect

peaks = peakdetect(data, lookahead=20) 
# Lookahead is the distance to look ahead from a peak to determine if it is the actual peak. 
# Change lookahead as necessary 
higherPeaks = np.array(peaks[0])
lowerPeaks = np.array(peaks[1])
plt.plot(data)
plt.plot(higherPeaks[:,0], higherPeaks[:,1], 'ro')
plt.plot(lowerPeaks[:,0], lowerPeaks[:,1], 'ko')

PeakDetection

答案 8 :(得分:0)

首先,"峰值"的定义如果没有进一步的规范,则含糊不清例如,对于以下系列,您会将5-4-5称为一个峰值还是两个?

1-2-1-2-1-1-5-4-5-1-1-5-1

在这种情况下,您需要至少两个阈值:1)仅高于阈值的高阈值,极值可以注册为峰值; 2)低阈值,以便由低于它的小值分隔的极值将变为两个峰值。

峰值检测是极值理论文献中一个经过深入研究的主题,也被称为“极端值的去模式”。其典型应用包括基于环境变量的连续读数识别危险事件,例如分析风速以探测风暴事件。