SciPy曲线拟合出现荒谬结果的问题排查
问题分析与修正方案
你的代码存在三个核心错误,导致拟合结果和绘图完全不匹配:
1. 拟合的x轴输入错误
curve_fit(gauss, diff, hist)中,用原始数组diff作为拟合的x值是错误的。hist是直方图每个区间(bin)的计数,对应的x应该是每个bin的中点,而非原始数据点。
2. 直方图的bins设置完全不合理
plt.hist(diff, bins=len(diff))会创建与数据点数量一致的bins,这会导致每个bin最多只有1个样本(除非存在大量重复值),直方图会变成离散尖峰,完全失去统计意义。
3. 高斯函数的幅度与直方图计数不匹配
norm.pdf输出的是概率密度(积分和为1),而直方图的y轴是样本计数,两者量级完全不同。直接拟合会导致幅度参数完全失真。
修正后的完整代码
import numpy as np from scipy.optimize import curve_fit from scipy.stats import norm import matplotlib.pyplot as plt # 1. 正确生成直方图并计算关键参数 hist, bins = np.histogram(diff, bins='auto') # 用auto自动选择合理的bin数量 bin_centers = (bins[:-1] + bins[1:]) / 2 # 计算每个bin的中点(拟合用x) bin_width = bins[1] - bins[0] # 获取bin宽度 n_samples = len(diff) # 总样本数 # 2. 定义适配计数的高斯函数 def gauss(x, disp, amp, mu, sigma): # amp对应:样本数 × bin宽度(概率密度转计数的缩放因子) return disp + amp * norm(mu, sigma).pdf(x) # 3. 设置合理的初始猜测值(用数据自身的统计量,而非随意赋值) p0 = ( 0, # disp:基线偏移,初始设0 n_samples * bin_width, # amp:计数缩放的初始值 np.mean(diff), # mu:数据均值 np.std(diff) # sigma:数据标准差 ) # 4. 限制参数范围(sigma必须为正,避免拟合出负数标准差) bounds = ( (-np.inf, 0, -np.inf, 0), # 参数下限:disp不限,amp>0,mu不限,sigma>0 (np.inf, np.inf, np.inf, np.inf) # 参数上限:全部不限 ) popt, pcov = curve_fit(gauss, bin_centers, hist, p0=p0, bounds=bounds) # 5. 绘制匹配的直方图与拟合曲线 x = np.linspace(bins[0], bins[-1], 1000) p = gauss(x, *popt) plt.hist(diff, bins=bins, label='原始直方图') plt.plot(x, p, 'r-', linewidth=2, label='拟合高斯曲线') plt.legend() plt.show()
可选优化:拟合概率密度
如果你更关注分布的形状而非计数,可以将直方图转换为概率密度后再拟合,这样不需要考虑幅度缩放:
# 转换直方图为概率密度 hist_pdf = hist / (n_samples * bin_width) def gauss_pdf(x, disp, amp, mu, sigma): return disp + amp * norm(mu, sigma).pdf(x) p0 = (0, 1, np.mean(diff), np.std(diff)) popt, pcov = curve_fit(gauss_pdf, bin_centers, hist_pdf, p0=p0, bounds=bounds) # 绘图时可选绘制密度直方图 plt.hist(diff, bins=bins, density=True, label='概率密度直方图') plt.plot(x, gauss_pdf(x, *popt), 'r-', linewidth=2, label='拟合高斯分布') plt.legend() plt.show()
内容的提问来源于stack exchange,提问作者Qwertie
相关产品推荐
相关产品推荐

