How to fit a curve with an optional parameter in the funtion in python

1.2k views Asked by At

I want to fit some data using curve_fit. I have a function such as

def fitf(x,y,z=10):
    return x*y+z

How do I proceed if I want to pass the optional argument z? Right now I'm using a wrapper function around fift such as

def fitff(x,y):
    return fitf(x,y,z=50)

but I believe there has to be a better solution to control the optional parameter, which I haven't been able to find in curve_fit. Is there a clean way to do this?

EDIT

For example, in the MWE below the plot that comes out is the following, which indicates that curve_fit is actually optimizing the optional value z as well. Is this behavior expected?

enter image description here

from scipy.optimize import curve_fit
from matplotlib import pyplot as plt

def fitf(x,y,z=10):
    return x*y+z

array1=range(10)
array2=[ fitf(el,5., z=2) for el in array1 ]
print array1
print array2

a=curve_fit(fitf, array1, array2)[0]
print a[0]

array3=[ fitf(el, a[0], z=a[1]) for el in array1 ]
print array3

plt.plot(array1)
plt.plot(array2)
plt.plot(array3, 'o')
plt.show()
2

There are 2 answers

2
tom10 On

You can do this with a lambda or a closure. Here's a lambda:

import numpy as np
from scipy.optimize import curve_fit

def func(x, a, b, c=10):
    return a * np.exp(-b * x) + c

xdata = np.linspace(0, 4, 50)
y = func(xdata, 2.5, 1.3, 0.5)
ydata = y + 0.2 * np.random.normal(size=len(xdata))

popt, pcov = curve_fit(lambda x, a, b:func(x, a,b,c=1.), xdata, ydata)

A closure would look like:

def F(c0):
    def f(x, a, b):
        return a * np.exp(-b * x) + c0 
        # return func(x, a, b, c0)  # or use this, depending on what organizes your code best, etc (and I guess this might be a bit slower with the extra fcn call)
    return f

popt, pcov = curve_fit(F(1.), xdata, ydata)

which takes a few extra lines for defining the closure, but then the call to curve_fit is cleaner and more intuitive.


As pointed out by @TomCho partial won't work, "Apparently this is because partial functions cannot be inspected..."

0
olga On

You could try using global variable z and have a function to set it. Something like:

z=10 # defining initial value for z
def fitf(x,y):
 return x*y+z

And the function to set the global variable z:

def set_z(opt):
 global z
 z=opt

then, you can set z to a different number before using your curve_fit :)