【问题标题】:Fitting two sets of data with two different model functions simultaneously giving a unique optimal parameter用两个不同的模型函数同时拟合两组数据,给出唯一的最优参数
【发布时间】:2018-09-04 02:39:16
【问题描述】:

我是 python 新手。使用来自scipy.optimizecurve_fit,我试图用两个不同的模型函数(每个模型函数(每个用于一组数据))同时拟合两组数据,并使用相同的参数进行优化。

这里是拟合代码的草图:

def f(x,a,b,c,d,e): 
   return some function

def g(x,a,b,c,d,e): 
   return some other function

a=1
b=2
c=3
d=4
e=5

guesspar=(a,b,c,d,e)
optimalparf, covf=opti.curve_fit(f,x,ydata1,guesspar,some sigma)

print optimalparf


guesspar=(a,b,c,d,e)
optimalparg, covg=opti.curve_fit(g,x,ydata2,guesspar,some sigma)

print optimalparg

其中guesspar是参数的初始值,optimalparf和optimalparg是我要搜索的最优值,ydata1和ydata2是两组数据,covf和covg是协方差矩阵。

现在,我的问题如下:我确实得到了guesspar 的两组不同的最佳值,这显然是错误的,因为整个图表的最佳值应该是相同的,也就是说,对于两个模型函数。 (除此之外,一组最优值在我感兴趣的上下文中是无稽之谈)。

我知道,我在这里编写的代码非常具有误导性。我会很感激一个提示,即如何用两个不同的函数拟合两组数据,同时拟合每组数据,从而产生一组独特的最佳参数。

PS: 这里是原始代码:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.pyplot import ion 
import time
import random as rd 
import scipy.optimize as opti 
import sys
from numpy import *

n0=1.5 

data =np.genfromtxt('some data') 
data=1000*data

pos=[]
for j in range(len(data)): 
pos.append(np.arcsin(np.sin(np.deg2rad(data[j,0]/1000))/1.5))

m1=[]
for j in range(len(data)): 
m1.append(data[j,1]) 

m1Sig=[]
for j in range(len(data)): 
m1Sig.append(data[j,2]) 

p1=[]
for j in range(len(data)): 
p1.append(data[j,3]) 

p1Sig=[]
for j in range(len(data)): 
p1Sig.append(data[j,4]) 

zero=[]
for j in range(len(data)): 
zero.append(data[j,5]) 

zeroSig=[]
for j in range(len(data)): 
zeroSig.append(data[j,6]) 

#define theta plot-range
thetaMin=-0.5 #[rad]
thetaMax=0.5 
thetaStep=1./635.
theta=np.arange(thetaMin,thetaMax,thetaStep) 
#define r plot-range
rMin=0.02 
rMax=0.09

comboY = np.append(m1, p1)

comboX = np.append(pos, pos)
comboTheta = np.append(theta, theta)

def rM1(theta,lam,d0,deltan,per,y0): 
return y0+((np.pi*deltan*d0)/(lam*np.cos(theta)))**2.*np.sin(np.sqrt(((np.pi*deltan*d0)/(lam*np.cos(theta)))**2.+((np.pi*d0*(-np.arcsin(lam/(2*per*n0))-theta))/per)**2.))**2./(((np.pi*deltan*d0)/(lam*np.cos(theta)))**2.+((np.pi*d0*(-np.arcsin(lam/(2*per*n0))-theta))/per)**2.) 


def rP1(theta,lam,d0,deltan,per,y0): 
return y0+((np.pi*deltan*d0)/(lam*np.cos(theta)))**2.*np.sin(np.sqrt(((np.pi*deltan*d0)/(lam*np.cos(theta)))**2.+((np.pi*d0*(np.arcsin(lam/(2*per*n0))-theta))/per)**2.))**2./(((np.pi*deltan*d0)/(lam*np.cos(theta)))**2.+((np.pi*d0*(np.arcsin(lam/(2*per*n0))-theta))/per)**2.)


def combinedFunction(comboData,lam,d0,deltan,per,y0):

result1 = rM1(theta,lam,d0,deltan,per,y0)
result2 = rP1(theta,lam,d0,deltan,per,y0)

return np.append(result1, result2)

lam1=0.633
d01=100.
deltan1=0.0005
per1=1. 
y01=0.02

m1Err=np.sqrt(m1)
p1Err=np.sqrt(p1)

comboErr=np.append(m1Err,p1Err)

startParam=[lam1, d01 ,deltan1, per1, y01]

popt, pcov = opti.curve_fit(combinedFunction, comboX, comboY, startParam) 
print popt 

lam,d0,deltan,per,y0 = popt

y_fit_1 = rM1(theta,lam,d0,deltan,per,y0) # first data set, first equation
y_fit_2 = rP1(theta,lam,d0,deltan,per,y0) # second data set, second equation

plt.plot(comboX, comboY, '.') # plot the raw data
plt.plot(pos, y_fit_1,'b') # plot the equation using the fitted parameters
plt.plot(pos, y_fit_2,'r') # plot the equation using the fitted parameters
plt.show()

print('lam,d0,deltan,per,y0:', popt)

【问题讨论】:

    标签: optimization scipy curve-fitting


    【解决方案1】:

    我认为这个例子会有所帮助,它有一个用于两个数据集和两个函数的共享参数,只需将所有参数共享,它应该是你需要的。

    import numpy as np
    import matplotlib
    import matplotlib.pyplot as plt
    from scipy.optimize import curve_fit
    
    y1 = np.array([ 16.00,  18.42,  20.84,  23.26])
    y2 = np.array([-20.00, -25.50, -31.00, -36.50, -42.00])
    comboY = np.append(y1, y2)
    
    x1 = np.array([5.0, 6.1, 7.2, 8.3])
    x2 = np.array([15.0, 16.1, 17.2, 18.3, 19.4])
    comboX = np.append(x1, x2)
    
    if len(y1) != len(x1):
        raise(Exception('Unequal x1 and y1 data length'))
    if len(y2) != len(x2):
        raise(Exception('Unequal x2 and y2 data length'))
    
    
    def function1(data, a, b, c): # not all parameters are used here, c is shared
            return a * data + c
    
    def function2(data, a, b, c): # not all parameters are used here, c is shared
            return b * data + c
    
    
    def combinedFunction(comboData, a, b, c):
        # single data reference passed in, extract separate data
        extract1 = comboData[:len(x1)] # first data
        extract2 = comboData[len(x1):] # second data
    
        result1 = function1(extract1, a, b, c)
        result2 = function2(extract2, a, b, c)
    
        return np.append(result1, result2)
    
    
    # some initial parameter values
    initialParameters = np.array([1.0, 1.0, 1.0])
    
    # curve fit the combined data to the combined function
    fittedParameters, pcov = curve_fit(combinedFunction, comboX, comboY, initialParameters)
    
    # values for display of fitted function
    a, b, c = fittedParameters
    
    y_fit_1 = function1(x1, a, b, c) # first data set, first equation
    y_fit_2 = function2(x2, a, b, c) # second data set, second equation
    
    plt.plot(comboX, comboY, 'D') # plot the raw data
    plt.plot(x1, y_fit_1) # plot the equation using the fitted parameters
    plt.plot(x2, y_fit_2) # plot the equation using the fitted parameters
    plt.show()
    
    print('a, b, c:', fittedParameters)
    

    【讨论】:

    • 谢谢!很好的例子!但是,它在我的情况下不起作用。我实际上想拟合两个相同但移位的函数,即区间(1,4)上的高斯钟和区间(7,10)上的另一个高斯钟。顺便说一句,两个铃铛的尾巴都定义在整个 x 轴上。我修改了你的代码,但是没有用。我得到了一条直线作为拟合曲线。有什么想法吗?
    • 你给出了区间,你能发布一个数据链接并给出两个函数吗?我的猜测是启动参数...
    • 我应该提到 scipy.optimize 模块有一个遗传算法,可用于确定 curve_fit() 的初始参数估计。我有一个使用这个 scipy 模块来查找初始参数估计的例子,用于将双洛伦兹方程拟合到碳纳米管的拉曼光谱bitbucket.org/zunzuncode/ramanspectroscopyfit
    • 请原谅我监督您的 cmets!我编辑了 OP,以便您可以查看我尝试执行的操作。请看PS。
    【解决方案2】:

    如果您不介意使用包装scipy 的附加包,那么我编写的symfit 包将允许您非常简单地执行此操作。示例代码:

    from symfit import variables, parameters, Fit
    
    # These are your datasets
    xdata = np.array([...])
    ydata = np.array([...])
    zdata = np.array([...])
    
    a, b, c, d, e = parameters('a, b, c, d, e')
    x, y, z = variables('x, y, z')
    
    model_dict = {
        y: a * x + b * x**2 + ...,
        z: a / x + b / x**2 + ...
    }
    
    fit = Fit(model_dict, x=xdata, y=ydata, z=zdata)
    fit_result = fit.execute()
    print(fit_result)
    

    就是这样!我在这里假设两个数据集共享相同的x-axis,但即使这样也不一定是这样。现在将优化参数以给出该方程组的最优解。

    有关更多信息,您可以找到文档here

    【讨论】:

    • symfit 中使用什么方法来确定初始参数估计值?
    • @JamesPhillips 默认情况下,它们始终设置为 1。为了更改它,您可以在调用 fit.execute() 之前设置例如 a.value = 5.0。您还可以通过将a.min 和/或a.max 设置为某个值来设置范围。如果您需要更智能的东西,您也可以先运行全局最小化器,例如 here 所述,但这在计算上(很多)成本更高,因此根据您的问题可能会过度杀伤。
    • 感谢您的代码!我有问题。当我根据您的方法运行代码时,出现以下错误:AttributeError: 'Variable' object has no attribute 'cos',因为我的代码中有“cos”函数。知道为什么会出现此错误吗?
    • 我认为您在定义model_dict 时可能在代码中使用了numpy.cos?请注意,模型必须是完全符号的,而不是数字的。所以你应该从symfit 导入cos 和类似的函数。例如。 from symfit import cos, sin, exp 等。祝你好运,如果你有其他问题或者它是否有效,请告诉我,总是很高兴得到反馈;)。
    • 谢谢!我已经注意到我应该使用from symfit import cos, pi, asin 并删除numpy.cos。但是,我仍然得到同样的错误。不知道为什么!
    猜你喜欢
    • 2019-05-22
    • 1970-01-01
    • 1970-01-01
    • 2016-04-18
    • 2016-09-16
    • 2022-01-22
    • 2021-03-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多