Search code examples
pythonscipycurve-fittingexponentialdata-fitting

Using scipy curve_fit to fit exponential curve (fitted curve does match real curve)


I'm trying to fit an exponential curve using curve_fit (scipy.optimize) but the fitted curve looks nowhere like the real curve. Right now I'm using the following code:

X=[0.0, 9.0, 18.0, 27.0, 36.0, 45.0, 54.0]
Y=[0.090316199, -0.078157925, -0.350137315, -0.695193468, -1.106773689, -1.60467115, -2.196169408]

#plot Y against X
fig = plt.figure(num=None, figsize=(9, 7),facecolor='w', edgecolor='k')
ax=fig.add_subplot(111)
ax.scatter(X,Y)

#fit using curve_fit
popt, pcov = curve_fit(func, X, Y,maxfev=10000)

#compute Y_estiamted using fitted parameters 
Y_estimated=[popt[0]*np.exp(i+popt[1])+popt[2] for i in X]

#plot Y_estiamted against X
ax.scatter(X,Y_estimated, c='r')

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

The blue curve is the real curve and the red curve is the fitted curve.

enter image description here

As you can see the fitted red curve does not match the real blue curve at all. Any help would be appreciated!


Solution

  • I got a fairly good fit to an asymptotic exponential type of equation that has a single shape parameter and a small offset, "1.0 - pow(a, x) + b". Here is a graphical Python fitter using this equation with your data.

    plot

    import numpy, scipy, matplotlib
    import matplotlib.pyplot as plt
    from scipy.optimize import curve_fit
    
    # ignore warnings within curve_fit() routine
    import warnings
    warnings.filterwarnings("ignore")
    
    X=[0.0, 9.0, 18.0, 27.0, 36.0, 45.0, 54.0]
    Y=[0.090316199, -0.078157925, -0.350137315, -0.695193468, -1.106773689, -1.60467115, -2.196169408]
    
    # alias data to match previous example
    xData = numpy.array(X, dtype=float)
    yData = numpy.array(Y, dtype=float)
    
    def func(x, a, b): # Asymptotic Exponential A equation with offset from zunzun.com
        return 1.0 - numpy.power(a, x) + b
    
    # these are the same as the scipy defaults
    initialParameters = numpy.array([1.0, 1.0])
    
    # curve fit the test data
    fittedParameters, pcov = curve_fit(func, xData, yData, initialParameters)
    
    modelPredictions = func(xData, *fittedParameters) 
    
    absError = modelPredictions - yData
    
    SE = numpy.square(absError) # squared errors
    MSE = numpy.mean(SE) # mean squared errors
    RMSE = numpy.sqrt(MSE) # Root Mean Squared Error, RMSE
    Rsquared = 1.0 - (numpy.var(absError) / numpy.var(yData))
    
    print('Parameters:', fittedParameters)
    print('RMSE:', RMSE)
    print('R-squared:', Rsquared)
    
    print()
    
    
    ##########################################################
    # graphics output section
    def ModelAndScatterPlot(graphWidth, graphHeight):
        f = plt.figure(figsize=(graphWidth/100.0, graphHeight/100.0), dpi=100)
        axes = f.add_subplot(111)
    
        # first the raw data as a scatter plot
        axes.plot(xData, yData,  'D')
    
        # create data for the fitted equation plot
        xModel = numpy.linspace(min(xData), max(xData))
        yModel = func(xModel, *fittedParameters)
    
        # now the model as a line plot
        axes.plot(xModel, yModel)
    
        axes.set_xlabel('X Data') # X axis data label
        axes.set_ylabel('Y Data') # Y axis data label
    
        plt.show()
        plt.close('all') # clean up after using pyplot
    
    graphWidth = 800
    graphHeight = 600
    ModelAndScatterPlot(graphWidth, graphHeight)