【问题标题】:Find local maximums in numpy array在numpy数组中查找局部最大值
【发布时间】:2016-05-18 20:55:33
【问题描述】:

我希望在我拥有的一些高斯平滑数据中找到峰值。我查看了一些可用的峰值检测方法,但它们需要一个输入范围来搜索,我希望这比这更自动化。这些方法也适用于非平滑数据。由于我的数据已经平滑,我需要一种更简单的方法来检索峰值。我的原始数据和平滑数据如下图所示。

本质上,有没有一种 Python 方法可以从平滑数据数组中检索最大值,使得数组像

    a = [1,2,3,4,5,4,3,2,1,2,3,2,1,2,3,4,5,6,5,4,3,2,1]

会返回:

    r = [5,3,6]

【问题讨论】:

  • 图表中的数据与数组a 之间的差异非常明显。对于图中的数据。我倾向于使用中值绝对偏差之类的方法从数据和统计显着峰值的阈值中简单地减去平滑版本。
  • 类似问题here

标签: python arrays numpy distribution


【解决方案1】:

有一个内置函数argrelextrema 可以完成此任务:

import numpy as np
from scipy.signal import argrelextrema
    
a = np.array([1,2,3,4,5,4,3,2,1,2,3,2,1,2,3,4,5,6,5,4,3,2,1])

# determine the indices of the local maxima
max_ind = argrelextrema(a, np.greater)

# get the actual values using these indices
r = a[max_ind]  # array([5, 3, 6])

这将为您提供r 所需的输出。

从 SciPy 1.1 版开始,您还可以使用 find_peaks。以下是取自文档本身的两个示例。

使用height 参数,可以选择高于某个阈值的所有最大值(在此示例中,所有非负最大值;如果必须处理嘈杂的基线,这将非常有用;如果您想找到最小值,只需将您的输入乘以-1):

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

x = electrocardiogram()[2000:4000]
peaks, _ = find_peaks(x, height=0)
plt.plot(x)
plt.plot(peaks, x[peaks], "x")
plt.plot(np.zeros_like(x), "--", color="gray")
plt.show()

另一个非常有用的参数是distance,它定义了两个峰之间的最小距离:

peaks, _ = find_peaks(x, distance=150)
# difference between peaks is >= 150
print(np.diff(peaks))
# prints [186 180 177 171 177 169 167 164 158 162 172]

plt.plot(x)
plt.plot(peaks, x[peaks], "x")
plt.show()

【讨论】:

  • @NathanThomas:很高兴它有帮助!顺便说一句:如果您对最小值感兴趣,可以将 np.greater 替换为 np.less
【解决方案2】:
>> import numpy as np
>> from scipy.signal import argrelextrema
>> a = np.array([1,2,3,4,5,4,3,2,1,2,3,2,1,2,3,4,5,6,5,4,3,2,1])
>> argrelextrema(a, np.greater)
array([ 4, 10, 17]),)
>> a[argrelextrema(a, np.greater)]
array([5, 3, 6])

如果您的输入代表噪声分布,您可以尝试使用 NumPy 卷积函数 smoothing

【讨论】:

    【解决方案3】:

    如果您的原始数据有噪声,则最好使用统计方法,因为并非所有峰值都很重要。对于您的 a 数组,一种可能的解决方案是使用双微分:

    peaks = a[1:-1][np.diff(np.diff(a)) < 0]
    # peaks = array([5, 3, 6])
    

    【讨论】:

    • 我按照这篇文章 (stackoverflow.com/questions/25571260/…) 的一些建议做了一个通用的高斯滤波器来消除噪声。该数据来自光谱仪,因此我试图在带宽上找到峰值响应。我认为高斯滤波器会给出一个很好的近似值(它不需要是精确的峰值)
    • ...您是在寻找上图中红线还是绿线的峰值?
    • 我正在寻找红线的峰值。我应该说得更清楚。每个峰值都是一个带宽,因此我希望获得该频带的最大值。
    • 我认为这里的代码应该可以解决你的问题:gist.github.com/endolith/250860
    【解决方案4】:

    如果您可以排除数组边缘的最大值,您始终可以通过以下方式检查一个元素是否大于其每个相邻元素:

    import numpy as np
    array = np.array([1,2,3,4,5,4,3,2,1,2,3,2,1,2,3,4,5,6,5,4,3,2,1])
    # Check that it is bigger than either of it's neighbors exluding edges:
    max = (array[1:-1] > array[:-2]) & (array[1:-1] > array[2:])
    # Print these values
    print(array[1:-1][max])
    # Locations of the maxima
    print(np.arange(1, array.size-1)[max])
    

    【讨论】:

      猜你喜欢
      • 2018-08-10
      • 1970-01-01
      • 2019-04-27
      • 1970-01-01
      • 1970-01-01
      • 2017-07-17
      • 2014-05-15
      • 2012-08-27
      相关资源
      最近更新 更多