I have a set of data and I would like to fit an exponential curve by using python. I looked a couple of examples and I came up with the following piece of script.
from pylab import rc
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
from scipy import optimize
def model_func(t, A, K, C):
return A * np.exp(-K*t) + C
def fit_exp_nonlinear(t, y):
opt_parms, parm_cov = optimize.curve_fit(model_func, t, y, maxfev=10000)
A, K, C = opt_parms
return A, K, C
pi_1_3 = np.array([186774.67608906, 119480.98881671, 81320.7163753 ,
58031.00546565, 42990.04111078, 32826.01571713,
25700.16102843, 20548.41187945, 16726.83116009,
13828.25419449, 11587.30623606, 9825.34010814,
8419.5903399 , 7283.07702701, 6353.54595154,
5585.14190098, 4943.96579672, 4404.19434404,
3946.30532107, 3555.02206224, 3218.38622266,
2927.06387001, 2673.47535718, 2451.61460839,
2256.54734046, 2084.28211566, 1931.48280645,
1795.43700167, 1673.85361378, 1564.82468004,
1466.7273566 , 1378.20995936, 1298.09333835,
1225.40136541, 1159.27214181, 1098.96372522,
1043.84974141, 993.36880068, 947.04676203,
904.45522954, 865.23010028, 829.04001802,
795.60459963, 764.66375969, 735.99814339,
709.40108872, 684.69988262, 661.73223448,
640.35399121, 620.44025093, 601.87371066,
584.54929565, 568.37272058, 553.26381714,
539.14562691, 525.94472176, 513.59920745,
502.05319667, 491.25259209, 481.15219684,
471.70448455, 462.87002943, 454.61638734,
446.90526636, 439.70525636, 432.99373968,
426.73945924, 420.91972403, 415.50905545,
410.49218343, 405.84570284, 401.55105219,
397.59401523, 393.95737052, 390.62344145,
387.58306896, 384.81946995, 382.319658 ,
380.06982549, 378.05960051, 376.27758291,
374.70924213, 373.34528829, 372.17106384,
371.17691362, 370.34772485, 369.67316117,
369.13731143, 368.72679983, 368.42660804,
368.22202786, 368.09453347, 368.02778592,
368.00352008, 367.99947609])
pi_2_3 = np.array([ 0.96762688, 0.96645461, 0.96574696, 0.96541101, 0.96521801,
0.96519657, 0.96540386, 0.96556112, 0.96589707, 0.96634024,
0.96686919, 0.9675554 , 0.96833452, 0.96922802, 0.97023588,
0.97134382, 0.97257327, 0.97390279, 0.97534668, 0.97689778,
0.97855611, 0.98032166, 0.98219442, 0.98417441, 0.98626876,
0.98846319, 0.99076483, 0.9931737 , 0.99556112, 0.99834167,
1.00103645, 1.00386705, 1.00680486, 1.00985704, 1.012995 ,
1.01623302, 1.01956397, 1.022995 , 1.02651894, 1.03015726,
1.0338456 , 1.03764832, 1.04152966, 1.04549678, 1.04953538,
1.05365261, 1.05784132, 1.0621015 , 1.06641887, 1.07080057,
1.07523231, 1.07970693, 1.0842173 , 1.08877055, 1.09335239,
1.09794853, 1.10255897, 1.10716941, 1.11177269, 1.11636169,
1.12092924, 1.12546104, 1.12994282, 1.13437455, 1.13874196,
1.14303074, 1.14723374, 1.15132952, 1.15531094, 1.15917798,
1.16290922, 1.1664975 , 1.16992852, 1.17320229, 1.1763045 ,
1.17922087, 1.18195854, 1.18450322, 1.18686204, 1.18902073,
1.19097212, 1.19273052, 1.19429593, 1.19566833, 1.19686204,
1.19787706, 1.19872051, 1.19941387, 1.19997141, 1.20040029,
1.20070765, 1.20092924, 1.20108649, 1.20118656, 1.20125804])
A, K, C = fit_exp_nonlinear(pi_2_3,pi_1_3)
fit_y = model_func(pi_2_3, A, K, C)
print A, K, C
fig = plt.figure(num=None, figsize=(8,16), dpi=80, facecolor='w', edgecolor='k', linewidth= 2.0, frameon=True)
fig = plt.xlabel(r'$\pi_1$', fontsize=17)
fig = plt.ylabel(r'$\pi_2$', fontsize=17)
fig = plt.autoscale(enable=True, axis='both', tight=False)
#fig = plt.ylim(0, 5)
#fig = plt.xlim(-0.1, 5e+7)
fig = plt.plot(pi_2_3, pi_1_3, linewidth=1.5, color='g', linestyle = '--', marker='s', markeredgecolor='g', markerfacecolor='white', label='measured')
fig = plt.plot(pi_2_3, fit_y, linewidth=1.5, color='k', linestyle = '-', marker='', markeredgecolor='k', markerfacecolor='white', label='fitted')
fig=plt.legend()
figE = plt.show()
However, as can be seen, it seems that the A, K, and C, are not well estimated(?). Does any have a better way of fitting an exponential curve to these data?
Moreover, is there any library in python for non linear regression analysis?
Thanks a lot in advance,
Gio
You get a bad fit because of two reasons:
You can improve the numerical condition of the model my moving the constant A
into the exponent, exp(-K*(t - t_0)) + C
. The result you get this way is about as good as it gets with the exponential model. Fitting in log space won't help either (and is not really an option as long as you have the additive constant C
in your model).
Sometimes optimize.curve_fit
needs a little help making an initial guess for the parameters which are in the right ballpark. The guess is passed to curve_fit
by specifying the p0
parameter.
You might have some independent way to guess what A, K, C should be based on theory or experience. For example, if your -K*t
values were very large (and negative), you could estimate C
as the value of model_func
when t
is very large, since the A * np.exp(-K * t)
term should go to zero (assuming K
is not extremely small). In any case, the theory should inform you what values for A, K, C are reasonable. You can use those as your guess.
Here is a 2-pass method which seems to work without having any a priori guess. It first tries to fit the data against a partial_func
model which has no constant term. It then uses the A
and K
parameters from that fit as guesses for the curve_fit against model_func
:
def model_func(t, A, K, C):
return A * np.exp(-K * t) + C
def partial_func(t, A, K):
return A * np.exp(-K * t)
def fit_exp_nonlinear(t, y):
opt_parms, parm_cov = optimize.curve_fit(partial_func, t, y, maxfev=10000)
A, K = opt_parms
opt_parms, parm_cov = optimize.curve_fit(model_func, t, y, p0=(A, K, 0))
A, K, C = opt_parms
return A, K, C
yields
A, K, C = (2.3961062737821128e+73, 163.82812722558725, 338.80276054827715)
The fit is not great, but is perhaps as good as it can get given that the data does not look very close to any exponential.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.