无法在python上使用scipy将数据拟合到给定函数(Jupyterlab)

fzsnzjdm  于 2023-05-17  发布在  Python
关注(0)|答案(2)|浏览(168)

问题是,我需要将一些数据拟合到一个函数中(这是我的教授给的,所以基本上我不能改变它),但拟合得很差,我不明白为什么。我也试过lmfit方法,但没有工作太多。下面是我使用的代码和我用残差得到的拟合图像:Fit

x1 = np.array([200.  , 220.  , 240.  , 260.  , 280.  , 300.  , 320.  , 340.  ,
       360.  , 380.  , 390.  , 392.5 , 395.  , 397.5 , 400.  , 402.5 ,
       405.  , 407.5 , 410.  , 411.25, 412.5 , 413.75, 414.  , 415.25,
       416.5 , 417.75, 418.5 , 420.  , 422.5 , 425.  , 427.5 , 430.  ,
       432.5 , 435.  , 437.5 , 440.  , 450.  , 460.  , 480.  , 500.  ,
       510.  , 520.  , 540.  , 560.  , 570.  , 580.  , 600.  ])

y1 = np.array([0.00787402, 0.00904762, 0.01079365, 0.01269841, 0.01539683,
       0.01857143, 0.02321429, 0.0302    , 0.04303279, 0.06767896,
       0.09456265, 0.10338983, 0.11410579, 0.12545932, 0.14700855,
       0.16944444, 0.19692833, 0.22965779, 0.27434783, 0.28681818,
       0.32079208, 0.32562814, 0.32893401, 0.33061224, 0.32727273,
       0.31268293, 0.30236967, 0.28940092, 0.24274194, 0.20989011,
       0.18052805, 0.15531915, 0.13259053, 0.11627907, 0.10341463,
       0.07345133, 0.07455357, 0.06403509, 0.04163265, 0.03811475,
       0.02948207, 0.0272    , 0.02301587, 0.02027559, 0.01968254,
       0.01795276, 0.01672584])

σ_y1 = np.array([0.00021564, 0.00023222, 0.00025707, 0.00028599, 0.00032924,
       0.00038243, 0.00046297, 0.00058876, 0.0008021 , 0.00127062,
       0.00180749, 0.00196919, 0.00217276, 0.00239208, 0.00284333,
       0.00317296, 0.00368032, 0.00432658, 0.00507593, 0.00532676,
       0.0060111 , 0.00611128, 0.00618   , 0.00621495, 0.00607953,
       0.0057964 , 0.0056074 , 0.00536185, 0.00463023, 0.00401191,
       0.00347255, 0.00303842, 0.00250275, 0.00219007, 0.00196289,
       0.0014738 , 0.00139797, 0.00129955, 0.00077378, 0.00082702,
       0.00054713, 0.00053481, 0.00043752, 0.00041071, 0.0003831 ,
       0.00037095, 0.00033556])

# Primo test di fit con 4 parametri
def fitfunc1(x, A, R, L, C, Off): # le frequenze 'f' sono in kHz
    ω = 2*np.pi*x 
    δ = R/(2*L)
    Ω = 1/sqrt(L*C)
    return A*ω/sqrt(ω**4-2*ω**2*(Ω**2-2*δ**2)+Ω**4)

R_tot = 77.54
R_G = 48
L = 308*1E-6
C = 508*1E-9

p_init1 = [31454, R_tot-R_G, L, C] # valori iniziali di (A, R, L, C)

p_best1, cov1 = curve_fit(
    fitfunc1, x1, y1,          # assegno funzione di fit, ascisse e ordinate
    sigma=σ_y1,                     # assegno gli errori sulle ordinate
    p0=p_init1, bounds=(0, +np.inf)  # imposto i valori iniziali dei parametri e intervalli ammessi [0, +∞) 
)                                 

#Somma dei residui e deviazione standard a posteriori
res1 = (y1-fitfunc1(x1, *p_best1))
sum_res1 = sum(res1)
σ_post1 = ma.sqrt(sum_res1/(y1.size))

print("---------------------------")
print("Best fit values:")
print("---------------------------")
print("A = (%.5f +/- %.5f)   err_percentuale = %.5f" % (p_best1[0],sqrt(cov1[0,0]),sqrt(cov1[0,0])*100/p_best1[0]))
print("R = (%.5f +/- %.5f) Ω err_percentuale = %.5f" % (p_best1[1],sqrt(cov1[1,1]),sqrt(cov1[1,1])*100/p_best1[1]))
print("L = (%.5f +/- %.5f) μH err_percentuale = %.5f" % (p_best1[2]*1E+6,sqrt(cov1[2,2])*1E+6,sqrt(cov1[2,2])*100/p_best1[2]))
print("C = (%.5f +/- %.5f) F err_percentuale = %.5f" % (p_best1[3]*1E+12,sqrt(cov1[3,3])*1E+12,sqrt(cov1[3,3])*100/p_best1[3]))
print("---------------------------")

fig1 = plt.figure(1, figsize=(9,7))
frame1a=fig1.add_axes((.1,.3,.7,.5))

# Grafico del fit
plt.errorbar(x1, y1, yerr=σ_y1, fmt='.', label='dati', capsize = 4)
_pts = np.linspace(x1[0], x1[-1], 1000)
plt.plot(_pts, fitfunc1(_pts, *p_best1), label='fit')
plt.title("Amplificazione ai capi di R")
plt.xlabel("frequenza [kHz]")
plt.ylabel(r"A = $V_{out}/V_{in}$")
plt.legend()
plt.tick_params(bottom = False)
plt.grid()
frame1a.set_xticklabels([])

# Grafico dei residui
frame1b=fig1.add_axes((.1,.1,.7,.19))
plt.errorbar(x1, res1, yerr = σ_y1, fmt ='.', capsize = 4)
plt.xlabel("frequenza [kHz]")
plt.ylabel(r"$A_{mis}-A_{fit}$")
plt.grid()

#salvataggio della file nella cartella, dpi = risoluzione immagine, bbox_inches = risolve problema di dimensione
#plt.savefig("name_of_file", dpi = 200, bbox_inches = "tight")

我尝试了几种方法,比如删除参数的边界,但这给了我巨大的错误,重命名参数(以防它们与初始值的名称冲突),并改变初始值以使拟合更精确(然而,初始参数应该在我编写时固定,因为这些值是用特定设备测量的)。(R_tot-R_G)表示RLC电路的电阻,L是电感,C是电容。唯一的未知值是曲线的振幅A。感谢您的评分

bvjveswy

bvjveswy1#

一个简单的方法来获得非常好的近似参数:

现在,如果你想根据一些拟合标准(LMSE,LMSRE,LMAE或其他)获得更准确的结果,你可以使用上述参数的近似值作为初始值来开始非线性回归的迭代方法(使用Python或任何其他方便的软件)。

tag5nh1u

tag5nh1u2#

我认为你的剧本是一个很好的开始,但有两个独立的问题与您的适合。首先,我认为你会发现,对于一个单一的振荡器(和,只是你的数学函数),R,L和C将无法完全独立地确定-他们几乎完全相关。固定这些值中的一个将允许很好地确定其他两个。
其次,R、L和C的初始值远远不是最优值,拟合根本无法找到更好的解决方案。我认为最简单的看法是,你的初始值L太低了,低了1000倍。
初始值对于拟合始终很重要。一个相关的主题(或者第三个主题)是,您应该将R、L和C的下限都设置为0--这在物理上是合理的,但也避免了取负数平方根的问题。
使用lmfit(因为我是主要作者--我相信这可以用curve_fit完成,并给予几乎相同的结果),允许所有R,L和C以及A和Off(添加到您的函数中)的fit看起来像:

import numpy as np
import matplotlib.pyplot as plt
import lmfit

def fitfunc1(x, A, R, L, C, Off): # le frequenze 'f' sono in kHz
    ω = 2*np.pi*x
    δ = R/(2*L)
    Ω = 1/np.sqrt(L*C)
    return Off + A*ω/np.sqrt(ω**4-2*ω**2*(Ω**2 - 2*δ**2)+Ω**4)

model = lmfit.Model(fitfunc1)

params = model.make_params(A=40, R=30, L=0.30, C=0.5e-6, Off=0)
params['A'].min = 0
params['R'].min = 0
params['L'].min = 0
params['C'].min = 0
# params['L'].vary = False

init = model.eval(params, x=x1)

result = model.fit(y1, params, x=x1, weights=1/σ_y1)

print(result.fit_report())

# #Somma dei residui e deviazione standard a posteriori
res1 = y1 - result.best_fit

fig1 = plt.figure(1, figsize=(9,7))
frame1a=fig1.add_axes((.1,.3,.7,.5))
#
# Grafico del fit
plt.errorbar(x1, y1, yerr=σ_y1, fmt='.', label='dati', capsize = 4)
plt.plot(x1, result.best_fit, 'o', label='best fit')
_pts = np.linspace(x1[0], x1[-1], 1000)
plt.plot(_pts, result.eval(x=_pts), label='interpolated fit')
plt.title("Amplificazione ai capi di R")
plt.xlabel("frequenza [kHz]")
plt.ylabel(r"A = $V_{out}/V_{in}$")
plt.legend()
plt.tick_params(bottom = False)
plt.grid(color='#EEE')
frame1a.set_xticklabels([])
#
# ....

会给予一个合适的报告

[[Model]]
    Model(fitfunc1)
[[Fit Statistics]]
    # fitting method   = leastsq
    # function evals   = 65
    # data points      = 47
    # variables        = 5
    chi-square         = 400.284733
    reduced chi-square = 9.53058888
    Akaike info crit   = 110.675341
    Bayesian info crit = 119.926079
    R-squared          = -673.477539
[[Variables]]
    A:    30.9354753 +/- 0.67812562 (2.19%) (init = 40)
    R:    23.3162313 +/- 1003.45159 (4303.66%) (init = 30)
    L:    0.25082189 +/- 10.7859228 (4300.23%) (init = 0.3)
    C:    5.8633e-07 +/- 2.5214e-05 (4300.36%) (init = 5e-07)
    Off:  6.8886e-04 +/- 4.3907e-04 (63.74%) (init = 0)
[[Correlations]] (unreported correlations are < 0.100)
    C(L, C)   = -1.0000
    C(R, L)   = +1.0000
    C(R, C)   = -1.0000
    C(A, Off) = -0.8021
    C(A, R)   = +0.5709

显示R、L和C的巨大不确定性,因为它们的相关性都超过0.999。
简单地修复L(通过取消注解# params['L'].vary = False)将为chi-square给予具有相同值的拟合,并报告

[[Model]]
    Model(fitfunc1)
[[Fit Statistics]]
    # fitting method   = leastsq
    # function evals   = 26
    # data points      = 47
    # variables        = 4
    chi-square         = 400.284733
    reduced chi-square = 9.30894728
    Akaike info crit   = 108.675341
    Bayesian info crit = 116.075931
    R-squared          = -673.477539
[[Variables]]
    A:    30.9354748 +/- 0.55046897 (1.78%) (init = 40)
    R:    27.8877948 +/- 0.84442502 (3.03%) (init = 30)
    L:    0.3 (fixed)
    C:    4.9022e-07 +/- 5.3035e-10 (0.11%) (init = 5e-07)
    Off:  6.8887e-04 +/- 3.9717e-04 (57.66%) (init = 0)
[[Correlations]] (unreported correlations are < 0.100)
    C(A, R)   = +0.7666
    C(A, Off) = -0.7613
    C(R, Off) = -0.5502

FWIW,使用上述代码修改后的图看起来像这样

相关问题