Nisfa
Nisfa

Reputation: 379

How to pass parameter to fit function when using scipy.optimize.curve_fit

I am trying to fit some data that I have using scipy.optimize.curve_fit. My fit function is:

def fitfun(x, a):
    return np.exp(a*(x - b))

What i want is to define a as the fitting parameter, and b as a parameter that changes depending on the data I want to fit. This means that for one set of data I would want to fit the function: np.exp(a*(x - 10)) while for another set I would like to fit the function np.exp(a*(x - 20)). In principle, I would like the parameter b to be passed in as any value.

The way I am currently calling curve_fit is:

coeffs, coeffs_cov = curve_fit(fitfun, xdata, ydata)

But what I would like would be something like this:

b=10     
coeffs, coeffs_cov = curve_fit(fitfun(b), xdata, ydata)
b=20     
coeffs2, coeffs_cov2 = curve_fit(fitfun(b), xdata, ydata)

So that I get the coefficient a for both cases (b=10 and b=20).

I am new to python so I cannot make it work, even though I have tried to read the documentation. Any help would be greatly appreciated.

Upvotes: 5

Views: 12852

Answers (5)

Brad Day
Brad Day

Reputation: 400

One really easy way to do this would be to use the partial function from functools. In this case all you would have to do is the following. In this case b be would have to defined otherwise I believe scipy.optimize.curvefit would try to optimize b in addition to a

from functools import partial

def fitfun(x, a, b):
    return np.exp(a*(x - b))

fitfun10 = partial(fitfun, b=10)
coeffs, coeffs_cov = curve_fit(fitfun10, xdata, ydata)

fitfun20 = partial(fitfun, b=20)
coeffs2, coeffs_cov2 = curve_fit(fitfun20, xdata, ydata)

Upvotes: 2

MPA
MPA

Reputation: 2028

I don't know if this is the "proper" way of doing things, but I usually wrap my function in a class, so that I can access parameters from self. Your example would then look like:

class fitClass:

    def __init__(self):
        pass

    def fitfun(self, x, a):
        return np.exp(a*(x - self.b))

inst = fitClass()

inst.b = 10
coeffs, coeffs_cov = curve_fit(inst.fitfun, xdata, ydata)

inst.b = 20
coeffs, coeffs_cov = curve_fit(inst.fitfun, xdata, ydata)

This approach avoids using global parameters, which are generally considered evil.

Upvotes: 10

Siva-Sg
Siva-Sg

Reputation: 2821

UPDATE: Apologies for posting the untested code. As pointed out by @mr-t , the code indeed throws an error. It seems , the kwargs argument of the curve_fit function is to set the keywords arguments of leastsq and least_squares functions and not the keyword arguments of fit function itself.

In this case, in addition to answer proposed by others, another possible solution is to redefine the fit function to return the error and directly call the leastsq function which allows to pass the arguments.

def fitfun(a,x,y,b):
    return np.exp(a*(x - b)) - y


b=10    
leastsq(fitfun,x0=1,args=(xdata,ydata,b))

Upvotes: 1

M Newville
M Newville

Reputation: 7862

Let me also recommend lmfit (http://lmfit.github.io/lmfit-py/) and its Model class for this type of problem. Lmfit provides a higher-level abstraction for curve fitting and optimization problems.

With lmfit, each parameter in the model becomes an object that can be fixed, varied freely, or given upper and lower bounds without changing the fitting function. In addition, you can define multiple "independent variables" for any model.

That gives you two possible approaches. First, define parameters and fix b:

from lmfit import Model
def fitfun(x, a, b):
    return np.exp(a*(x - b))

# turn this model function into a Model:
mymodel = Model(fitfun)

# create parameters with initial values.  Note that parameters are 
# **named** according to the arguments of your model function:
params = mymodel.make_params(a=1, b=10)

# tell the 'b' parameter to not vary during the fit
params['b'].vary = False

# do fit
result = mymodel.fit(ydata, params, x=xdata)
print(result.fit_report())

The params is not changed in the fit (updated params are in result.params), so to fit another set of data, you could just do:

params['b'].value = 20  #  Note that vary is still False
result2 = mymodel.fit(ydata2, params, x=xdata2)

An alternative approach would be to define b as an independent variable:

mymodel = Model(fitfun, independent_vars=['x', 'b'])

params = mymodel.make_params(a=1)
result = model.fit(ydata, params, x=xdata, b=10)

Lmfit has many other nice features for curve-fitting including composing complex models and evaluation of confidence intervals.

Upvotes: 5

Mr. T
Mr. T

Reputation: 12410

You can define b as a global variable inside the fit function.

from scipy.optimize import curve_fit

def fitfun(x, a):
    global b
    return np.exp(a*(x - b))

xdata = np.arange(10)

#first sample data set
ydata = np.exp(2 * (xdata - 10))

b = 10
coeffs, coeffs_cov = curve_fit(fitfun, xdata, ydata)
print(coeffs)

#second sample data set
ydata = np.exp(5 * (xdata - 20))

b = 20
coeffs, coeffs_cov = curve_fit(fitfun, xdata, ydata)
print(coeffs)

Output:

[2.]
[5.]

Upvotes: 2

Related Questions