如何为对数分箱的Mass直方图添加趋势曲线?
给对数分箱直方图添加趋势曲线
嘿,我来帮你搞定这个对数分箱直方图的趋势曲线问题!你的现有代码已经搭好了对数分箱的直方图框架,现在只需要添加趋势曲线的计算和绘制逻辑就行。因为是对数坐标轴,直接在原始空间拟合容易出数值问题,所以我们可以在对数空间处理后再转换回来,或者用核密度估计(KDE)生成平滑趋势线。
下面是修改后的完整代码,我会逐部分解释:
import matplotlib.pyplot as plt import numpy as np from scipy.stats import gaussian_kde # 用于生成平滑的核密度估计曲线 # 假设Mass是你的预定义变量,这里先模拟一组符合范围的测试数据 np.random.seed(42) Mass = np.logspace(43, 45, 1000) * np.random.lognormal(0, 0.3, 1000) # 生成对数分箱:直接用Mass的最小/最大值生成,比基于线性分箱转换更准确 num_bins = 10 logbins = np.logspace(np.log10(Mass.min()), np.log10(Mass.max()), num_bins + 1) # 绘制对数分箱直方图 n, bins, patches = plt.hist(x=Mass, bins=logbins, color='#0504aa', alpha=0.8, rwidth=0.85) # --- 方法1:多项式拟合趋势线 --- # 计算对数分箱的中点(用几何平均,适配对数刻度的特性) bin_midpoints = np.sqrt(bins[:-1] * bins[1:]) # 在对数空间拟合多项式(避免大数值带来的拟合误差) z = np.polyfit(np.log10(bin_midpoints), np.log10(n), 2) # 2次多项式,可根据需求调整次数 p = np.poly1d(z) # 生成拟合曲线的x轴点(更密集,让曲线更平滑) fit_x = np.logspace(np.log10(Mass.min()), np.log10(Mass.max()), 100) # 转换回原始空间的y值 fit_y = 10 ** p(np.log10(fit_x)) # 绘制拟合曲线 plt.plot(fit_x, fit_y, color='red', linewidth=2, label='Polynomial Fit Trend') # --- 方法2:核密度估计(KDE)平滑曲线(可选,注释掉方法1即可用这个)--- # kde = gaussian_kde(np.log10(Mass)) # 在对数空间计算KDE,避免数值偏差 # kde_x = np.logspace(np.log10(Mass.min()), np.log10(Mass.max()), 100) # # 缩放KDE值以匹配直方图的计数(KDE是概率密度,需乘以样本数和分箱宽度) # kde_y = kde(np.log10(kde_x)) * len(Mass) * np.diff(np.log10(logbins))[0] # plt.plot(kde_x, kde_y, color='green', linewidth=2, label='KDE Smooth Trend') # 完善图表设置 plt.xscale('log') plt.xlabel('Mass $(g)$ ') plt.ylabel('Number of halos') plt.legend() plt.show()
关键细节说明:
- 对数分箱优化:直接用
Mass的最小/最大值生成对数分箱,比你原来先做线性分箱再转换的方式更贴合数据范围。 - 分箱中点计算:用几何平均
np.sqrt(bins[:-1] * bins[1:])作为对数分箱的中点,比线性中点更适配对数刻度的分布逻辑。 - 拟合策略:在对数空间进行多项式拟合,避免原始数据的大数值导致拟合失效,之后再转换回原始空间绘制曲线。
- KDE选项:如果想要更平滑的趋势线,可以用KDE方法,需要注意缩放KDE值来匹配直方图的计数单位(因为KDE默认是概率密度,不是计数)。
你可以根据自己的数据分布特点选择多项式拟合或者KDE方法,调整多项式次数或者KDE的带宽(gaussian_kde可以传入bw_method参数调整平滑程度)。
内容的提问来源于stack exchange,提问作者Salome




