Trouble with curve fitting - lmfit won't produce proper fit to peak data

删除回忆录丶 提交于 2019-12-12 01:39:37

问题


I'm quite new to python and the lmfit model and having some trouble. I want to fit a peak function (something like Gaussian or Voigtian profil) to my experimental data, but it never gives me any good results. Its best fit is a linear function, which kind of describes the base line of my peak profile.

The x data for the fitting process are simply numbers running from 0 to 100. Here are my y data:

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])

I tried the following approaches with different model functions (Gaussian, Voigtian and 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()

The exact same code works very well for a profile test function, which I created, so I guess there is nothing wrong with it. But for the real measurement data, it always gives a linear function, no matter which profile model I choose. Here is an example:

>>> 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])

I used the following approach for another try, but here, it didn't fit something at all and I only got nan values back, although it works fine for my Gaussian test function:

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()

I tried to fit the data with origin and it definitely works (so the data are not 'unfitable'), but how can I do it with python properly? Do you no any other ways I can try or things I can initialise to make it work?


回答1:


A PseudoVoigt function (or Voigt or Gaussian or Lorentzian) goes to 0 at +/- infinity. Your data looks to go to ~1.0, with a dip around x=50.

You almost certainly want to add either a linear or constant component to the model. For a linear component, try:

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))

or for a constant, try:

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))

as a better model for this data.

Also, to get better initial values for the parameters, you might try:

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

so that the curve being used for initial values is more like a positive peak. Of course, the sign of the amplitude will be wrong, but its magnitude will be close, and the starting center and width will be close to correct. That should make the fit more robust.



来源:https://stackoverflow.com/questions/34362012/trouble-with-curve-fitting-lmfit-wont-produce-proper-fit-to-peak-data

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!