动态定义一个函数

时间:2011-11-19 19:44:10

标签: python numpy scipy

我正在尝试编写一个曲线拟合函数,它返回最佳参数a,b和c,这是一个简化的例子:

import numpy
import scipy
from scipy.optimize import curve_fit

def f(x, a, b, c):
    return x * 2*a + 4*b - 5*c

xdata = numpy.array([1,3,6,8,10])
ydata = numpy.array([  0.91589774,   4.91589774,  10.91589774,  14.91589774,  18.91589774])
popt, pcov = scipy.optimize.curve_fit(f, xdata, ydata)

这很好用,但我想让用户有机会提供一些(或没有)参数a,b或c,在这种情况下,它们应该被视为常量而不是估计。如何编写f以使其仅适合用户未提供的参数?

基本上,我需要使用正确的参数动态定义f。例如,如果用户知道a,则f变为:

def f(x, b, c):
    a = global_version_of_a
    return x * 2*a + 4*b - 5*c

5 个答案:

答案 0 :(得分:6)

collections.namedtuple playbook获取页面,您可以使用exec“动态”定义func

import numpy as np
import scipy.optimize as optimize
import textwrap

funcstr=textwrap.dedent('''\
def func(x, {p}):
    return x * 2*a + 4*b - 5*c
''')
def make_model(**kwargs):
    params=set(('a','b','c')).difference(kwargs.keys())
    exec funcstr.format(p=','.join(params)) in kwargs
    return kwargs['func']

func=make_model(a=3, b=1)

xdata = np.array([1,3,6,8,10])
ydata = np.array([  0.91589774,   4.91589774,  10.91589774,  14.91589774,  18.91589774])
popt, pcov = optimize.curve_fit(func, xdata, ydata)
print(popt)
# [ 5.49682045]

注意这一行

func=make_model(a=3, b=1)

您可以将任何您喜欢的参数传递给make_model。传递给make_model的参数将成为func中的固定常量。无论参数是什么,都会成为optimize.curve_fit试图适应的免费参数。

例如,上面,a = 3和b = 1成为func中的固定常数。实际上,exec语句将它们放在func的全局命名空间中。因此,func被定义为x和单个参数c的函数。请注意,popt的返回值是一个长度为1的数组,对应于剩余的自由参数c


关于textwrap.dedent:在上面的示例中,不需要调用textwrap.dedent。但是在“真实”脚本中,funcstr在函数内部或更深的缩进级别定义,textwrap.dedent允许您编写

def foo():
    funcstr=textwrap.dedent('''\
        def func(x, {p}):
            return x * 2*a + 4*b - 5*c
        ''')

而不是视觉上没有吸引力的

def foo():
    funcstr='''\
def func(x, {p}):
    return x * 2*a + 4*b - 5*c
'''

有些人喜欢

def foo():
    funcstr=(
        'def func(x, {p}):\n'
        '    return x * 2*a + 4*b - 5*c'
        )

但我发现分别引用每一行并添加显式的EOL字符有点麻烦。但它会为你保存一个函数调用。

答案 1 :(得分:1)

我通常会将lambda用于此目的。

user_b, user_c = get_user_vals()
opt_fun = lambda x, a: f(x, a, user_b, user_c)
popt, pcov = scipy.optimize.curve_fit(opt_fun, xdata, ydata)

答案 2 :(得分:0)

如果你想要一个基于curve_fit的简单解决方案,我建议你将你的函数包装在一个类中。最小的例子:

import numpy
from scipy.optimize import curve_fit

class FitModel(object):
    def f(self, x, a, b, c):
        return x * 2*a + 4*b - 5*c

    def f_a(self, x, b, c):
        return self.f(x, self.a, b, c)

# user supplies a = 1.0
fitModel = FitModel()
fitModel.a = 1.0

xdata = numpy.array([1,3,6,8,10])
ydata = numpy.array([  0.91589774,   4.91589774,  10.91589774,  14.91589774,  18.91589774])
initial = (1.0,2.0)
popt, pconv = curve_fit(fitModel.f_a, xdata, ydata, initial)

答案 3 :(得分:0)

已经有一个包可以做到这一点:

https://lmfit.github.io/lmfit-py/index.html

来自自述文件:

“LMfit-py提供了最小二乘最小化例程和类 使用简单,灵活的方法来参数化模型 适合数据。命名参数可以固定或自由保存 调整适合,或保持在下限和上限之间。在 另外,参数可以被约束为简单的数学 其他参数的表达。“

答案 4 :(得分:-2)

def f(x, a = 10, b = 15, c = 25):
    return x * 2*a + 4*b - 5*c

如果用户没有为相关参数提供参数,则会使用=符号右侧指定的任何参数:

例如为:
f(5, b = 20)将评估为return 5 * 2*10 + 4*20 - 5*25
f(7)将评估为return 7 * 2*10 + 4*15 - 5*25