如何在多列Pandas DataFrame对数直方图添加均值点与标准差线?
实现方案:在对数刻度直方图的每个区间添加均值点和标准差竖线
我拥有三个包含多列的Pandas DataFrame,数据生成代码如下:
import pandas as pd import numpy as np from matplotlib import pyplot as plt from scipy import stats import seaborn as sns data = np.random.randint(0, 1001, size=(1000, 100)) data[data < 0] = 0 df1 = pd.DataFrame(data) df2 = pd.DataFrame(data) df3 = pd.DataFrame(data)已通过以下代码绘制出三个DataFrame的未堆叠对数刻度直方图:
num_bin = 30 figure, axes = plt.subplots(1, 3, sharex=True, figsize=(15,5)) axes[0].set_title('log df1') axes[1].set_title('log df2') axes[2].set_title('log df3') sns.histplot(ax=axes[0],data=df1[df1>0], bins=num_bin, log_scale=True, weights=None, legend=False, palette='viridis', alpha=0.1) sns.histplot(ax=axes[1],data=df2[df2>0], bins=num_bin, log_scale=True, weights=None, legend=False, palette='husl', alpha=0.1) sns.histplot(ax=axes[2],data=df3[df3>0], bins=num_bin, log_scale=True, weights=None, legend=False, palette='inferno', alpha=0.1) plt.show()现在希望在每个直方图的每个区间(bin)上,添加基于所有列计算得到的均值点和标准差竖线,请问有什么实现方案?
核心思路
- 先统一计算所有直方图的对数刻度区间边界,确保三个子图的区间完全一致
- 对每个DataFrame,按区间分组计算所有数据的均值和标准差
- 在对应子图上用散点标记均值位置,用竖线表示标准差范围
完整代码实现
import pandas as pd import numpy as np from matplotlib import pyplot as plt import seaborn as sns # 生成数据 data = np.random.randint(0, 1001, size=(1000, 100)) data[data < 0] = 0 df1 = pd.DataFrame(data) df2 = pd.DataFrame(data) df3 = pd.DataFrame(data) num_bin = 30 figure, axes = plt.subplots(1, 3, sharex=True, figsize=(15,5)) axes[0].set_title('log df1') axes[1].set_title('log df2') axes[2].set_title('log df3') # 统一生成对数刻度区间边界(保证三个图的bins完全匹配) all_valid_data = pd.concat([df1[df1>0].stack(), df2[df2>0].stack(), df3[df3>0].stack()]) log_bins = np.logspace(np.log10(all_valid_data.min()), np.log10(all_valid_data.max()), num_bin + 1) # 定义工具函数:计算区间统计量并绘制到指定轴 def add_stats_markers(ax, df, bins, marker_color): # 展平数据并过滤0值 flat_data = df[df>0].stack().values # 给每个数据点分配所属区间 bin_indices = np.digitize(flat_data, bins) # 按区间分组计算均值、标准差,跳过空区间 group_stats = pd.DataFrame({ 'value': flat_data, 'bin_idx': bin_indices }).groupby('bin_idx').agg( mean_val=('value', 'mean'), std_val=('value', 'std') ).dropna() # 计算每个区间的中心位置(对数轴用几何平均更合理) bin_centers = np.sqrt(bins[:-1] * bins[1:])[group_stats.index - 1] # 绘制均值点(zorder确保在直方图上方) ax.scatter(bin_centers, group_stats['mean_val'], color=marker_color, s=50, zorder=5, label='均值') # 绘制标准差竖线 for x, mean, std in zip(bin_centers, group_stats['mean_val'], group_stats['std_val']): ax.vlines(x, mean - std, mean + std, color=marker_color, alpha=0.7, zorder=4) ax.legend() # 绘制直方图并添加统计标记 sns.histplot(ax=axes[0], data=df1[df1>0], bins=log_bins, log_scale=True, weights=None, legend=False, palette='viridis', alpha=0.1) add_stats_markers(axes[0], df1, log_bins, '#440154') sns.histplot(ax=axes[1], data=df2[df2>0], bins=log_bins, log_scale=True, weights=None, legend=False, palette='husl', alpha=0.1) add_stats_markers(axes[1], df2, log_bins, '#fde725') sns.histplot(ax=axes[2], data=df3[df3>0], bins=log_bins, log_scale=True, weights=None, legend=False, palette='inferno', alpha=0.1) add_stats_markers(axes[2], df3, log_bins, '#e7298a') plt.tight_layout() plt.show()
关键细节说明
- 统一区间:基于所有有效数据生成对数区间,避免三个子图的区间错位,保证统计量的可比性
- 区间统计:用
np.digitize快速分配数据区间,分组计算自动跳过无数据的空区间 - 对数适配:区间中心采用几何平均计算,符合对数坐标轴的分布逻辑
- 图层控制:通过
zorder参数让统计标记显示在直方图上方,提升视觉可读性
内容的提问来源于stack exchange,提问作者Giantanque
相关产品推荐
相关产品推荐

