I have two NumPy arrays x
and y
. When I try to fit my data using exponential function and curve_fit
(SciPy) with this simple code
#!/usr/bin/env python from pylab import * from scipy.optimize import curve_fit x = np.array([399.75, 989.25, 1578.75, 2168.25, 2757.75, 3347.25, 3936.75, 4526.25, 5115.75, 5705.25]) y = np.array([109,62,39,13,10,4,2,0,1,2]) def func(x, a, b, c, d): return a*np.exp(b-c*x)+d popt, pcov = curve_fit(func, x, y)
I get wrong coefficients popt
[a,b,c,d] = [1., 1., 1., 24.19999988]
What is the problem?
y = e(ax)*e(b) where a ,b are coefficients of that exponential equation. We would also use numpy. polyfit() method for fitting the curve. This function takes on three parameters x, y and the polynomial degree(n) returns coefficients of nth degree polynomial.
On the Curve Fitter tab, in the Data section, click Select Data. In the Select Fitting Data dialog box, select X Data and Y Data, or just Y Data against an index. Click the arrow in the Fit Type section to open the gallery, and click Exponential in the Regression Models group.
The SciPy API provides a 'curve_fit' function in its optimization library to fit the data with a given function. This method applies non-linear least squares to fit the data and extract the optimal parameters out of it.
First comment: since a*exp(b - c*x) = (a*exp(b))*exp(-c*x) = A*exp(-c*x)
, a
or b
is redundant. I'll drop b
and use:
def func(x, a, c, d): return a*np.exp(-c*x)+d
That isn't the main issue. The problem is simply that curve_fit
fails to converge to a solution to this problem when you use the default initial guess (which is all 1s). Check pcov
; you'll see that it is inf
. This is not surprising, because if c
is 1, most of the values of exp(-c*x)
underflow to 0:
In [32]: np.exp(-x) Out[32]: array([ 2.45912644e-174, 0.00000000e+000, 0.00000000e+000, 0.00000000e+000, 0.00000000e+000, 0.00000000e+000, 0.00000000e+000, 0.00000000e+000, 0.00000000e+000, 0.00000000e+000])
This suggests that c
should be small. A better initial guess is, say, p0 = (1, 1e-6, 1)
. Then I get:
In [36]: popt, pcov = curve_fit(func, x, y, p0=(1, 1e-6, 1)) In [37]: popt Out[37]: array([ 1.63561656e+02, 9.71142196e-04, -1.16854450e+00])
This looks reasonable:
In [42]: xx = np.linspace(300, 6000, 1000) In [43]: yy = func(xx, *popt) In [44]: plot(x, y, 'ko') Out[44]: [<matplotlib.lines.Line2D at 0x41c5ad0>] In [45]: plot(xx, yy) Out[45]: [<matplotlib.lines.Line2D at 0x41c5c10>]
Firstly I would recommend modifying your equation to a*np.exp(-c*(x-b))+d
, otherwise the exponential will always be centered on x=0
which may not always be the case. You also need to specify reasonable initial conditions (the 4th argument to curve_fit
specifies initial conditions for [a,b,c,d]
).
This code fits nicely:
from pylab import * from scipy.optimize import curve_fit x = np.array([399.75, 989.25, 1578.75, 2168.25, 2757.75, 3347.25, 3936.75, 4526.25, 5115.75, 5705.25]) y = np.array([109,62,39,13,10,4,2,0,1,2]) def func(x, a, b, c, d): return a*np.exp(-c*(x-b))+d popt, pcov = curve_fit(func, x, y, [100,400,0.001,0]) print popt plot(x,y) x=linspace(400,6000,10000) plot(x,func(x,*popt)) show()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With