【问题标题】:Scipy curve_fit with variable and constant arguments带有变量和常量参数的 Scipy curve_fit
【发布时间】:2021-04-30 20:18:19
【问题描述】:

我正在尝试使用curve_fit 使用带有两个变量参数和一个常量的方程来拟合数据点。由于我想测试常量的不同值,所以我想避免将它写入函数。

Scipy 的例子,我尝试做:

def func(x, a, b, c):
    return a * np.exp(-b * x) + c

a_test = 3

popt, pcov = curve_fit(func, xdata, ydata, bounds=([a_test, 0, 0], [a_test, 1., 0.5]))

但我得到一个值错误,因为第一个参数的下限和上限相等。

有没有办法在不使用其他库(例如 lmfit)的情况下做到这一点?

【问题讨论】:

  • 如果是常量,为什么要写成func中的参数?
  • 好点,我已经修改了我的问题以解释我希望能够测试常量的不同值。
  • 请注意,当前接受的答案看起来是正确的,但实际上是不正确的,并且可能会导致(未被注意到的)不正确的结果。
  • 对于“如何在库 X 中做一些简单易行的事情,但不使用库 X”的问题,是否有类别或 SO 标志?就像,问题中的答案(“使用 lmfit”)不是吗?您将如何回答“如何在不使用任何 json 库的情况下稳健地读取和解释 JSON 文件?”之类的问题?

标签: python scipy curve-fitting


【解决方案1】:

我不知道之前的答案是什么,但我认为你应该这样做:

import numpy as np
from scipy.optimize import *

a_test = 3

def func(x, b, c, a=a_test ):
    return a * np.exp(-b * x) + c

xdata = range(11, 17)
ydata = [a_test * np.exp(-0.8 * x) + 0.2 for x in xdata]

popt, pcov = curve_fit(func, xdata, ydata, bounds=([0, 0], [1., 0.5]))

print(popt,pcov)

导致

popt = [0.79999036 0.19999999]
pcov = [[2.78472686e-11 4.44800146e-14]
 [4.44800146e-14 1.50965321e-16]]

【讨论】:

  • 我认为这失败了,因为a=a_test 在编译时被评估。所以一旦你改变了a_test,你还需要重新编译/评估函数。类似于在要列出的函数中设置 kwarg 的问题,例如a=[].
  • 通过在func 中的return 之前放置一个print(a) 函数来测试它:它将是3,无论您将a_test 更改为什么值,然后再进行一次拟合。
【解决方案2】:

编写一个函数,该函数返回一个函数,该函数用变量常量包装拟合函数(是的:这是三个函数):

def func(x, a, b, c):
    return a * np.exp(-b * x) + c

def wrapperfunc(a_test):
    def tempfunc(x, b, c, a=a_test):
        return func(x, a, b, c)
    return tempfunc

a_test = 3

xdata = range(11, 17)
ydata = [a_test * np.exp(-0.8 * x) + 0.2 for x in xdata]

# Note: run `wrapperfunc`, so that the actual fitting func (`tempfunc`) is returned
popt, pcov = curve_fit(wrapperfunc(a_test), xdata, ydata, bounds=([0, 0], [1., 0.5]))

print(popt, pcov)

a_test = 4

xdata = range(11, 17)
ydata = [a_test * np.exp(-0.8 * x) + 0.2 for x in xdata]

popt, pcov = curve_fit(wrapperfunc(a_test), xdata, ydata, bounds=([0, 0], [1., 0.5]))

print(popt, pcov)

【讨论】:

  • 感谢您的建议!它有效,但 Panda50 的解决方案也是如此。我确实尝试了您在函数中打印a 的建议,当我更改它时它返回了正确的值。
  • @ocns 我很确定这是不正确的(已接受的答案),因此您可能已经做了其他事情,例如第二次重新评估整个函数。无论如何,我希望没有根据你的结果做出重要的决定。
【解决方案3】:

为了清楚起见,正如原始问题提到的那样,使用lmfit 可以轻松完成此类事情。这样的事情会做:

import lmfit
import numpy as np
def func(x, a, b, c):
    return a * np.exp(-b * x) + c

model = lmfit.Model(func)
params = lmfit.Parameters() 
params.add('a', value=3, vary=False)
params.add('b', value=0.5, min=0, max=1)
params.add('c', value=0.25, min=0, max=1)

# test values for a, find one with lowest chi-square
best_result, best_a, best_chisqr = None, None, 1e199
for aval in np.linspace(0, 10, 21):
    params['a'].value = aval
    result = model.fit(ydata, params, x=xdata)
    if result.chisqr < best_chisqr:
       best_chisqr = result.chisqr
       best_result = result
       best_a = aval

print(f"### best value for a = {best_a:.2f}")
print(best_result.fit_report())

请注意,尝试通过设置值的下限和上限来冻结变量会导致统计分析中混淆问题中是否存在 2 个或 3 个变量。另一方面,此处所示的循环显然一次使用 2 个变量,但它确实忽略了 a 是一种可变参数。为了更加健壮,您可能希望允许 a 变化。

使用lmfit 的“负担”是必须运行“pip install lmfit”,这可能需要几秒钟的时间——甚至可能需要您阅读此消息的时间。

【讨论】:

    猜你喜欢
    • 2021-02-16
    • 2016-03-12
    • 2013-10-12
    • 2022-11-22
    • 2017-01-31
    • 2014-07-12
    • 2021-07-13
    • 1970-01-01
    • 2017-04-11
    相关资源
    最近更新 更多