问题
I am trying to fit a gaussian to my data which is taken in a pretty narrow spectral window. We got about 2 points of continuum and then about 10-11 that are part of the line. It should still be possible to fit it I think, but the curve fit is failing each time, and I am not sure why.
When running I get RuntimeError: Optimal parameters not found: Number of calls to function has reached maxfev = 800.
Code and data:
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
import numpy as np
x = np.arange(13)
xx = np.arange(130)/13.
y = np.array([19699.959 , 21679.445 , 21143.195 , 20602.875 , 16246.769 ,
11635.25 , 8602.465 , 7035.493 , 6697.0337, 6510.092 ,
7717.772 , 12270.446 , 16807.81 ])
# weighted arithmetic mean (corrected - check the section below)
mean = sum(x * y) / sum(y)
sigma = np.sqrt(sum(y * (x - mean)**2) / sum(y))
def Gauss(x, a, x0, sigma):
return a * np.exp(-(x - x0)**2 / (2 * sigma**2))
popt,pcov = curve_fit(Gauss, x, y, p0=[max(y), mean, sigma])
plt.plot(x, y, 'b+:', label='data')
plt.plot(xx, Gauss(xx, *popt), 'r-', label='fit')
plt.legend()
plt.show()
回答1:
As the error says, the procedure to find the optimal values doesn't converge. If you really think that what you have can be fitted with a Gaussian curve, then this in general means you have a bad starting point.
How you're giving the starting point could have been an issue, particularly with how you provide sigma given that at positions 11, 12 and 13 you have what could be the onset of another signal. Anyhow, that's not the biggest issue this time, but the fact that you forgot to add an offset to the gaussian function
# ----> new parameter in signature
# |
def Gauss(x, y0, a, x0, sigma):
return y0 + a * np.exp(-(x - x0)**2 / (2 * sigma**2))
# |
# -------> adding and offset
Then, you can decide how to provide a starting point for the offset, but by eye, I did set 5000
popt, pcov = curve_fit(Gauss, x, y, p0=[5000, max(y), mean, sigma])
Doing that, I get a fit. But, due to the last three data points, it's not a very nice one.
If you avoid those values, the fit improves significantly.
Edit:
As indicated in the comments, the Gaussian is centered at about 8 looking downwards (silly me, it was an absorption line).
In such a case, the offset should be located at about the maximum ~22000 and then the parameter for the amplitude should be negative ~ -(max(y)-min(y)) ~ -16000.
And as an addition, better change xx
to be as follows
xx = np.linspace(0, 13, 100)
or
xx = np.arange(0, 13, 0.05)
Which will give
and checking popt
you get basically the values I mentioned/estimated by just looking at the plot ~(2180, -16000, 8) with a sigma of 2.7 which was the only one I don't have an immediate feeling on how to estimate.
My guess is that you should actually be fitting a mixture of Gauss and a Cauchy/Lorentz lineshape or even better a Voigt lineshape, to account for experimental broadening.
来源:https://stackoverflow.com/questions/59047395/fitting-gaussian-to-absorbtion-line-in-python