Habi
Habi

Reputation: 1157

Wrong exponential fit with `scipy.optimize.curve_fit`

I am trying to exponentially fit some data, but cannot seem to coerce scipy.optimize.curve_fit to give me a pleasing result.

I have boiled down my code to the example below, including an empirically derived manual fit to the data.

import matplotlib.pylab as plt
import numpy
import scipy.stats

x = [25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
     43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
     61, 62, 63, 64]
y = [9, 9, 10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 14, 14,
     14, 14, 15, 15, 16, 16, 17, 17, 18, 19, 20, 21, 22, 24, 25, 27, 30, 31,
     32, 37, 37]

def fitting_function(x, a, b, c):
    return a * numpy.exp(b * x) + c

plt.plot(x, y, 'o', label='Original data')
OptimalValues, Covariance = scipy.optimize.curve_fit(fitting_function, x, y)
plt.plot(x, fitting_function(x, *OptimalValues), '-',
         label='Fitted Curve (%0.2e*e^%sx+%0.2e)' % (OptimalValues[0],
                                              OptimalValues[1],
                                              OptimalValues[2]))
ManualFit = [0.1 * numpy.exp(0.09 * i) + 8 for i in x]
plt.plot(x, ManualFit, '-', label='Manual Fit (%s*e^%sx+%s)' % (0.1, 0.09, 8))
plt.legend(loc='best')
plt.show()

Result of the code above

According to other answers here on Stack Overflow an obvious solution is to provide curve_fit with a reasonable initial guess. If I do that by putting the below lines at the relevant place my code (lines 16 and 17) the interpreter complains about a ValueError because it somehow tries to broadcast the guess to len(x) or len(y0 (i.e. operands could not be broadcast together with shapes (0) (40).

guess = (0.1, 0.1, 10)
OptimalValues, Covariance = scipy.optimize.curve_fit(fitting_function, x, y,
                                                     p0=guess)

How can I get scipy.optimize.curve_fit to give me a meaningful output in this case?

Upvotes: 0

Views: 1118

Answers (1)

tmdavison
tmdavison

Reputation: 69213

Turn your x and y lists into numpy arrays, and it will work fine. I found that to get a satisfactory fit, you do need to include your initial guess. So, this code:

import matplotlib.pylab as plt
import numpy
import scipy.stats

x = numpy.array([25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
     43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
     61, 62, 63, 64])
y = numpy.array([9, 9, 10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 14, 14,
     14, 14, 15, 15, 16, 16, 17, 17, 18, 19, 20, 21, 22, 24, 25, 27, 30, 31,
     32, 37, 37])

def fitting_function(x, a, b, c):
    return a * numpy.exp(b * x) + c

plt.plot(x, y, 'o', label='Original data')

guess =(0.1,0.1,10)
OptimalValues, Covariance = scipy.optimize.curve_fit(fitting_function, x, y,
                                                     p0=guess)

plt.plot(x, fitting_function(x, *OptimalValues), '-',
         label='Fitted Curve (%0.2e*e^%sx+%0.2e)' % (OptimalValues[0],
                                              OptimalValues[1],
                                              OptimalValues[2]))
ManualFit = [0.1 * numpy.exp(0.09 * i) + 8 for i in x]
plt.plot(x, ManualFit, '-', label='Manual Fit (%s*e^%sx+%s)' % (0.1, 0.09, 8))
plt.legend(loc='best')
plt.show()

Produces this plot:

enter image description here

Upvotes: 2

Related Questions