【问题标题】:fit multiple gaussians to the data in python将多个高斯拟合到python中的数据
【发布时间】:2014-11-13 05:57:17
【问题描述】:

我只是想知道是否有一种简单的方法可以实现 10 个峰值的高斯/洛伦兹拟合并提取 fwhm 并确定 fwhm 在 x 值上的位置。复杂的方法是分离峰值并拟合数据并提取 fwhm。

数据是 [https://drive.google.com/file/d/0B6sUnnbyNGuOT2RZb2UwYXU4dlE/view?usp=sharing].

非常感谢任何建议。谢谢。

from scipy.optimize import curve_fit
import numpy as np
import matplotlib.pyplot as plt

data = np.loadtxt('data.txt', delimiter=',')
x, y = data

plt.plot(x,y)
plt.show()

def func(x, *params):
    y = np.zeros_like(x)
    print len(params)
    for i in range(0, len(params), 3):
        ctr = params[i]
        amp = params[i+1]
        wid = params[i+2]
        y = y + amp * np.exp( -((x - ctr)/wid)**2)



guess = [0, 60000, 80, 1000, 60000, 80]
for i in range(12):
    guess += [60+80*i, 46000, 25]


popt, pcov = curve_fit(func, x, y, p0=guess)
print popt
fit = func(x, *popt)

plt.plot(x, y)
plt.plot(x, fit , 'r-')
plt.show()



Traceback (most recent call last):
File "C:\Users\test.py", line 33, in <module>
popt, pcov = curve_fit(func, x, y, p0=guess)
File "C:\Python27\lib\site-packages\scipy\optimize\minpack.py", line 533, in curve_fit
res = leastsq(func, p0, args=args, full_output=1, **kw)
File "C:\Python27\lib\site-packages\scipy\optimize\minpack.py", line 368, in leastsq
shape, dtype = _check_func('leastsq', 'func', func, x0, args, n)
File "C:\Python27\lib\site-packages\scipy\optimize\minpack.py", line 19, in _check_func
res = atleast_1d(thefunc(*((x0[:numinputs],) + args)))
File "C:\Python27\lib\site-packages\scipy\optimize\minpack.py", line 444, in    _ general_function
return function(xdata, *params) - ydata
TypeError: unsupported operand type(s) for -: 'NoneType' and 'float'

【问题讨论】:

  • @LokeshA.R. fwhm 的通常含义是“半最大值全宽”。它可以方便地测量光谱峰的宽度。

标签: python numpy scipy


【解决方案1】:

这需要非线性拟合。 scipy 的 curve_fit 函数是一个很好的工具。

要使用curve_fit,我们需要一个模型函数,称为func,它将x 和我们的(猜测的)参数作为参数并返回y 的相应值。作为我们的模型,我们使用高斯和:

from scipy.optimize import curve_fit
import numpy as np

def func(x, *params):
    y = np.zeros_like(x)
    for i in range(0, len(params), 3):
        ctr = params[i]
        amp = params[i+1]
        wid = params[i+2]
        y = y + amp * np.exp( -((x - ctr)/wid)**2)
    return y

现在,让我们为参数创建一个初始猜测。这个猜测从x=0x=1,000 处的峰值开始,振幅为 60,000,电子折叠宽度为 80。然后,我们在x=60, 140, 220, ... 添加候选峰值,振幅为 46,000,宽度为 25:

guess = [0, 60000, 80, 1000, 60000, 80]
for i in range(12):
    guess += [60+80*i, 46000, 25]

现在,我们准备好进行拟合了:

popt, pcov = curve_fit(func, x, y, p0=guess)
fit = func(x, *popt)

要查看我们的表现如何,让我们绘制实际的y 值(黑色实线)和fit(红色虚线)与x

如您所见,合身性相当好。

完整的工作代码

from scipy.optimize import curve_fit
import numpy as np
import matplotlib.pyplot as plt

data = np.loadtxt('data.txt', delimiter=',')
x, y = data

plt.plot(x,y)
plt.show()

def func(x, *params):
    y = np.zeros_like(x)
    for i in range(0, len(params), 3):
        ctr = params[i]
        amp = params[i+1]
        wid = params[i+2]
        y = y + amp * np.exp( -((x - ctr)/wid)**2)
    return y

guess = [0, 60000, 80, 1000, 60000, 80]
for i in range(12):
    guess += [60+80*i, 46000, 25]   

popt, pcov = curve_fit(func, x, y, p0=guess)
print popt
fit = func(x, *popt)

plt.plot(x, y)
plt.plot(x, fit , 'r-')
plt.show()

【讨论】:

  • 非常感谢您提供的精彩代码!合身看起来几乎完美。我想知道我是否可以以某种方式缩小它。如果我添加像 y = (y + (scale*amp) * np.exp( -((x - ctr)/wid)**2) 这样的参数并说 scale = 0.98 是否有意义?如何为每个峰?
  • 不断收到此错误:文件“C:\Python27\lib\site-packages\scipy\optimize\minpack.py”,第 444 行,在 _general_function 返回函数(xdata,*params) - ydata TypeError: 不支持的操作数类型 -: 'NoneType' 和 'float'
  • 这可能表明ydataNone。请检查xy 是否已成功读入。
  • 请看上面的代码,它确实有一个有效的 x 和 y 值。 PLz让我知道诀窍在哪里..
  • @Rocky 我的错:我没有将func 的最后一行复制并粘贴到答案中。答案现在已更新为完整的工作代码。
【解决方案2】:

@john1024 的回答很好,但需要手动过程来生成初始猜测。这是一种自动化起始猜测的简单方法。将 john1024 的相关 3 行代码替换为以下内容:

    import scipy.signal
    i_pk = scipy.signal.find_peaks_cwt(y, widths=range(3,len(x)//Npks))
    DX = (np.max(x)-np.min(x))/float(Npks) # starting guess for component width
    guess = np.ravel([[x[i], y[i], DX] for i in i_pk]) # starting guess for (x, amp, width) for each component

【讨论】:

    【解决方案3】:

    恕我直言,始终建议在此类问题中绘制残差(数据 - 模型)。您还需要查看适合的 ChiSq。

    【讨论】:

    • 这条消息应该是评论而不是答案;)
    猜你喜欢
    • 1970-01-01
    • 2017-08-30
    • 2019-07-17
    • 1970-01-01
    • 2017-09-15
    • 1970-01-01
    • 2019-01-17
    • 2015-10-10
    • 1970-01-01
    相关资源
    最近更新 更多