Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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
like image 948
Benjamin Avatar asked Nov 19 '11 19:11

Benjamin


People also ask

How do you dynamically define a function in Python?

Method 1: exec() It's the perfect way to dynamically create a function in Python! ? Python's built-in exec() executes the Python code you pass as a string or executable object argument. This is called dynamic execution because, in contrast to normal static Python code, you can generate code and execute it at runtime.

What is a dynamic function?

Dynamic function is a way of dynamically invoking a function call. The compiler will have limited knowledge of what you are up to so you will get run time errors if you don't use correct inputs and outputs. One example that runs different functions depending on user input: DEFINE VARIABLE iFunc AS INTEGER NO-UNDO.

What is a dynamic function in JavaScript?

The dynamic nature of JavaScript means that a function is able to not only call itself, but define itself, and even redefine itself. This is done by assigning an anonymous function to a variable that has the same name as the function.


2 Answers

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.

like image 195
unutbu Avatar answered Sep 28 '22 01:09

unutbu


I usually use a lambda for this purpose.

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)
like image 27
robince Avatar answered Sep 28 '22 01:09

robince