小编典典

Python/SciPy 的寻峰算法

all

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

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

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

一维峰:

带峰值的 FFT输出

二维峰:

带圆圈峰值的氡变换输出

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

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

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

更新:

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

更新更新:

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


阅读 70

收藏
2022-08-16

共1个答案

小编典典

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()
2022-08-16