Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Exponential curve fitting in SciPy

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?

like image 946
drastega Avatar asked Jan 29 '14 02:01

drastega


People also ask

How do you fit an exponential data curve in Python?

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.

How do you fit an exponential curve to data?

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.

What is curve_fit SciPy?

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.


2 Answers

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>] 

fit plot

like image 52
Warren Weckesser Avatar answered Sep 18 '22 06:09

Warren Weckesser


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() 
like image 24
three_pineapples Avatar answered Sep 22 '22 06:09

three_pineapples