在一维numpy数组中使用Numpy查找局部最大值/最小值
你可以build议从numpy / scipy模块function,可以在一维numpyarrays中find本地最大值/最小值? 显然最简单的方法是看看最近的邻居,但我想有一个公认的解决scheme,是numpy发行版的一部分。
如果您正在查找1d数组中比其邻居小的所有条目,则可以尝试
numpy.r_[True, a[1:] < a[:-1]] & numpy.r_[a[:-1] < a[1:], True]
你也可以在使用numpy.convolve()
这一步之前平滑你的数组。
我不认为这是一个专门的function。
在SciPy> = 0.11
import numpy as np from scipy.signal import argrelextrema x = np.random.random(12) # for local maxima argrelextrema(x, np.greater) # for local minima argrelextrema(x, np.less)
产生
>>> x array([ 0.56660112, 0.76309473, 0.69597908, 0.38260156, 0.24346445, 0.56021785, 0.24109326, 0.41884061, 0.35461957, 0.54398472, 0.59572658, 0.92377974]) >>> argrelextrema(x, np.greater) (array([1, 5, 7]),) >>> argrelextrema(x, np.less) (array([4, 6, 8]),)
请注意,这些是本地最大/最小值的x的指数。 要获取值,请尝试:
>>> x[argrelextrema(x, np.greater)[0]]
scipy.signal
还分别提供argrelmax
和argrelmin
以查找最大值和最小值。
对于噪音不太大的曲线,我推荐下面的小代码片段:
from numpy import * # example data with some peaks: x = linspace(0,4,1e3) data = .2*sin(10*x)+ exp(-abs(2-x)**2) # that's the line, you need: a = diff(sign(diff(data))).nonzero()[0] + 1 # local min+max b = (diff(sign(diff(data))) > 0).nonzero()[0] + 1 # local min c = (diff(sign(diff(data))) < 0).nonzero()[0] + 1 # local max # graphical output... from pylab import * plot(x,data) plot(x[b], data[b], "o", label="min") plot(x[c], data[c], "o", label="max") legend() show()
+1
很重要,因为diff
会减less原始索引号。
另一种方法(更多的话,更less的代码),可能有助于:
局部最大值和最小值的位置也是一阶导数过零点的位置。 find过零点通常比直接find局部最大值和最小值要容易得多。
不幸的是,一阶导数倾向于“放大”噪声,所以当原始数据中存在显着的噪声时,只有在原始数据应用了一定程度的平滑之后才使用一阶导数。
由于平滑是最简单的意义上的低通滤波器,所以平滑通常通过使用卷积内核来完成(而且很容易),并且“塑造”内核可以提供令人惊讶的特征保留/增强能力。 寻找一个最佳内核的过程可以通过各种手段自动化,但最好的方法可能是简单的powershell(寻找小内核足够快)。 一个好的内核会(如预期的)大量地扭曲原始数据,但不会影响感兴趣的峰值/谷值的位置。
幸运的是,通常可以通过简单的SWAG(“受过教育的猜测”)创build合适的内核。 平滑核的宽度应该比原始数据中最宽的预期“有趣”峰宽一些,并且其形状将类似于该峰(单尺度小波)。 对于平均值保持的内核(任何良好的平滑滤波器应该是什么),内核元素的总和应该精确等于1.00,并且内核应该关于其中心对称(意味着它将具有奇数个元素)。
给定一个最佳的平滑内核(或针对不同数据内容优化的less量内核),平滑程度成为卷积内核(“增益”)的缩放因子。
确定“正确”(最佳)平滑度(卷积核增益)甚至可以自动化:比较一阶导数数据的标准偏差与平滑数据的标准偏差。 两个标准偏差的比率如何随平滑度的变化而变化,用来预测有效的平滑值。 一些手动数据运行(真正具有代表性的)应该是所有需要的。
以上所有的在先解决scheme计算一阶导数,但是他们不把它当作一个统计测量,上述解决scheme也不试图执行特征保持/增强平滑(以帮助微妙的峰值“超越”噪声)。
最后,坏消息是:当噪声的特征看起来像真正的峰值(重叠带宽)时,发现“真实”的峰值将成为皇家的痛苦。 下一个更复杂的解决scheme通常是使用考虑相邻“真实”峰值之间的关系(例如峰值出现的最小或最大速率)的较长的卷积核(“更宽的核孔径”),或者使用倍数使用具有不同宽度的内核进行卷积传递(但是只有当内核速度更快时:这是一个基本的math事实,即按顺序执行的线性卷积总是可以一起卷积成单个卷积)。 但是,首先find一系列有用的内核(不同宽度)并将它们放在一起比直接在一个步骤中直接find最终内核要容易得多。
希望这提供了足够的信息让Google(也许是一个很好的统计文本)填补空白。 我真的希望我有时间提供一个工作的例子,或链接到一个。 如果有人遇到一个在线,请在这里发布!
更新:我不喜欢渐变,所以我发现使用numpy.diff
更可靠。 请让我知道,如果它做你想要的。
关于噪声的问题,math问题是如果我们想要看噪声,我们可以使用前面提到的类似convolve的东西来定位最大值/最小值。
import numpy as np from matplotlib import pyplot a=np.array([10.3,2,0.9,4,5,6,7,34,2,5,25,3,-26,-20,-29],dtype=np.float) gradients=np.diff(a) print gradients maxima_num=0 minima_num=0 max_locations=[] min_locations=[] count=0 for i in gradients[:-1]: count+=1 if ((cmp(i,0)>0) & (cmp(gradients[count],0)<0) & (i != gradients[count])): maxima_num+=1 max_locations.append(count) if ((cmp(i,0)<0) & (cmp(gradients[count],0)>0) & (i != gradients[count])): minima_num+=1 min_locations.append(count) turning_points = {'maxima_number':maxima_num,'minima_number':minima_num,'maxima_locations':max_locations,'minima_locations':min_locations} print turning_points pyplot.plot(a) pyplot.show()
为什么不使用Scipy的内置函数signal.find_peaks_cwt来完成这个工作?
from scipy import signal import numpy as np #generate junk data (numpy 1D arr) xs = np.arange(0, np.pi, 0.05) data = np.sin(xs) # maxima : use builtin function to find (max) peaks max_peakind = signal.find_peaks_cwt(data, np.arange(1,10)) #generate an inverse numpy 1D arr (in order to find minima) inv_data = 1./data # minima : use builtin function fo find (min) peaks (use inversed data) min_peakind = signal.find_peaks_cwt(inv_data, np.arange(1,10)) #show results print "maxima", data[max_peakind] print "minima", data[min_peakind]
结果:
maxima [ 0.9995736] minima [ 0.09146464]
问候
这些解决scheme都没有为我工作,因为我想在重复值的中心find高峰。 例如,在
ar = np.array([0,1,2,2,2,1,3,3,3,2,5,0])
答案应该是
array([ 3, 7, 10], dtype=int64)
我用循环做了这个。 我知道这不是超级干净,但它完成了工作。
def findLocalMaxima(ar): # find local maxima of array, including centers of repeating elements maxInd = np.zeros_like(ar) peakVar = -np.inf i = -1 while i < len(ar)-1: #for i in range(len(ar)): i += 1 if peakVar < ar[i]: peakVar = ar[i] for j in range(i,len(ar)): if peakVar < ar[j]: break elif peakVar == ar[j]: continue elif peakVar > ar[j]: peakInd = i + np.floor(abs(ij)/2) maxInd[peakInd.astype(int)] = 1 i = j break peakVar = ar[i] maxInd = np.where(maxInd)[0] return maxInd
import numpy as np x=np.array([6,3,5,2,1,4,9,7,8]) y=np.array([2,1,3,5,3,9,8,10,7]) sortId=np.argsort(x) x=x[sortId] y=y[sortId] minm = np.array([]) maxm = np.array([]) i = 0 while i < length-1: if i < length - 1: while i < length-1 and y[i+1] >= y[i]: i+=1 if i != 0 and i < length-1: maxm = np.append(maxm,i) i+=1 if i < length - 1: while i < length-1 and y[i+1] <= y[i]: i+=1 if i < length-1: minm = np.append(minm,i) i+=1 print minm print maxm
minm
和maxm
包含最小值和最大值的指数。 对于一个庞大的数据集,它会给出很多极大值/极小值,所以在这种情况下先平滑曲线,然后应用这个algorithm。