将高斯拟合到 python 中的吸收线
Fitting gaussian to absorbtion line in python
我正在尝试对我的数据进行高斯拟合,这些数据是在非常窄的光谱 window 中获取的。我们得到了大约 2 个连续体点,然后大约有 10-11 个点是直线的一部分。我想应该还是可以拟合的,但是每次曲线拟合都失败,我也不知道为什么。
当 运行 我得到 RuntimeError: Optimal parameters not found: Number of calls to function has reached maxfev = 800.
代码和数据:
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()
正如错误所说,寻找最优值的过程没有收敛。如果你真的认为你拥有的东西可以用高斯曲线拟合,那么这通常意味着你的起点不好。
你如何给出起点可能是个问题,特别是你如何提供 sigma,因为在位置 11、12 和 13 你有可能是另一个信号的开始。无论如何,这不是这次最大的问题,而是您忘记为高斯函数添加偏移量
# ----> 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
然后,你可以决定如何提供一个偏移量的起点,但是从目测来看,我确实设置了5000
popt, pcov = curve_fit(Gauss, x, y, p0=[5000, max(y), mean, sigma])
这样做,我就健康了。但是,由于最后三个数据点,这不是一个很好的数据。
如果避免这些值,拟合度会显着提高。
编辑:
正如评论中指出的那样,高斯分布以 8 为中心向下看(愚蠢的我,这是一条吸收线)。
在这种情况下,偏移量应位于最大值 ~22000 左右,然后振幅参数应为负值 ~ -(max(y)-min(y)) ~ -16000。
另外,最好将xx
改成如下
xx = np.linspace(0, 13, 100)
或
xx = np.arange(0, 13, 0.05)
哪个会给
并检查 popt
你基本上得到了我 mentioned/estimated 的值,只需查看 ~(2180, -16000, 8) 和 2.7 的西格玛,这是我唯一没有的对如何估算有一个直接的感觉。
我的猜测是,您实际上应该拟合高斯和 Cauchy/Lorentz 线形或更好的 Voigt lineshape 的混合,以考虑实验范围的扩大。
我正在尝试对我的数据进行高斯拟合,这些数据是在非常窄的光谱 window 中获取的。我们得到了大约 2 个连续体点,然后大约有 10-11 个点是直线的一部分。我想应该还是可以拟合的,但是每次曲线拟合都失败,我也不知道为什么。
当 运行 我得到 RuntimeError: Optimal parameters not found: Number of calls to function has reached maxfev = 800.
代码和数据:
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()
正如错误所说,寻找最优值的过程没有收敛。如果你真的认为你拥有的东西可以用高斯曲线拟合,那么这通常意味着你的起点不好。
你如何给出起点可能是个问题,特别是你如何提供 sigma,因为在位置 11、12 和 13 你有可能是另一个信号的开始。无论如何,这不是这次最大的问题,而是您忘记为高斯函数添加偏移量
# ----> 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
然后,你可以决定如何提供一个偏移量的起点,但是从目测来看,我确实设置了5000
popt, pcov = curve_fit(Gauss, x, y, p0=[5000, max(y), mean, sigma])
这样做,我就健康了。但是,由于最后三个数据点,这不是一个很好的数据。
如果避免这些值,拟合度会显着提高。
编辑:
正如评论中指出的那样,高斯分布以 8 为中心向下看(愚蠢的我,这是一条吸收线)。
在这种情况下,偏移量应位于最大值 ~22000 左右,然后振幅参数应为负值 ~ -(max(y)-min(y)) ~ -16000。
另外,最好将xx
改成如下
xx = np.linspace(0, 13, 100)
或
xx = np.arange(0, 13, 0.05)
哪个会给
并检查 popt
你基本上得到了我 mentioned/estimated 的值,只需查看 ~(2180, -16000, 8) 和 2.7 的西格玛,这是我唯一没有的对如何估算有一个直接的感觉。
我的猜测是,您实际上应该拟合高斯和 Cauchy/Lorentz 线形或更好的 Voigt lineshape 的混合,以考虑实验范围的扩大。