曲线拟合的麻烦 - lmfit无法适应峰值数据

时间:2015-12-18 18:40:20

标签: python curve-fitting lmfit

我对python和lmfit模型很陌生并且遇到了一些麻烦。我想在我的实验数据中加入一个峰值函数(类似Gaussian或Voigtian profil),但它从来没有给我任何好的结果。它最适合的是线性函数,它描述了我的峰值曲线的基线。

拟合过程的x数据只是从0到100的数字。这是我的y数据:

array([ 0.99518284,  0.99449661,  0.99609029,  0.996     ,  0.994307  ,
    0.999693  ,  0.99826185,  0.99680361,  0.99474041,  0.99793228,
    0.99385553,  0.99869526,  1.00044695,  0.99625734,  0.99758916,
    0.99489842,  1.00032957,  0.9967088 ,  0.99655982,  0.99990068,
    0.99515576,  0.99665914,  0.99990068,  0.99595034,  0.99792777,
    0.9941851 ,  0.99458691,  0.99312415,  0.99815801,  0.99851919,
    0.99637472,  0.996     ,  0.99632957,  0.99185102,  0.99173363,
    0.9915395 ,  0.99038826,  0.9917246 ,  0.99315124,  0.98968397,
    0.99120993,  0.98981038,  0.9892009 ,  0.99009932,  0.98853725,
    0.98624379,  0.98620316,  0.9826772 ,  0.99204966,  0.98455982,
    0.99049661,  0.98591422,  0.98906546,  0.98664108,  0.98740858,
    0.99076298,  0.99046953,  0.99067269,  0.99255982,  0.99264108,
    0.99215801,  0.99990068,  0.9948623 ,  0.99616704,  0.99307449,
    0.99626637,  0.9934447 ,  0.99476749,  0.99636117,  0.99840181,
    0.9984921 ,  0.99782844,  0.99853273,  0.99575621,  0.9985553 ,
    0.99936343,  0.99643792,  0.99825734,  0.9964605 ,  0.99879007,
    1.00068172,  0.99580135,  0.99898871,  1.00069074,  0.99920993,
    0.9963702 ,  0.99591874,  0.99730023,  0.99765237,  0.99334537,
    0.99798194,  0.99770655,  0.99702935,  0.99716027,  0.99662754,
    0.99779684,  0.9967088 ,  0.99736343,  0.99786907,  0.9968623 ,
    0.99961174])

我尝试了以下不同模型函数的方法(Gaussian,Voigtian和PseudoVoigtian):

>>> from lmfit.models import PseudoVoigtModel
>>> mod = PseudoVoigtModel()
>>> pars = mod.guess(y, x=x)
>>> out = mod.fit(y, pars, x=x)
>>> print(out.fit_report(min_correl=0.25))
>>> out.plot()

完全相同的代码非常适用于我创建的配置文件测试功能,所以我猜它没有任何问题。但对于实际测量数据,无论我选择哪种轮廓模型,它总是提供线性函数。这是一个例子:

>>> out.best_fit
array([ 0.99410398,  0.99412124,  0.99413851,  0.99415577,  0.99417303,
    0.99419029,  0.99420755,  0.99422481,  0.99424207,  0.99425932,
    0.99427658,  0.99429383,  0.99431108,  0.99432833,  0.99434558,
    0.99436283,  0.99438007,  0.99439732,  0.99441456,  0.9944318 ,
    0.99444904,  0.99446628,  0.99448351,  0.99450075,  0.99451798,
    0.99453522,  0.99455245,  0.99456968,  0.99458691,  0.99460413,
    0.99462136,  0.99463858,  0.99465581,  0.99467303,  0.99469025,
    0.99470747,  0.99472468,  0.9947419 ,  0.99475912,  0.99477633,
    0.99479354,  0.99481075,  0.99482796,  0.99484517,  0.99486237,
    0.99487958,  0.99489678,  0.99491398,  0.99493118,  0.99494838,
    0.99496558,  0.99498278,  0.99499997,  0.99501716,  0.99503436,
    0.99505155,  0.99506874,  0.99508592,  0.99510311,  0.9951203 ,
    0.99513748,  0.99515466,  0.99517184,  0.99518902,  0.9952062 ,
    0.99522338,  0.99524055,  0.99525772,  0.9952749 ,  0.99529207,
    0.99530924,  0.9953264 ,  0.99534357,  0.99536074,  0.9953779 ,
    0.99539506,  0.99541222,  0.99542938,  0.99544654,  0.9954637 ,
    0.99548085,  0.99549801,  0.99551516,  0.99553231,  0.99554946,
    0.99556661,  0.99558376,  0.9956009 ,  0.99561805,  0.99563519,
    0.99565233,  0.99566947,  0.99568661,  0.99570375,  0.99572088,
    0.99573802,  0.99575515,  0.99577228,  0.99578941,  0.99580654,
    0.99582367])

我使用以下方法进行另一次尝试,但是在这里,它根本不适合某些东西而且我只返回了nan值,尽管它适用于我的高斯测试函数:

from lmfit.models import GaussianModel
from lmfit import Model
import numpy as np

def gaussian(x, amp, cen, wid):
    "1-d gaussian: gaussian(x, amp, cen, wid)"
    return (amp/(sqrt(2*pi)*wid)) * exp(-(x-cen)**2 /(2*wid**2))

gmod = Model(gaussian)

mod.set_param_hint('x', value=10)
mod.set_param_hint('cent', value=47)
mod.set_param_hint('wid', value=20)
mod.set_param_hint('amp', value=0.2)
pars = gmod.make_params()

out = gmod.fit(normedy, pars, x=x)
print(out.fit_report(min_correl=0.1))
plt.figure(5, figsize=(8,8))
out.plot_fit()

我试图将数据与原点相匹配并且它确实有效(因此数据不是'不合适'),但是如何正确地使用python呢?你没有其他任何我可以尝试的方法或者我可以初始化以使其有效的东西吗?

1 个答案:

答案 0 :(得分:1)

PseudoVoigt函数(或Voigt或Gaussian或Lorentzian)在+/-无穷大处变为0。您的数据看起来会达到~1.0,并且在x = 50附近下跌。

您几乎肯定希望为模型添加线性或常量组件。对于线性组件,请尝试:

mod = PseudoVoigtModel()
pars = mod.guess(y, x=x)
mod = mod + LinearModel()
pars.add('intercept', value=1, vary=True)
pars.add('slope', value=0, vary=True)
out = mod.fit(y, pars, x=x)
print(out.fit_report(min_correl=0.25))

或对于常数,请尝试:

mod = PseudoVoigtModel()
pars = mod.guess(y, x=x)
mod = mod + ConstantModel()
pars.add('c', value=1, vary=True)
out = mod.fit(y, pars, x=x)
print(out.fit_report(min_correl=0.25))

作为此数据的更好模型。

此外,要获得更好的参数初始值,您可以尝试:

mod = PseudoVoigtModel()
pars = mod.guess((1-y), x=x)    # Note '1-y'

使得用于初始值的曲线更像是正峰值。当然,幅度的符号将是错误的,但其幅度将接近,并且起始中心和宽度将接近正确。这应该使合身更加健壮。