Fitting data with an exponential law

Viewed 55

I'd like to fit some data with an exponential function. I used scipy.optimize.curve_fit because I already used it for other fits. This time, there is an issue and I can't figure out what's wrong.

Here is what the data looks like when plotted : data.png

as you see it seems to follow an exponential law.

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

data = np.array([ 
    0.,  1.93468444,  3.69735865,  5.38185988,  6.02549022,
    6.69199075,  7.72316694,  8.08913061,  8.84570241,  8.69711608,
    8.80038144,  9.78951087,  9.68486674, 10.06175145, 10.44039495,
   10.0481156 ,  9.76656204,  9.88581457,  9.81805445, 10.42432252,
   10.41102239, 11.2911395 ,  9.64866184,  9.98072231, 10.83644694,
   10.24748571, 10.81333209, 10.75949899, 10.90367328, 10.42446764,
   10.51441017, 10.73047737, 10.8159758 , 10.51013538, 10.02862504,
    9.76352112, 10.64829309, 10.6293347 , 10.67752596, 10.34801542,
   10.53158576, 10.92883362, 10.67002314, 10.37015825, 10.74876349,
   10.12821343, 10.8974205 , 10.1591103 , 10.588377  , 11.92134556,
   10.309095  , 11.1174362 , 10.72654524, 10.60890374, 10.37456491,
   10.05935346, 11.21295863, 11.09013951, 10.60862773, 11.2558922 ,
   11.24660234, 10.35981557, 10.81284365, 10.96113067, 10.22716439,
    9.8394873 , 10.01892084, 10.38237311, 10.04920671, 10.87782442,
   10.42438756, 10.05614503, 10.5446946 ,  9.99974368, 10.76930547,
   10.22164072, 10.36942999, 10.89888302, 10.47035428, 10.58157374,
   11.12615892, 11.30866718, 10.33215937, 10.46723351, 10.54072701,
   11.45027197, 10.45895588, 10.34176601, 10.78405493, 10.43964778,
   10.34047484, 10.25099046, 11.05847515, 10.27408195, 10.27529163,
   10.16568845, 10.86451738, 10.73205291, 10.73300649, 10.49463959,
   10.03729782
])

t = np.linspace(0, 100, len(data)) #time array

def expo(x, a, b, c): #exponential function for fitting
   return a * np.exp(b * x) + c

fig1, ax1 = plt.subplots()
ax1.plot(t, data, ".", label="data")
coefs = curve_fit(expo, t, data)[0] # fitting
ax1.plot(t, expo(t, coefs[0], coefs[1], coefs[2]), "-", label="fit")
ax1.legend()
plt.show()

The problem is that curve_fit() returns very big or very small coefficients a,b and c while it should return something more like a = -10.5, b = -0.2, c = 10.5

3 Answers

The fitting process works by finding a local minimum of a loss function. If the problem is unconstrained, there may be several such local minima, each giving different values of parameters, and you may get a different one than the one that you are expecting.

If you have a guess what the parameters should be, you can provide it to narrow the search:

# with an initial guess for values of a, b, c 
coefs = curve_fit(expo, t, data, p0=[-10, -1, 10])[0]

The coefficients it produces are:

array([-10.48815244,  -0.2091102 ,  10.56699883])

Alternatively, you can specify bonds for the parameters:

# with lower and upper bounds for a, b, c
coefs = curve_fit(expo, t, data, bounds=([-20, -2, 0], [-10, 2, 20]))[0]

This gives the same results as above.

Probably a non-linear regression algorithm is implemented in your software.

"Guessed" initial values of the parameters are required to start the iterative process. If no initial value is provided by the user, some initial values are evaluated by the software. That is often a cause of failure because the computed initial values might be too far from the correct values.

Some good initial values can be found in using a linear regression method which doesn't requires initial values. See the calculus below.

enter image description here

The result is :

enter image description here

If the accuracy of the above result is not sufficient according to some specified criteria of fitting, a non-linear regression is necessary. In this case the above values of the parameters $a,b,c$ can be used as initial values to initiate the iterative calculus.

Note : The principle of the method which lineraizes the non-linear regression as shown above is explained in : https://fr.scribd.com/doc/14674814/Regressions-et-equations-integrales

Here is what i tried, used negative b in np.exp

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

>>>[-10.4881516    0.20911016  10.5669989 ]
Related