在一个频谱中找到峰值的位置,使用numpy。
在一个频谱中找到峰值的位置,使用numpy。
我有一个TOF谱图,我想使用Python(numpy)实现一个算法,找出谱图中的所有极大值,并返回对应的x值。
\n我在网上查找了下面报告的算法。
\n这里的假设是在最大值附近,前一个值和最大值之间的差异大于一个数DELTA。问题是,我的谱图由等间距的点组成,即使在最大值附近也是如此,所以DELTA从来没有超过过,函数peakdet返回一个空数组。\n您有任何想法如何解决这个问题吗?我真的很感谢您对代码的评论,因为我在Python方面还是新手。\n谢谢! \n
import sys from numpy import NaN, Inf, arange, isscalar, asarray, array def peakdet(v, delta, x = None): maxtab = [] mintab = [] if x is None: x = arange(len(v)) v = asarray(v) if len(v) != len(x): sys.exit('输入向量v和x的长度必须相等') if not isscalar(delta): sys.exit('输入参数delta必须是标量') if delta <= 0: sys.exit('输入参数delta必须是正数') mn, mx = Inf, -Inf mnpos, mxpos = NaN, NaN lookformax = True for i in arange(len(v)): this = v[i] if this > mx: mx = this mxpos = x[i] if this < mn: mn = this mnpos = x[i] if lookformax: if this < mx-delta: maxtab.append((mxpos, mx)) mn = this mnpos = x[i] lookformax = False else: if this > mn+delta: mintab.append((mnpos, mn)) mx = this mxpos = x[i] lookformax = True return array(maxtab), array(mintab)
\n下面显示了部分谱图。实际上,我有比这里显示的更多的峰值。\n
在查看了答案和建议之后,我决定提供一个我经常使用的解决方案,因为它简单直观且更易于调整。它使用滑动窗口,并计算当窗口沿x轴移动时,局部峰值作为最大值出现的次数。正如建议的那样,“局部最大值”没有普遍的定义,这意味着某些调整参数是不可避免的。该函数使用“窗口大小”和“频率”来微调结果。窗口大小以自变量(x)的数据点数来衡量,频率表示峰值检测的灵敏度(也表示为数据点数;频率值越低,产生的峰值越多,反之亦然)。主要函数如下所示:
def peak_finder(x0, y0, window_size, peak_threshold): # extend x, y using window size y = numpy.concatenate([y0, numpy.repeat(y0[-1], window_size)]) x = numpy.concatenate([x0, numpy.arange(x0[-1], x0[-1]+window_size)]) local_max = numpy.zeros(len(x0)) for ii in range(len(x0)): local_max[ii] = x[y[ii:(ii + window_size)].argmax() + ii] u, c = numpy.unique(local_max, return_counts=True) i_return = numpy.where(c>=peak_threshold)[0] return(list(zip(u[i_return], c[i_return])))
同时,以下代码片段用于生成下面显示的图像:
import numpy from matplotlib import pyplot def plot_case(axx, w_f): p = peak_finder(numpy.arange(0, len(Y)), -Y, w_f[0], w_f[1]) r = .9*min(Y)/10 axx.plot(Y) for ip in p: axx.text(ip[0], r + Y[int(ip[0])], int(ip[0]), rotation=90, horizontalalignment='center') yL = pyplot.gca().get_ylim() axx.set_ylim([1.15*min(Y), yL[1]]) axx.set_xlim([-50, 1100]) axx.set_title(f'window: {w_f[0]}, count: {w_f[1]}', loc='left', fontsize=10) return(None) window_frequency = {1:(15, 15), 2:(100, 100), 3:(100, 5)} f, ax = pyplot.subplots(1, 3, sharey='row', figsize=(9, 4), gridspec_kw = {'hspace':0, 'wspace':0, 'left':.08, 'right':.99, 'top':.93, 'bottom':.06}) for k, v in window_frequency.items(): plot_case(ax[k-1], v) pyplot.show()
三个案例展示了不同的参数值,分别为(从左到右):(1)峰值过多,(2)峰值过少,(3)峰值数量适中。为了生成Y数据,我使用了上面给出的函数,并从答案中添加了一些噪声。希望对一些人有用,但它的效率主要取决于实际的峰值形状和距离。
问题的出现原因是:需要在一个频谱中找到峰值的位置。
解决方法是使用SciPy库中的find_peaks函数。首先,导入所需的库和模块。然后,生成一个长度为1000的零数组Y。通过插入峰值函数到Y中,使其具有峰值。接下来,给Y添加噪声,使其变得有噪声。为了找到峰值,我们需要将Y乘以-1,并使用find_peaks函数来获取实际的峰值位置。最后,为了绘制图形,我们需要将Y乘以-1。通过调用plot函数,我们可以绘制Y和峰值的位置。
在find_peaks函数中,可以设置height参数来限制只找到高于某个阈值的峰值。通过设置distance参数,可以限制峰值之间的最小距离。
因此,我们可以使用以下代码来找到高于0.002阈值且峰值之间距离大于100的峰值位置:
peaks, _ = find_peaks(Y, height=0.002, distance=100)
原因:这篇文章主要是为了解决在给定的频谱中找到峰值位置的问题。作者使用了一种基于信号处理的方法来解决这个问题。
解决方法:作者首先使用了scipy库中的convolve函数来计算频谱的导数。然后,通过检查导数的正负号来确定信号的斜率变化情况。接下来,作者找到了所有负斜率的位置,并筛选出最终的峰值位置。最后,作者使用matplotlib库将找到的峰值位置在频谱上进行了可视化。
文章中还提到了一些优化的方法,如根据峰值的大小设置一个阈值来筛选峰值,以及根据峰值的分布情况动态调整阈值。作者还介绍了如何处理基线变化的情况,以及如何简化代码以提高效率。
通过使用信号处理的方法,作者成功地解决了在给定频谱中找到峰值位置的问题。他提供了一种简单而有效的方法,并给出了一些优化的建议。这个方法可以在实际应用中用来分析具有峰值的频谱数据。