Search code examples
python-3.xmathphysicscurve-fittinglmfit

lmfit model for multi exponential decay


I am trying to perform a fitting for my data using the lmfit package. However I couldnt find any built-in model for a multi-exponential decay. I have tried to create my own function, and then to fit it. My code is the following:

import os
import time
import numpy as np
import pandas as pd
import lmfit
from lmfit.models import ExponentialModel, LinearModel
from lmfit import Model, Parameter, report_fit

def MultiExpDecay(tiempo,C1,tau1,C2,tau2):
    return C1*np.exp(-tiempo/tau1)+C2*np.exp(-tiempo/tau2)

def MultiExpDecay_fit():
    C1s = []
    C1s_error = []
    C2s = []
    C2s_error = []
    tau1s = []
    tau1s_error = []
    tau2s = []
    tau2s_error = []
    Fit_MultiExpDecays = []
    model = Model(MultiExpDecay, independent_vars=['tiempo'])
    for c in range(len(V_APD.columns)):
        xdat = tiempo.iloc[:, c]
        ydat = V_APD.iloc[:, c]
        pars = model.guess(ydat, x=xdat)
        fit = model.fit(ydat, pars, x=xdat)
        fit_values = model.eval(pars, x=xdat)
        Fit_MultiExpDecays.append(fit_values)
        for key in fit.params:
            if key == 'C1':
                #print(key, "=", out.params[key].value, "+/-", out.params[key].stderr)
                C1s.append(fit.params[key].value)
                C1s_error.append(fit.params[key].stderr)
            elif key == 'C2':
                C2s.append(fit.params[key].value)
                C2s_error.append(fit.params[key].stderr)
            elif key == 'tau1':
                tau1s.append(fit.params[key].value)
                tau1s_error.append(fit.params[key].stderr)
            elif key == 'tau2':
                tau2s.append(fit.params[key].value)
                tau2s_error.append(fit.params[key].stderr)
    Fit_MultiExpDecays = np.transpose(pd.DataFrame(Fit_MultiExpDecays, index=labels))
    C1 = np.transpose(pd.DataFrame(C1s, index = labels, columns = ['C1']))
    C2 = np.transpose(pd.DataFrame(C2s, index=labels, columns=['C2']))
    tau1 = np.transpose(pd.DataFrame(tau1s, index=labels, columns=['tau1']))
    tau2 = np.transpose(pd.DataFrame(tau2s, index=labels, columns=['tau2']))
    C1_error = np.transpose(pd.DataFrame(C1s_error, index = labels, columns=['C1 error']))
    C2_error = np.transpose(pd.DataFrame(C2s_error, index=labels, columns=['C2 error']))
    tau1_error = np.transpose(pd.DataFrame(tau1s_error, index=labels, columns=['tau1 error']))
    tau2_error = np.transpose(pd.DataFrame(tau2s_error, index=labels, columns=['tau2 error']))
    C1 = pd.concat([C1, C1_error])
    C2 = pd.concat([C2, C2_error])
    tau1 = pd.concat([tau1, tau1_error])
    tau2 = pd.concat([tau2, tau2_error])
    return C1, C2, tau1, tau2, Fit_MultiExpDecays

C1, C2, tau1, tau2, Fit_MultiExpDecays = MultiExpDEcay_fit():

An error is raised but cannot identify the problem.

File "C:\ProgramData\Anaconda3\Lib\site-packages\lmfit\model.py", line 737, in guess
    raise NotImplementedError(msg)
NotImplementedError: guess() not implemented for Model

Solution

  • You could also use two lmfit.ExponentialModel, as with

    import numpy as np
    from lmfit.models import ExponentialModel
    
    def dblexp( x, c1, l1, c2, l2 ):
        return c1 * np.exp( -x / l1 ) + c2 * np.exp( -x / l2 )
        
    xl = np.linspace(0, 10, 101)
    yl = dblexp(xl, 32.44, 0.81, 11.51, 9.22 ) + np.random.normal(0, 0.2, xl.size)
    
    mymodel = ExponentialModel(prefix='e1_') + ExponentialModel(prefix='e2_') 
    
    params = mymodel.make_params(e1_amplitude=10, e1_decay=10,
                                 e2_amplitude=25, e2_decay=0.50)
    
    result = mymodel.fit(yl, params, x=xl)
    print( result.fit_report() )
    

    For the actual question: As the exception says, Model does not know how to guess reasonable parameter values for an arbitrary, user-defined model function - the Model.guess() method raises this exception by default and must be overwritten by each subclass. lmfit provides this method for the provided submodels (including ExponentialModel), but these are each coded with an understanding of those particular models.

    Finally: fitting double-exponential decay is notoriously error-prone and difficult for least-squares approaches (or at least Levenberg-Marquardt implementations). For more realistic cases, I would recommend fitting the log of the data with the log of the double-exponential decay.