Benjamin - 1 year ago 91
Python Question

# Dynamically define a function

I am trying to write a curve fitting function which returns the optimal parameters a, b and c, here is a simplified example:

``````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)
``````

This works fine, but I want to give the user a chance to supply some (or none) of the parameters a, b or c, in which case they should be treated as constants and not estimated. How can I write
`f`
so that it fits only the parameters not supplied by the user?

Basically, I need to define
`f`
dynamically with the correct arguments. For instance if
`a`
was known by the user,
`f`
becomes:

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

Taking a page from the collections.namedtuple playbook, you can use exec to "dynamically" define `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]
``````

Note the line

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

You can pass whatever parameters you like to make_model. The parameters you pass to `make_model` become fixed constants in `func`. Whatever parameters remain become free parameters that `optimize.curve_fit` will try to fit.

For example, above, a=3 and b=1 become fixed constants in `func`. Actually, the `exec` statement places them in `func`'s global namespace. `func` is thus defined as a function of `x` and the single parameter `c`. Note the return value for `popt` is an array of length 1 corresponding to the remaining free parameter `c`.

Regarding `textwrap.dedent`: In the above example, the call to `textwrap.dedent` is unnecessary. But in a "real-life" script, where `funcstr` is defined inside a function or at a deeper indentation level, `textwrap.dedent` allows you to write

``````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
'''
``````

Some people prefer

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

but I find quoting each line separately and adding explicit EOL characters a bit onerous. It does save you a function call however.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download