这里展示如何使用curve_fit进行高斯双峰拟合。
其实双峰或者多峰拟合的思路非常简单,就是把单个 gaussian 函数加起来,形成一个新的函数,再使用 curve_fit 进行拟合。比较繁琐的是,参数数量倍增。
# 定义高斯函数def gaussian(x, a, x0, sigma, b): ''' x: 输入变量 a: 参数,缩放量 x0:高斯峰所在位置,相当于高斯分布平均值 sigma:高斯分布一个标准差 b: 参数,平移量 ''' return a * np.exp(-(x - x0) ** 2 / (2 * sigma ** 2))+b
def double_gaussian(x, a1, mean1, sigma1, b1, a2, mean2, sigma2, b2): return gaussian(x, a1, mean1, sigma1, b1) + gaussian(x, a2, mean2, sigma2, b2)然后一个很容易理解的情况是,参数越多,拟合难度越大。因此可以在 curve_fit 中设置 最大迭代次数 maxfev 这个参数为更大值(默认为1800,可以设置到20000)。
此外还可以设置拟合参数的上下限(bounds)。但如果设置了 bounds,就不能使用默认的 lm 方法进行拟合,只能用 rtf 或者 dogbox,这些在 curve_fit 的文档中都有详细说明。
然后还有一些奏效的方法,包括对数据进行预处理,减小噪声干扰;或者对高斯函数进行变形;或者更换其它更适合的算法。
适用于简单情况下的高斯双峰拟合函数代码如下:
def double_gaussian_fit(xx, yy, initial_guess, maxfev=20000): '''返回拟合后曲线的xy值''' # bounds = ((0, 0, 0, -np.inf, 0, 0, 0, -np.inf),(np.inf, 10, 10, np.inf, np.inf, 10, 10, np.inf)) # popt, pcov = curve_fit(double_gaussian, xx, yy, p0=initial_guess, maxfev=maxfev, method='rtf', bounds=bounds)
popt, pcov = curve_fit(double_gaussian, xx, yy, p0=initial_guess, maxfev=maxfev) x3 = np.linspace(xx.min(),xx.max(), 300) # 根据x范围均匀插值 y3 = double_gaussian(x3, *popt) plt.figure(figsize=(4,3)) plt.scatter(xx, yy, c='gray') plt.plot(x3, y3, c='red') plt.show()
# 计算各个参数的拟合误差 perr = np.sqrt(np.diag(pcov))
# 计算拟合的R方值 y2 = double_gaussian(xx, *popt) mean = np.mean(yy) ss_tot = np.sum((yy - mean) ** 2) ss_res = np.sum((yy - y2) ** 2) r_squared = 1 - (ss_res / ss_tot) print(f'R_squared:{r_squared}')
loc1 = popt[1] loc2 = popt[5] sig1 = popt[2] sig2 = popt[6] FWHM1 = 2 * np.sqrt(2 * np.log(2)) * abs(sig1) FWHM2 = 2 * np.sqrt(2 * np.log(2)) * abs(sig2) # 打印拟合参数和误差 print("参数:", popt) print("参数误差:", perr) print(f"Peak 1 location: {loc1*1000:.1f} nm\nSigma: {abs(sig1)*1000:.1f} nm\nFWHM(分辨率): {FWHM1*1000:.1f} nm") print(f"Peak 2 location: {loc2*1000:.1f} nm\nSigma: {abs(sig2)*1000:.1f} nm\nFWHM(分辨率): {FWHM2*1000:.1f} nm") return x3, y3