拟合复合函数组合参数的模块化(pythonic)方法

拟合复合函数组合参数的模块化(pythonic)方法,python,curve-fitting,model-fitting,lmfit,Python,Curve Fitting,Model Fitting,Lmfit,我的问题是关于拟合由不同参数函数组成的复杂模型的参数 更准确地说,我想描述一个复杂的实验。 实验产生了测量数据的一维数组data,其中每个条目对应于(一组)实验控制变量x 我现在建立了一个理论模型(实际上是多个模型,见下文)model(x,pars),它需要x和许多参数pars来预测数据。但是,并非所有参数都已知,我需要对它们进行拟合 此外,该模型的一些细节尚不确定。因此,我实际上有一系列多个模型,这些模型在某些部分非常相似,但模型的某些内部组件不同(但模型的很大一部分是相同的) 不幸的是,将一

我的问题是关于拟合由不同参数函数组成的复杂模型的参数

更准确地说,我想描述一个复杂的实验。 实验产生了测量数据的一维数组
data
,其中每个条目对应于(一组)实验控制变量
x

我现在建立了一个理论模型(实际上是多个模型,见下文)
model(x,pars)
,它需要
x
和许多参数
pars
来预测
数据。但是,并非所有参数都已知,我需要对它们进行拟合

此外,该模型的一些细节尚不确定。因此,我实际上有一系列多个模型,这些模型在某些部分非常相似,但模型的某些内部组件不同(但模型的很大一部分是相同的)

不幸的是,将一个组件切换到另一个组件可能会引入新的(未知)参数,即我们现在有
modelA(x,parsA)
modelB(x,parsB)
它们有不同的参数

基本上,该模型由函数
f(x,pars,vals_of_subfuncs)
组成,其中
x
是自变量,
pars
f
的一些显式参数,
vals_of_subfuncs
是对一些低级函数求值的结果,这些函数本身依赖于它们自己的参数(可能是他们自身较低级别功能的结果等) 显然,不可能有递归,并且在最低级别的函数不依赖于其他函数的值

这幅图最好地说明了这种情况:

自变量为
x
(蓝色),参数为
a、b、c、d
(红色),子函数的值以绿色箭头显示在表示函数的节点中

在(1)中,我们有一个最低级别的函数
G(x;(a,b);{}
,没有子函数,还有一个更高级别的函数
F(x;c;G(x;(a,b))
,其求值给出了模型结果,这取决于
x
pars=(a,b,c)

在(2)和(3)中,我们分别更改了模型的一个组件(
F->F'
)和(
G->G'
),这改变了最终模型的参数依赖性

现在,我正在寻找一种最具python/modular风格的方法来解决在这种情况下实现参数拟合的问题,而不必每次交换/更改模型的组件时都重新编写拟合函数,从而可能引入一个新参数

目前,我正在尝试使用
lmfit
来解决这个问题。我还考虑过可能尝试使用
sympy
来处理符号“参数”,但我不认为所有出现的函数都可以轻松编写为表达式,可以由
asteval
进行计算


有人知道一种自然的方法来处理这种情况吗?

既然你提出了
sympy
,我想你应该看看,它正是你在最后一段中要求的。有了
symfit
你可以编写符号表达式,然后用
scipy
,这将使你的工作变得非常简单你可以随意组合不同的子模型

让我使用
symfit
实现您的第二个示例:

from symfit import variables, parameters, Fit, Model

a, b, c = parameters('a, b, c')
x, G, F = variables('x, G, F')

model_dict = {
    G: a * x + b,
    F: b * G + c * x
}
model = Model(model_dict)
print(model.connectivity_mapping)
我选择了这些非常简单的函数,但您显然可以选择任何您想要的。要查看此模型是否与您的插图相匹配,以下是
connectivity\u mapping
打印的内容:

{F: {b, G, x, c}, G: {b, a, x}}
因此,您可以看到,这实际上是一个表示您所绘制内容的映射。(参数在每个集合中没有特定的顺序,但它们将按正确的顺序进行计算,例如,
g
before
F
)然后根据您的数据进行计算,只需执行以下操作即可

fit = Fit(model, x=xdata, F=Fdata)
fit_results = fit.execute()
就是这样!我希望这能更清楚地说明为什么我认为
symfit
确实适合您的用例。很抱歉,我之前无法澄清这一点,我仍在将此功能最终添加到API中,所以到目前为止,它只存在于开发分支中。但我刚刚发布了此功能和许多其他功能:)


免责声明:我是
symfit

的作者,我认为通过一个更具体的示例(即实际代码),这个问题肯定会得到改进。如果我理解正确,您有一个通用模型

def model_func(x, a, b, c, d):
     gresult = G(x, a, b, d)
     return F(x, b, c, gresult)
但是您还需要控制
d
b
是否真的是变量,以及
c
是否传递给
F
。这是否正确

如果这是正确的(或者至少抓住了精神),那么我认为您可以使用
lmfit
(免责声明:我是主要作者)将关键字参数添加到模型函数中,并将一些参数值设置为固定值

例如,您可以执行如下重新排列:

def G(x, a, b=None, d=None):
    if b is not None and d is None:
       return calc_g_without_d(x, a, b)
    return calc_g_with_d(x, a, d) 

def F(x, gresult, b, c=None):
    if c is None:
       return calc_f_without_c(x, gresult, b)
    return calc_f_with_c(x, gresult, b, c) 

def model_func(x, a, b, c, d, g_with_d=True, f_with_c=True):
     if g_with_d:
        gresult = G(x, a, d)
     else:
        gresult = G(x, a, b)
     if f_with_c:
         return F(x, gresult, b, c=c)
     else:
         return F(x, gresult, b)
现在,当您制作模型时,您可以用_c
覆盖默认值
f_和/或用_d
覆盖默认值
g_:

import lmfit
mymodel = lmfit.Model(model_func, f_with_c=False)
params = mymodel.make_params(a=100, b=0.2201, c=2.110, d=0)
然后使用
mymodel.eval()
评估模型,或者使用
mymodel.fit()
运行fit,并为关键字参数
f_with_c
和/或
g_with_d
传递显式值,如

test = mymodel.eval(params, x=np.linspace(-1, 1, 41), 
                    f_with_c=False, g_with_d=False)

我认为,按照您指定的方式,当
g_with_d=False
时,您希望确保
d
不是拟合中的变量,并且在某些情况下,您希望
b
在拟合中不发生变化。您可以使用

params['b'].vary = False
params['d'].vary = False

我可以想象你的实际问题比这稍微复杂一些,但我希望这能帮助你朝着正确的方向开始。

谢谢你的回答

我认为
lmfit
可能可以做我想做的事情,但我必须自己实现“模块化”。 我举的例子只是一个概念性的和模仿性的例子
params['b'].vary = False
params['d'].vary = False