166

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

我的特定应用是二维数组,但通常它会用于在 FFT 等中查找峰值。

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

一维峰:

带峰值的 FFT 输出

二维峰:

带圆圈峰值的氡变换输出

寻峰算法会找到这些峰值的位置(不仅仅是它们的值),并且理想情况下会找到真正的样本间峰值,而不仅仅是具有最大值的索引,可能使用二次插值或其他方法。

通常,您只关心几个强峰,因此选择它们要么是因为它们高于某个阈值,要么是因为它们是按幅度排序的有序列表的前n 个峰。

正如我所说,我知道如何自己写这样的东西。我只是想问一下是否有一个已知运行良好的预先存在的功能或包。

更新:

翻译了一个 MATLAB 脚本,它适用于一维案例,但可能会更好。

更新更新:

Sixtenbe为一维案例创建了一个更好的版本

4

9 回答 9

133

scipy.signal.find_peaks顾名思义,函数对此很有用。但重要的是要很好地理解它的参数width, thresholddistance prominence重要的是要获得良好的峰值提取。

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

什么是(地形)突出?它是“从山顶下降到任何更高地形所需的最小高度”,如下所示:

在此处输入图像描述

这个想法是:

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

测试:

在此处输入图像描述

我故意使用(嘈杂的)频率变化的正弦曲线,因为它显示出许多困难。我们可以看到该width参数在这里不是很有用,因为如果您将最小值设置width得太高,那么它将无法在高频部分跟踪非常接近的峰值。如果设置width得太低,信号左侧会出现许多不需要的峰值。同样的问题distancethreshold仅与直接邻居比较,这在这里没有用。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()
于 2018-10-02T16:17:57.370 回答
49

我正在研究一个类似的问题,我发现一些最好的参考来自化学(来自质谱数据中的峰)。要全面了解峰值查找算法,请阅读内容。这是我遇到过的关于峰值发现技术的最清晰的评论之一。(小波最适合在噪声数据中找到此类峰值。)。

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

于 2009-12-17T17:06:46.203 回答
21

scipy 中有一个名为的函数scipy.signal.find_peaks_cwt听起来适合您的需求,但是我没有使用它的经验,所以我不能推荐..

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

于 2013-09-17T12:58:31.663 回答
18

对于那些不确定在 Python 中使用哪种寻峰算法的人,这里是替代方案的快速概述:https ://github.com/MonsieurV/py-findpeaks

想要自己与 MatLab 函数等效findpeaks,我发现 Marcos Duarte 的detect_peaks 函数是一个不错的选择。

很容易使用:

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))

这会给你:

检测峰值结果

于 2015-07-18T17:53:43.233 回答
6

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

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

于 2009-11-27T08:20:25.320 回答
3

要检测正峰和负峰,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')

峰值检测

于 2020-10-29T10:09:33.760 回答
2

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

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

于 2009-11-12T08:36:11.300 回答
2

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

于 2009-11-12T09:17:01.080 回答
1

首先,如果没有进一步的说明,“峰值”的定义是模糊的。例如,对于以下系列,您将 5-4-5 称为一峰还是二峰?

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

在这种情况下,您至少需要两个阈值: 1) 一个高阈值,仅高于该阈值才能将极值记录为峰值;2) 一个低阈值,使得由低于它的小值分隔的极值将成为两个峰值。

峰值检测是极值理论文献中深入研究的主题,也称为“极值去簇”。其典型应用包括根据环境变量的连续读数识别危险事件,例如分析风速以检测风暴事件。

于 2016-05-03T16:02:59.657 回答