如何在对数X轴直方图上正确拟合与缩放概率密度函数?
问题解决:对数轴下Gilbrat PDF拟合曲线平缓的问题
问题描述
我尝试将Gilbrat PDF拟合至列表形式的数据集,希望在对数X轴的直方图中展示数据并添加拟合曲线,但曲线相比直方图过于平缓。我曾参考相关方法调整PDF缩放,但问题仍未解决。以下是含随机生成数据的代码示例:
import scipy.stats as st import numpy as np import matplotlib.pyplot as plt #create random dataset data = st.gilbrat.rvs(scale = 10, size = 100).tolist() param = st.gilbrat.fit(data) x = np.linspace(min(data),max(data),len(data)) pdf = st.gilbrat.pdf(x, param[0], param[1]) plt.figure() logbins = np.logspace(np.log10(np.min(data)),np.log10(np.max(data)),20) result = plt.hist(data, bins=logbins, edgecolor="black", alpha = 0.5, label="data") dx = result[1][1] - result[1][0] plt.plot(x,pdf * (len(data)*dx), label="fit") plt.xscale('log') plt.xlabel("x") plt.ylabel("Number of occurence") plt.legend()
问题根源
你用了固定的dx = result[1][1] - result[1][0]来缩放PDF,但对数刻度下的bin宽度是不均匀的——后面的bin宽度远大于前面的,单一的dx无法匹配所有位置的缩放需求,导致曲线整体被低估,看起来过于平缓。
修正方案
有两种常用的修正方式,根据你需要的y轴类型选择:
方式1:使用概率密度匹配(推荐)
让直方图输出概率密度(density=True),直接和scipy.stats的PDF输出对齐,无需额外缩放:
import scipy.stats as st import numpy as np import matplotlib.pyplot as plt # 创建随机数据集 data = st.gilbrat.rvs(scale=10, size=100).tolist() param = st.gilbrat.fit(data) # 用对数间隔生成x点,贴合对数轴的分布,曲线更平滑 x = np.logspace(np.log10(np.min(data)), np.log10(np.max(data)), 1000) pdf = st.gilbrat.pdf(x, param[0], param[1]) plt.figure() logbins = np.logspace(np.log10(np.min(data)), np.log10(np.max(data)), 20) # 开启density=True,直方图y轴为概率密度 plt.hist(data, bins=logbins, edgecolor="black", alpha=0.5, label="data", density=True) # 直接绘制PDF,和直方图的概率密度轴匹配 plt.plot(x, pdf, label="fit", linewidth=2) plt.xscale('log') plt.xlabel("x") plt.ylabel("概率密度") plt.legend() plt.show()
方式2:保留出现次数作为y轴
如果需要y轴显示实际计数,需要为每个x位置匹配对应的bin宽度,再进行缩放:
import scipy.stats as st import numpy as np import matplotlib.pyplot as plt # 创建随机数据集 data = st.gilbrat.rvs(scale=10, size=100).tolist() param = st.gilbrat.fit(data) # 用对数间隔生成x点 x = np.logspace(np.log10(np.min(data)), np.log10(np.max(data)), 1000) pdf = st.gilbrat.pdf(x, param[0], param[1]) plt.figure() logbins = np.logspace(np.log10(np.min(data)), np.log10(np.max(data)), 20) # 获取每个bin的宽度 bin_widths = logbins[1:] - logbins[:-1] # 绘制直方图并获取计数 counts, _, patches = plt.hist(data, bins=logbins, edgecolor="black", alpha=0.5, label="data") # 为每个x点插值匹配对应的bin宽度 x_bin_widths = np.interp(x, logbins[:-1], bin_widths) # 缩放PDF:概率密度 * 样本数 * 对应bin宽度 = 计数 plt.plot(x, pdf * len(data) * x_bin_widths, label="fit", linewidth=2) plt.xscale('log') plt.xlabel("x") plt.ylabel("出现次数") plt.legend() plt.show()
额外优化点
- 用
np.logspace生成x轴数据,让点在对数轴上均匀分布,拟合曲线更平滑准确 - 增加拟合曲线的线宽(
linewidth=2),让曲线更清晰
内容的提问来源于stack exchange,提问作者Conni
相关产品推荐
相关产品推荐

