Reputation: 67
I'm trying to fit a line to an upside down gaussian distribution using scipy.optimize.curve_fit. It works perfectly to fit a traditional gaussian, but wont fit a gaussian with the sign flipped, and instead will always output a straight line.
I've also tried constraining my gaussian function so that the variable 'a' is always negative but this does not solve the problem. Specifying -max(y) instead of max(y) also does not seem to help.
import scipy
import numpy as np
import matplotlib.pyplot as plt
import statsmodels.api as sm
from scipy import asarray as ar,exp
def fitdata(x, y):
mean = sum(x * y) / sum(y)
sigma = np.sqrt(sum(y * (x - mean) ** 2) / sum(y))
def guassianfunc(xVar, a, b, c):
return a * exp(-(xVar - b) ** 2 / (2 * c ** 2))
popt, _ = scipy.optimize.curve_fit(guassianfunc, x, y, p0=[max(y), mean, sigma])
return guassianfunc(np.arange(1, 6, 1), *popt)
x = np.array((1,2,3,4,5))
f, ((ax1, ax2)) = plt.subplots(2, sharex='col', sharey='row')
y = np.array((1, 2, 3, 2, 1))
ax1.plot(x, y, color='black')
ax1.plot(x, fitdata(x, y), linewidth=2, label='Fit')
y = np.array((3, 2, 1, 2, 3))
ax2.plot(x, y, color='black')
ax2.plot(x, fitdata(x, y), linewidth=2, label='Fit')
plt.legend()
Upvotes: 3
Views: 1518
Reputation: 45752
I think it's because your second line would require an offset parameter maybe? i.e. your guassianfunc
can make an upside down bell-curve provided it's below the x-axis. For example:
y = np.array((3, 2, 1, 2, 3))-4
ax2.plot(x, y, color='black')
ax2.plot(x, fitdata(x, y), linewidth=2, label='Fit')
Try adding in a 4th param like this
def fitdata(x, y):
mean = sum(x * y) / sum(y)
sigma = np.sqrt(sum(y * (x - mean) ** 2) / sum(y))
def guassianfunc(xVar, a, b, c, d):
return a * exp(-(xVar - b) ** 2 / (2 * c ** 2)) + d
popt, _ = scipy.optimize.curve_fit(guassianfunc, x, y, p0=[max(y), mean, sigma,0])
return guassianfunc(x, *popt)
This also reduces your error significantly.
Upvotes: 3