简体   繁体   中英

Fitting an exponential curve to numerical data in python

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:

  1. Your model isn't a particular good fit for your data.
  2. The fit is numerically ill-conditioned.

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM