Peak-finding algorithm for Python/SciPy
我可以自己写一些东西,通过找到一阶导数的零交叉点或其他东西,但它似乎是一个足够通用的函数,可以包含在标准库中。有人知道吗?
我的特殊应用是一个二维数组,但通常它会用于在FFT等中查找峰值。
具体地说,在这类问题中,有多个强峰值,然后有许多较小的"峰值",这些都是由噪声引起的,应该忽略不计。这些只是例子,不是我的实际数据:
一维峰值:
二维峰值:
寻峰算法可以找到这些峰的位置(不仅仅是它们的值),理想情况下可以找到真正的样本间峰,而不仅仅是具有最大值的指数,可能使用二次插值或其他方法。
一般来说,你只关心一些强峰,所以它们要么被选择是因为它们高于某个阈值,要么因为它们是按振幅排序的有序列表的前n个峰。
正如我所说,我知道如何自己写这样的东西。我只是在问是否有一个预先存在的函数或包可以很好地工作。
更新:
我翻译了一个matlab脚本,它可以很好地处理一维情况,但可能会更好。
最新更新:
Sixtenbe为一维案例创建了一个更好的版本。
我正在研究一个类似的问题,我发现了一些最好的参考资料来自化学(从质谱数据中的峰值发现)。为了对峰值查找算法进行全面的回顾,请阅读本文。这是对我所遇到的寻峰技术最清晰的评论之一。(小波是在噪声数据中寻找此类峰值的最佳方法。)
看起来你的峰很清晰,没有隐藏在噪音中。在这种情况下,我建议使用光滑的精明的戈莱导数来找到峰值(如果你只是区分上面的数据,你会发现一堆误报)。这是一种非常有效的技术,并且非常容易实现(您确实需要一个包含基本操作的矩阵类)。如果你简单地找到第一个S-G导数的零交叉点,我想你会很高兴的。
Scipy中有一个名为
http://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.find_peaks_cwt.html
顾名思义,函数
根据我的测试和文档,突出的概念是"有用的概念",以保持良好的峰值,并丢弃噪声峰值。
什么是(地形)突出?这是"从山顶到任何更高地形所需的最低下降高度",如图所示:
这个想法是:
The higher the prominence, the more"important" the peak is.
测试:
我故意用(有噪音的)频率变化的正弦曲线,因为它显示出许多困难。我们可以看到,
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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() |
对于那些不确定在python中使用哪种寻峰算法的人,这里快速概述了替代方法:https://github.com/monsieurv/py-findpeaks
我希望自己能与matlab
非常容易使用:
1 2 3 4 5 6 | 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)) |
这将给你:
以可靠的方式检测频谱中的峰值已经被研究了很多,例如80年代所有关于音乐/音频信号正弦建模的工作。在文献中寻找"正弦模型"。
如果你的信号和例子一样清晰,那么一个简单的"给我一个比n个邻居高的振幅"应该可以很好地工作。如果你有噪声信号,一个简单但有效的方法是及时观察你的峰值,跟踪它们:然后你检测光谱线而不是光谱峰。在信号的滑动窗口上计算FFT,得到一组光谱(也叫光谱图)。然后观察光谱峰在时间上的演变(即在连续窗口中)。
我不认为你要找的是Scipy提供的。在这种情况下,我自己编写代码。
从scipy.interpolate中得到的样条插值和平滑是非常好的,可能对拟合峰值和找到最大值的位置非常有帮助。
有标准的统计函数和方法来查找数据的异常值,这可能是您在第一种情况下需要的。使用导数可以解决第二个问题。但是,我不确定是否有一种方法可以同时解决连续函数和采样数据。
首先,如果没有进一步的规范,"峰值"的定义是模糊的。例如,对于以下系列,您会将5-4-5称为一个峰值还是两个峰值?
1-2-1-2-1-1-5-4-5-1-1-5-1
在这种情况下,您将需要至少两个阈值:1)一个高阈值,它只能高于这个阈值,一个极端值可以注册为一个峰值;2)一个低阈值,以便由它下面的小值分隔的极端值将成为两个峰值。
峰值检测是极值理论文献中研究得很好的一个课题,也称为"极值去聚类"。其典型应用包括根据环境变量的连续读数识别危险事件,例如分析风速以检测风暴事件。