Python中如何仅拟合函数中的一个参数,而该函数有多个参数?

45

在Python中,我有一个带有很多参数的函数。我想将此函数拟合到数据集上,但只使用一个参数,其余参数我想自行提供。以下是一个示例:

def func(x,a,b):
   return a*x*x + b

for b in xrange(10):
   popt,pcov = curve_fit(func,x1,x2)

我希望只对a进行拟合,而参数b采用循环变量的值。如何实现?


你应该查看http://en.wikipedia.org/wiki/Curve_fitting。 - ninjagecko
1
有无限种方法来定义“拟合”曲线的含义,对于每种方法,实现它的方式也有很多种。你想要的拟合曲线类型通常取决于你试图解决的问题。假设你不在意,一种简单的方法称为最小二乘法,它最小化误差平方和。这里有一个预制库,可以计算“阻尼”最小二乘法的解决方案:http://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.curve_fit.html。虽然问题不完整,但建议关闭并重新开启一个关于曲线拟合的具体问题。 - ninjagecko
5
我不关心算法,我只会使用来自scipy.optimize的curve_fit。我无法理解的是,我应该在哪里指定一个参数应该取我的值,以及应该拟合哪个参数? - lovespeed
8
他的问题非常具体,目的明确。他并不是在询问曲线拟合的过程如何工作。 - PaulMag
我有另一个建议,可能更直观。 - azerila
7个回答

66

您可以使用lambda将func进行封装,如下所示:

def func(x, a, b):
   return a*x*x + b

for b in xrange(10):
   popt, pcov = curve_fit(lambda x, a: func(x, a, b), x1, x2)
一个lambda是一个匿名函数,Python中仅能用于简单的一行函数。通常情况下,它被用来减少代码量,当不需要为函数分配名称时。更详细的描述可以在官方文档中找到:http://docs.python.org/tutorial/controlflow.html#lambda-forms 在这种情况下,lambda被用于固定func其中一个参数。新创建的函数仅接受两个参数:xa,而b被固定为从本地变量b中获取的值。然后,将这个新函数作为参数传递给curve_fit函数。

1
请您详细说明一下,什么是 Lambda? - lovespeed
1
我已经在答案中添加了更多细节。 - Anton Beloglazov

6
更好的方法是使用lmfit,它提供了更高级别的曲线拟合接口。除了其他功能之外,Lmfit使拟合参数成为可以具有范围或显式固定的一级对象(等等)。
使用lmfit,这个问题可能被解决为:
from lmfit import Model
def func(x,a,b):
   return a*x*x + b

# create model
fmodel = Model(func)
# create parameters -- these are named from the function arguments --
# giving initial values
params = fmodel.make_params(a=1, b=0)

# fix b:
params['b'].vary = False

# fit parameters to data with various *static* values of b:
for b in range(10):
   params['b'].value = b
   result = fmodel.fit(ydata, params, x=x)
   print(": b=%f, a=%f+/-%f, chi-square=%f" % (b, result.params['a'].value, 
                                             result.params['a'].stderr,
                                             result.chisqr))

2

建议不要使用 lambda 函数,因为这可能不太直观。相反,我建议指定 scikit 的 curve_fit 参数 bounds,它可以强制在自定义边界内搜索参数。

你所需要做的就是让变量 a 在 -inf 和 +inf 之间移动,而变量 b 在 (b - epsilon) 和 (b + epsilon) 之间移动。

以你的示例为例:

epsilon = 0.00001

def func(x,a,b):
    return a*x*x + b

for b in xrange(10):
    popt,pcov = curve_fit(func,x1,x2, bounds=((-np.inf,b-epsilon), (np.inf,b+epsilon))

1
这会给人一种有两个变量的假象,但实际上目标是只有一个。我强烈建议不要采用这种方法。 - M Newville
没有幻觉。使用 Epsilon 的目的是回忆数学极限的定义,比导入新库更少侵入性。 - bobo32
3
在进行统计目的(如减少卡方)的变量数量分配时,变量数量是1还是2?如果您使用默认值curve_fit(..., absolute_sigma=False),则返回的协方差矩阵(即您的pcov)将为2x2,并且假定有2个变量进行重新缩放,即使只有1个实际变量。因此,a的不确定性将被错误地估计。如果b不是一个变量,请不要将它作为一个没有更改其值的自由度的变量。 - M Newville

1

我有效地使用Anton Beloglazov的解决方案,但为了可读性,我喜欢避免使用lambda函数,因此我采取以下做法:

def func(x,a,b):
   return a*x*x + b

def helper(x,a):
   return func(x,a,b)

for b in xrange(10):
   popt,pcov = curve_fit(helper, x1, x2)

这让人想起Rick Berg的回答,但我喜欢有一个专门处理问题“物理学”的函数和一个帮助函数来使代码正常运行。

0

Scipy的curve_fit函数需要三个位置参数:func、xdata和ydata。 因此,除了使用函数包装器的方法外,另一种方法是将'b'视为xdata(即自变量),通过构建一个矩阵来同时包含原始xdata(x1)和固定参数b的第二列。

假设x1和x2是数组:

def func(xdata,a):
   x, b = xdata[:,0], xdata[:,1]  # Extract your x and b
   return a*x*x + b

for b in xrange(10): 
   xdata = np.zeros((len(x1),2))  # initialize a matrix
   xdata[:,0] = x1  # your original x-data
   xdata[:,1] = b  # your fixed parameter
   popt,pcov = curve_fit(func,xdata,x2)  # x2 is your y-data

0
另一种方法是使用相同的上下限(+ eps)作为初始值。使用与初始条件和边界相同的示例:
def func(x,a,b):
   return a*x*x + b
# free for a and b
popt,pcov = curve_fit(func, x1, x2, 
                      p0=[1,1], 
                      bounds=[(-inf,-inf),(inf,inf)])

# free for a; fixed for b  ; 
eps=1/100
popt,pcov = curve_fit(func, x1, x2, 
                      p0=[1,1], 
                      bounds=[(-inf,(1-eps)),(inf,(1+eps))])

记得插入一个 epsilon,否则 a 和 b 必须相同。


小心使用这种方法。我见过它导致pcov值爆炸。 - undefined

0

如果您愿意/能够编辑原始函数,则有一个更简单的选项。

将您的函数重新定义为:

def func(x,a):
    return a*x*x + b

然后你可以将它简单地放在你的循环中作为参数 b:

for b in xrange(10):
   popt,pcov = curve_fit(func, x1, x2)

注意事项:为了使其正常工作,该函数需要在调用它的同一脚本中定义。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接