Python scipy 指数修正高斯分布曲线拟合问题排查与实现
问题根源
你的代码出问题有三个核心原因,根本没有完成拟合流程:
- 导入了
curve_fit但从未调用执行拟合操作,指数修正高斯分布必需的形状参数K、位置参数loc、尺度参数scale没有从数据中估计,你写的exponnorm.pdf(y,K)里K是未定义变量,运行时本身就会报错,更不可能得到正确曲线 exponnorm.pdf的参数传参逻辑完全错误:该方法第一个入参是需要计算概率密度的自变量坐标(也就是你直方图的bin中心x值),你传入的y是直方图的频次计数,完全不符合入参要求- 画直方图时默认输出的是样本频次,和概率密度函数量级差极大,就算参数传对,曲线和直方图也完全匹配不上
修正方案
直接用scipy统计分布自带的拟合方法即可,比手动调用curve_fit做最小二乘适配分布更稳定,步骤如下:
- 提取目标列数据后先过滤无效空值
- 调用
exponnorm.fit()直接从原始数据估计K、loc、scale三个参数,不需要手动猜初值 - 画直方图时开启
density=True参数,让直方图纵轴输出概率密度,和pdf计算结果的量级匹配 - 用拟合得到的参数计算对应x区间的pdf值,再绘图对比
修正后的可运行代码如下:
import matplotlib.pyplot as plt import numpy as np from scipy.stats import exponnorm # 读取数据 data = np.genfromtxt("Data.dat", delimiter='\t') # 提取第二列值为922的行,取第9列(索引8)的TOF数据 Tofs = data[data[:,1] == 922, 8] # 过滤可能的nan无效值 Tofs = Tofs[~np.isnan(Tofs)] # 画归一化直方图 plt.figure(figsize=(10,6)) counts, bin_borders, _ = plt.hist(Tofs, bins=100, density=True, alpha=0.6, label='原始数据直方图') # 计算每个bin的中心x坐标 bin_centers = (bin_borders[:-1] + bin_borders[1:]) / 2 # 核心:从原始数据拟合exponnorm的三个参数 K, loc, scale K_fit, loc_fit, scale_fit = exponnorm.fit(Tofs) print(f"拟合得到参数:K={K_fit:.3f}, loc={loc_fit:.3f}, scale={scale_fit:.3f}") # 用拟合参数计算pdf值 pdf_vals = exponnorm.pdf(bin_centers, K_fit, loc=loc_fit, scale=scale_fit) # 画拟合曲线 plt.plot(bin_centers, pdf_vals, 'r-', linewidth=2, label='指数修正高斯拟合曲线') plt.xlabel('TOF值') plt.ylabel('概率密度') plt.legend() plt.show()
拟合优化提示
- 如果自动拟合的效果不好,可以给
fit()方法传入合理的参数初值,比如提前把loc初值设为数据的峰值位置,能大幅提升拟合收敛速度和准确率 - 直方图的bin数量可以根据数据量调整,你当前有效数据量在400行左右,bins设为30~50比100更能降低统计噪声对拟合的干扰
- 一般做分布参数估计优先用scipy分布自带的
fit()方法,该方法基于极大似然估计做参数优化,稳定性比手动写最小二乘拟合高很多;如果确实需要拟合直方图的频次曲线,再调用curve_fit,传入归一化后的y值和合理参数初值即可。
内容的提问来源于stack exchange,提问作者albedo
相关产品推荐
相关产品推荐

