Reputation: 379
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
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
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
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
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
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