动态定义一个函数

Ben*_*min 7 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)
Run Code Online (Sandbox Code Playgroud)

这工作正常,但我想让用户有机会提供一些(或没有)参数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
Run Code Online (Sandbox Code Playgroud)

unu*_*tbu 8

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]
Run Code Online (Sandbox Code Playgroud)

注意这一行

func=make_model(a=3, b=1)
Run Code Online (Sandbox Code Playgroud)

您可以将任何您喜欢的参数传递给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
        ''')
Run Code Online (Sandbox Code Playgroud)

而不是视觉上没有吸引力

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

有些人更喜欢

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

但我发现分别引用每一行并添加明确的EOL字符有点麻烦.但它确实为您保存了一个函数调用.