在图中显示lmfit模型中心的错误

huangapple go评论62阅读模式
英文:

Showing the error of lmfit-model center in plot

问题

我帮您翻译代码中的相关部分,只返回翻译好的内容,不包含其他内容。

# 创建一个包含伪Voigt模型的函数
def Voigt_app(x, y, a, b):
    
    psvo_mod = PseudoVoigtModel(prefix='psvo_')
    pars = psvo_mod.guess(y, x=x)
    
    con_mod = ConstantModel(prefix='con_')
    pars = pars.update(con_mod.make_params(c=(y[0]+y[1])/2))
    
    mod = psvo_mod + con_mod

    out = mod.fit(y, pars, x=x)
    
    # 获取拟合结果的详细信息
    details = out.fit_report(min_correl=0.25)
    
    return out, details

# 创建一个包含正弦模型的函数
def Sin_app(x, y):
    sin_mod = SineModel(prefix='sin_')
    pars = sin_mod.guess(y, x=x)
    
    con_mod = ConstantModel(prefix='con_')
    pars = pars.update(con_mod.make_params(c=np.mean(y)))
    
    mod = sin_mod + con_mod

    out = mod.fit(y, pars, x=x)
    
    # 获取拟合结果的详细信息
    details = out.fit_report(min_correl=0.25)
    
    return out, details

# 数据
data = np.asarray(
      [[14407.0 , 26989.0 , 31026.0 , 31194.0 , 31302.0 , 91996.0 , 112250.0 , 112204.0 , 113431.0 , 75097.0 , 55942.0 , 55826.0 , 56181.0 , 28266.0 , 14687.0],
       [12842.0 , 13275.0 , 13581.0 , 13943.0 , 14914.0 , 20463.0 , 21132.0 , 21457.0 , 23308.0 , 32017.0 , 30808.0 , 30927.0 , 30337.0 , 21025.0 , 15216.0],
       [15770.0 , 17677.0 , 19008.0 , 20911.0 , 25958.0 , 27984.0 , 28164.0 , 31161.0 , 33517.0 , 29430.0 , 28673.0 , 32725.0 , 28495.0 , 24527.0 , 25173.0],
       [16299.0 , 20067.0 , 25102.0 , 34968.0 , 37757.0 , 44871.0 , 51347.0 , 60154.0 , 54985.0 , 53383.0 , 45776.0 , 40836.0 , 30816.0 , 27922.0 , 26066.0]])
a, b = 1932, 1947
centers = []

colors = ['red', 'black', 'green', 'blue']
fig, ax = plt.subplots()
for i in range(4):
    ax.scatter(np.arange(15), data[i, :], label=f'data {i}', color=colors[i])
    out, details = Voigt_app(x, data[i, :], a, b)
    ax.plot(out.best_fit, label=f'fit {i}', color=colors[i])
    centers.append(out.values['psvo_center'])
ax.set_title('数据点和伪Voigt拟合')
ax.legend()

# 创建误差棒图
fig, ax = plt.subplots()
ax.errorbar(np.arange(4), centers, yerr=np.sqrt(out.covar[2, 2]), color='red', fmt='o', label='data')

out, details = Sin_app(np.arange(4), np.asarray(centers))
ax.plot(np.arange(4), out.best_fit, color='black', label='fit')
ax.set_title('峰值位置和正弦拟合')
ax.legend()

以上是将第二张图的散点图替换为带有伪Voigt峰位置置信区间的误差棒图的代码部分。

英文:

I'm working with fitting of data with the python lmfit package. I am looking at several peaks and fitting them with a pseudo-Voigt + constant model. I then extract the center position of the fitted peak and compile about a hundred of these peak positions. The hundred positions are then plotted and fitted with a sine-wave model, also with lmfit.
My issue, is that I am not able to find the information I need to show the uncertainty of the peak positions extracted from the pseudo-Voigt fitting. In many similar questions, I encounter the eval_uncertainty function also included in the lmfit package, but I am not able to use this to extract the confidence intervals of the center position, only uncertainties in the amplitude and sigma value of the peak.

The ideal result would look like the following image:
The ideal case I'm trying to achieve

And my current graph looks like this:
Current graph

Obviously I need to switch from a scatter plot to an errorbar plot, but I do not have any error-intervals to work with at the moment.

The following is the most simple workable "mini"-case I could construct, while still including my original fit-models. My original data is around 100 datapoints instead of four, and the sine-wave in this smaller case is therefore not "smooth", but this should not affect the method of introduction of errorbars.

import numpy as np
import matplotlib.pyplot as plt
from lmfit.models import PseudoVoigtModel, ConstantModel, SineModel
def Voigt_app(x,y,a,b):
psvo_mod = PseudoVoigtModel(prefix='psvo_')
pars = psvo_mod.guess(y,x=x)
con_mod = ConstantModel(prefix='con_')
pars = pars.update(con_mod.make_params(c = (y[0]+y[1])/2))
mod = psvo_mod + con_mod
out = mod.fit(y, pars, x=x)
details = out.fit_report(min_correl=0.25)
return out, details
def Sin_app(x,y):
sin_mod = SineModel(prefix='sin_')
pars = sin_mod.guess(y,x=x)
con_mod = ConstantModel(prefix='con_')
pars = pars.update(con_mod.make_params(c = np.mean(y)))
mod = sin_mod + con_mod
out = mod.fit(y,pars,x=x)
details = out.fit_report(min_correl=0.25)
return out, details
data = np.asarray(
[[14407.0 , 26989.0 , 31026.0 , 31194.0 , 31302.0 , 91996.0 , 112250.0 , 112204.0 , 113431.0 , 75097.0 , 55942.0 , 55826.0 , 56181.0 , 28266.0 , 14687.0],
[12842.0 , 13275.0 , 13581.0 , 13943.0 , 14914.0 , 20463.0 , 21132.0 , 21457.0 , 23308.0 , 32017.0 , 30808.0 , 30927.0 , 30337.0 , 21025.0 , 15216.0],
[15770.0 , 17677.0 , 19008.0 , 20911.0 , 25958.0 , 27984.0 , 28164.0 , 31161.0 , 33517.0 , 29430.0 , 28673.0 , 32725.0 , 28495.0 , 24527.0 , 25173.0],
[16299.0 , 20067.0 , 25102.0 , 34968.0 , 37757.0 , 44871.0 , 51347.0 , 60154.0 , 54985.0 , 53383.0 , 45776.0 , 40836.0 , 30816.0 , 27922.0 , 26066.0]])
a,b = 1932, 1947
centers = []
colors = ['red','black','green','blue']
fig, ax = plt.subplots()
for i in range(4):
ax.scatter(np.arange(15),data[i,:],label=f'data {i}',color=colors[i])
out, details = Voigt_app(x,data[i,:],a,b)
ax.plot(out.best_fit,label=f'fit {i}',color=colors[i])
centers.append(out.values['psvo_center'])
ax.set_title('Data points and pseudo-Voigt fits')
ax.legend()
fig, ax = plt.subplots()
ax.scatter(np.arange(4),centers,color='red',label='data')
out, details = Sin_app(np.arange(4),np.asarray(centers))
ax.plot(np.arange(4),out.best_fit,color='black',label='fit')
ax.set_title('Peak positions and sine-wave fit')
ax.legend()

From this I would like to switch out the scatter plot in the second graph with an errorbar plot with the confidence intervals of the pseudo-Voigt peak positions ("out.values['psvo_center']").

答案1

得分: 0

这是如何修改您的Voigt_app函数以实现此目标的示例:

def Voigt_app(x, y, a, b):
    
    psvo_mod = PseudoVoigtModel(prefix='psvo_')
    pars = psvo_mod.guess(y, x=x)
    
    con_mod = ConstantModel(prefix='con_')
    pars = pars.update(con_mod.make_params(c=(y[0]+y[1])/2))
    
    mod = psvo_mod + con_mod

    out = mod.fit(y, pars, x=x)
    
    # 计算拟合参数的置信区间
    ci = lmfit.conf_interval(out, out)
    print(lmfit.printfuncs.report_ci(ci))

    # 提取'psvo_center'的不确定性
    center_err = (ci['psvo_center'][2][1] - ci['psvo_center'][1][1]) / 2

    details = out.fit_report(min_correl=0.25)
    
    return out, details, center_err

然后,您可以在绘制误差条时使用这些不确定性:

center_errs = []
for i in range(4):
    out, details, center_err = Voigt_app(x, data[i, :], a, b)
    # ...
    center_errs.append(center_err)

# ...

ax.errorbar(np.arange(4), centers, yerr=center_errs, fmt='o', color='red', label='data')

请注意,这只是提供了如何修改您的代码以包括不确定性和绘制误差条的示例。具体的应用取决于您的数据和需求。

英文:

Here's how you could modify your Voigt_app function to do that:

def Voigt_app(x,y,a,b):
psvo_mod = PseudoVoigtModel(prefix='psvo_')
pars = psvo_mod.guess(y,x=x)
con_mod = ConstantModel(prefix='con_')
pars = pars.update(con_mod.make_params(c = (y[0]+y[1])/2))
mod = psvo_mod + con_mod
out = mod.fit(y, pars, x=x)
# Calculate the confidence interval for the fitted parameters
ci = lmfit.conf_interval(out, out)
print(lmfit.printfuncs.report_ci(ci))
# Extract the uncertainty for 'psvo_center'
center_err = (ci['psvo_center'][2][1] - ci['psvo_center'][1][1])/2
details = out.fit_report(min_correl=0.25)
return out, details, center_err

You can then use these uncertainties when plotting the error bars:

center_errs = []
for i in range(4):
out, details, center_err = Voigt_app(x, data[i,:], a, b)
# ...
center_errs.append(center_err)
# ...
ax.errorbar(np.arange(4), centers, yerr=center_errs, fmt='o', color='red', label='data')

答案2

得分: 0

如果我理解您的问题正确,您想要提取每个拟合的“psvo_center”参数的最佳拟合值,以及“psvo_center”的标准误差,然后将它们用作拟合正弦振荡的数据。

如果我理解正确的话,我认为您想要保存stderr属性,通过修改您的代码如下:

centers = []
stderrs = []  # < new

colors = ['red','black','green','blue']
fig, ax = plt.subplots()
for i in range(4):
    ax.scatter(np.arange(15),data[i,:],label=f'data {i}',color=colors[i])
    out, details = Voigt_app(np.arange(15), data[i,:], a, b)
    ax.plot(out.best_fit,label=f'fit {i}',color=colors[i])
    centers.append(out.params['psvo_center'].value)       # < new 
    stderrs.append(out.params['psvo_center'].stderr)      # < new

然后,您可以使用它们创建一个误差棒图:

ax.errorbar(np.arange(4), centers, stderrs, color='red',label='data')

您可能还想使用这些stderrs来加权拟合正弦函数,可以修改您的Sin_App如下:

def Sin_app(x, y, dely):       # < new
    sin_mod = SineModel(prefix='sin_')
    pars = sin_mod.guess(y,x=x)

    con_mod = ConstantModel(prefix='con_')
    pars = pars.update(con_mod.make_params(c = np.mean(y)))

    mod = sin_mod + con_mod

    out = mod.fit(y, pars, x=x, weights=1./dely)    # < new
    details = out.fit_report(min_correl=0.25)
    return out, details

然后调用:

out, details = Sin_app(np.arange(4), np.asarray(centers), np.asarray(stderrs))

对于您提供的4个示例数据集,标准误差的变化并不是很大,所以对拟合结果的影响不大。但是随着您添加更多数据集,这可能会变得更重要。在“生产环境”中使用之前,您可能想添加一个检查以确保stderr有效(例如,不是None且不是0)。

最后,您可能还想计算正弦函数的预测不确定性并绘制该范围。您的示例对于正弦拟合有4个变量和只有4个值,这在这种情况下会导致拟合非常完美,拟合的正弦函数具有非常小的不确定性。因此,为了说明这种方法,我在下面的拟合中固定了con_c参数。如果拟合超过4个峰值剖面的结果,这可能就不是必要的。但是,作为说明:

将您的Sin_app修改如下:

def Sin_app(x, y, dely):
    sin_mod = SineModel(prefix='sin_')
    pars = sin_mod.guess(y,x=x)

    con_mod = ConstantModel(prefix='con_')
    pars = pars.update(con_mod.make_params(c = np.mean(y)))

    pars['con_c'].vary = False  # 只有4个(x,y)值时需要

    mod = sin_mod + con_mod

    out = mod.fit(y, pars, x=x, weights=1./dely)
    details = out.fit_report(min_correl=0.25)

    delta_fit = out.eval_uncertainty(sigma=1)   # < new
    return out, details, delta_fit

然后使用以下代码绘制结果:

out, details, delta_fit = Sin_app(np.arange(4), np.asarray(centers), np.asarray(stderrs))

ax.plot(np.arange(4),out.best_fit, 'o', color='black',label='fit')

ax.fill_between(np.arange(4), 
                out.best_fit-delta_fit, out.best_fit+delta_fit,
                color="#D7D7D7")

ax.set_title('Peak positions and sine-wave fit')
ax.legend()

这将导致最终绘图如下:

在图中显示lmfit模型中心的错误

显示了峰值中心的“数据”带有误差棒,拟合值以及拟合的1σ范围。

英文:

If I understand your question correctly, you want to extract not only the best-fit value for the 'psvo_center' parameter for each of your fits, but also the standard error for 'psvo_center', and then use those as the data for fitting to a sinusoidal oscillation.

If that is correct, I think that what you want to do is to save the stderr attribute, by changing you code to

centers = []
stderrs = []  # < new
colors = ['red','black','green','blue']
fig, ax = plt.subplots()
for i in range(4):
ax.scatter(np.arange(15),data[i,:],label=f'data {i}',color=colors[i])
out, details = Voigt_app(np.arange(15), data[i,:], a, b)
ax.plot(out.best_fit,label=f'fit {i}',color=colors[i])
centers.append(out.params['psvo_center'].value)       # < new 
stderrs.append(out.params['psvo_center'].stderr)      # < new

You can then use that to make and error-bar plot with

ax.errorbar(np.arange(4), centers, stderrs, color='red',label='data')

You might also want to use those stderrs to weight the fit to the sinusoidal function, perhaps modifying your Sin_App to be


def Sin_app(x, y, dely):       # < new
sin_mod = SineModel(prefix='sin_')
pars = sin_mod.guess(y,x=x)
con_mod = ConstantModel(prefix='con_')
pars = pars.update(con_mod.make_params(c = np.mean(y)))
mod = sin_mod + con_mod
out = mod.fit(y, pars, x=x, weights=1./dely)    # < new
details = out.fit_report(min_correl=0.25)
return out, details

and calling that with

out, details = Sin_app(np.arange(4), np.asarray(centers), np.asarray(stderrs))

for the 4 example datasets you give, the variation in the standard-error isn't very big, so it does not make a big difference. But as you add more datasets, it might become more important. You might want to add a check that stderr is valid (like, not None and not 0) before using this "in production".

For "one more thing", you might also want to calculate the predicted uncertainty in the sinusoidal function and plot that range too. Your example has 4 variables for the sinusoidal fit and only 4 values, which results for this case in a near-perfect fit with absurdly small uncertainties on the fitted sine function. So to illustrate the approach, I fix the con_c parameter in the fit below. If fitting the results of more than 4 peak profiles, that should not be necessary. But, for illustration,
changing your Sin_app to be

def Sin_app(x, y, dely):
sin_mod = SineModel(prefix='sin_')
pars = sin_mod.guess(y,x=x)
con_mod = ConstantModel(prefix='con_')
pars = pars.update(con_mod.make_params(c = np.mean(y)))
pars['con_c'].vary = False  # needed with only 4 (x,y) values
mod = sin_mod + con_mod
out = mod.fit(y, pars, x=x, weights=1./dely)
details = out.fit_report(min_correl=0.25)
delta_fit = out.eval_uncertainty(sigma=1)   # < new
return out, details, delta_fit

and then plotting the results of using that with


out, details, delta_fit = Sin_app(np.arange(4), np.asarray(centers), np.asarray(stderrs))
ax.plot(np.arange(4),out.best_fit, 'o', color='black',label='fit')
ax.fill_between(np.arange(4), 
out.best_fit-delta_fit, out.best_fit+delta_fit,
color="#D7D7D7")
ax.set_title('Peak positions and sine-wave fit')
ax.legend()

This leads to the final plot of:

在图中显示lmfit模型中心的错误

showing the "data" for the peak centers with error bars, the fitted values, and the 1-sigma range of the fit.

huangapple
  • 本文由 发表于 2023年7月10日 21:20:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/76654169.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定