You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python中使用curve_fit进行双高斯拟合的问题

双高斯拟合精准优化方案

问题分析

原拟合结果不佳的核心原因包括:

  • find_peaks参数设置不合理,峰值定位存在偏差
  • 初始参数猜测精度不足,峰位置和宽度的估算受数据离散性影响较大
  • curve_fit未设置参数约束,拟合过程中参数易偏离合理范围
  • 模型未考虑基线噪声,导致整体拟合偏移

优化步骤

  • 精准定位峰值:调整find_peaks的distance和height参数,确保两个峰被正确识别分离。
  • 优化初始参数:用二次插值细化峰位置,结合稳健的半高宽计算宽度参数,同时加入基线初始值(取数据基线区域均值)。
  • 添加参数边界:通过curve_fit的bounds参数限制参数范围(如高度为正、宽度为正、峰位置在数据区间内),防止拟合发散。
  • 引入基线项:在双高斯模型中加入常数基线,适配数据的背景噪声。

修改后的代码

import matplotlib.pyplot as plt
import numpy as np
from scipy.signal import find_peaks
from scipy.optimize import curve_fit
from scipy.interpolate import interp1d

# 原始数据
y_data = np.array([
    1.500e-04, 1.500e-04, 1.500e-04, 1.500e-04, 1.700e-04, 1.600e-04, 1.800e-04,
    1.600e-04, 1.700e-04, 2.300e-04, 2.500e-04, 3.200e-04, 3.200e-04, 3.800e-04,
    4.000e-04, 5.000e-04, 5.600e-04, 5.600e-04, 6.500e-04, 7.500e-04, 9.100e-04,
    1.180e-03, 1.550e-03, 2.110e-03, 2.880e-03, 3.850e-03, 5.200e-03, 6.780e-03,
    8.950e-03, 1.123e-02, 1.403e-02, 1.723e-02, 2.031e-02, 2.330e-02, 2.495e-02,
    2.433e-02, 2.171e-02, 1.725e-02, 1.231e-02, 8.080e-03, 4.980e-03, 2.960e-03,
    1.970e-03, 1.830e-03, 2.220e-03, 2.880e-03, 3.700e-03, 4.650e-03, 5.820e-03,
    7.150e-03, 8.450e-03, 9.510e-03, 1.006e-02, 9.660e-03, 8.560e-03, 6.910e-03,
    5.100e-03, 3.380e-03, 2.170e-03, 1.230e-03, 7.000e-04, 3.600e-04, 2.200e-04,
    1.700e-04, 1.500e-04, 1.600e-04, 1.100e-04, 1.100e-04, 1.200e-04, 1.000e-04,
    1.200e-04, 1.100e-04, 1.300e-04, 1.500e-04, 1.200e-04, 1.600e-04, 1.200e-04,
    1.200e-04, 1.200e-04, 1.200e-04, 1.100e-04, 1.500e-04, 1.500e-04, 1.300e-04,
    1.100e-04, 8.000e-05, 1.200e-04, 1.200e-04, 1.100e-04, 1.100e-04, 1.500e-04])

x_data = np.array([
    6555.101, 6555.201, 6555.301, 6555.401, 6555.501, 6555.601, 6555.701, 6555.801,
    6555.901, 6556.001, 6556.101, 6556.201, 6556.301, 6556.401, 6556.501, 6556.601,
    6556.701, 6556.801, 6556.901, 6557.001, 6557.101, 6557.201, 6557.301, 6557.401,
    6557.501, 6557.601, 6557.701, 6557.801, 6557.901, 6558.001, 6558.101, 6558.201,
    6558.301, 6558.401, 6558.501, 6558.601, 6558.701, 6558.801, 6558.901, 6559.001,
    6559.101, 6559.201, 6559.301, 6559.401, 6559.501, 6559.601, 6559.701, 6559.801,
    6559.901, 6560.001, 6560.101, 6560.201, 6560.301, 6560.401, 6560.501, 6560.601,
    6560.701, 6560.801, 6560.901, 6561.001, 6561.101, 6561.201, 6561.301, 6561.401,
    6561.501, 6561.601, 6561.701, 6561.801, 6561.901, 6562.001, 6562.101, 6562.201,
    6562.301, 6562.401, 6562.501, 6562.601, 6562.701, 6562.801, 6562.901, 6563.001,
    6563.101, 6563.201, 6563.301, 6563.401, 6563.501, 6563.601, 6563.701, 6563.801,
    6563.901, 6564.001, 6564.001])

# 优化峰值检测:设置distance确保两个峰被正确分离
peaks, _ = find_peaks(y_data, height=0.001, distance=20)

# 定义带基线的双高斯函数
def double_gaussian(x, a1, b1, c1, a2, b2, c2, baseline):
    g1 = a1 * np.exp(-((x - b1) ** 2) / (2 * c1 ** 2))
    g2 = a2 * np.exp(-((x - b2) ** 2) / (2 * c2 ** 2))
    return g1 + g2 + baseline

# 更稳健的FWHM计算函数
def find_fwhm(x, y, peak_idx):
    half_max = y[peak_idx] / 2
    # 左半部分插值找半高位置
    left_points = y[:peak_idx+1][::-1]
    left_xs = x[:peak_idx+1][::-1]
    left_idx = np.argmax(left_points <= half_max)
    if left_idx == 0:
        left_x = left_xs[0]
    else:
        x0, x1 = left_xs[left_idx-1], left_xs[left_idx]
        y0, y1 = left_points[left_idx-1], left_points[left_idx]
        left_x = x0 + (half_max - y0) * (x1 - x0)/(y1 - y0)
    
    # 右半部分插值找半高位置
    right_points = y[peak_idx:]
    right_xs = x[peak_idx:]
    right_idx = np.argmax(right_points <= half_max)
    if right_idx == 0:
        right_x = right_xs[0]
    else:
        x0, x1 = right_xs[right_idx-1], right_xs[right_idx]
        y0, y1 = right_points[right_idx-1], right_points[right_idx]
        right_x = x0 + (half_max - y0) * (x1 - x0)/(y1 - y0)
    return right_x - left_x

# 用二次插值细化峰位置
interp_func = interp1d(x_data, y_data, kind='quadratic')
x_fine = np.linspace(x_data[peaks[0]-2], x_data[peaks[0]+2], 1000)
y_fine = interp_func(x_fine)
peak1_x = x_fine[np.argmax(y_fine)]

x_fine2 = np.linspace(x_data[peaks[1]-2], x_data[peaks[1]+2], 1000)
y_fine2 = interp_func(x_fine2)
peak2_x = x_fine2[np.argmax(y_fine2)]

# 峰高取插值后的最大值
peak1_a = np.max(y_fine)
peak2_a = np.max(y_fine2)

# 计算FWHM并转换为高斯宽度
fwhm1 = find_fwhm(x_data, y_data, peaks[0]) / 2.355
fwhm2 = find_fwhm(x_data, y_data, peaks[1]) / 2.355

# 基线初始值:取数据前10个点的均值
baseline_guess = np.mean(y_data[:10])

# 初始参数猜测
initial_guess = [peak1_a, peak1_x, fwhm1, peak2_a, peak2_x, fwhm2, baseline_guess]

# 设置参数边界,避免拟合发散
bounds = (
    [0, min(x_data), 0.01, 0, min(x_data), 0.01, 0],  # 参数下界
    [max(y_data)*1.2, max(x_data), 2, max(y_data)*1.2, max(x_data), 2, max(y_data)*0.1]  # 参数上界
)

# 执行带边界约束的拟合
params, covariance = curve_fit(double_gaussian, x_data, y_data, p0=initial_guess, bounds=bounds)

# 生成拟合曲线数据
x_fit = np.linspace(min(x_data), max(x_data), 10000)
fitted_data = double_gaussian(x_fit, *params)

# 分离单个高斯分量和基线
g1 = params[0] * np.exp(-((x_fit - params[1]) ** 2) / (2 * params[2] ** 2))
g2 = params[3] * np.exp(-((x_fit - params[4]) ** 2) / (2 * params[5] ** 2))
baseline = params[6] * np.ones_like(x_fit)

# 绘图展示
plt.scatter(x_data, y_data, label='原始数据', s=10)
plt.plot(x_fit, g1 + baseline, 'g--', label='高斯峰1')
plt.plot(x_fit, g2 + baseline, 'm--', label='高斯峰2')
plt.plot(x_fit, fitted_data, 'r-', label='双高斯拟合曲线', linewidth=2)
plt.xlabel('x')
plt.ylabel('y')
plt.legend()
plt.show()

优化效果说明

修改后的代码能够:

  • 精准定位两个峰的位置,避免拟合错位
  • 拟合曲线与原始数据的匹配度显著提升
  • 高斯分量的高度、宽度和位置与实际数据特征对齐

内容的提问来源于stack exchange,提问作者bougab

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 14:48:09