Benjamin - 8 months ago 54

Python Question

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`

Basically, I need to define

`f`

`a`

`f`

`def f(x, b, c):`

a = global_version_of_a

return x * 2*a + 4*b - 5*c

Answer Source

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

instead of the visually unappealing

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