You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多直方图对比:基于区间计数的箱线图可视化实现问询

实现直方图嵌套箱线图的可视化方案

我明白你想要的是在每个直方图区间(bin)的位置上,展示对应五组数据计数的箱线图,形成一种嵌套式的可视化效果。我们可以通过自动分箱统计替代手动计数,再将直方图和箱线图叠加在同一坐标轴上来实现这个需求,下面是完整的解决方案:

步骤1:自动分箱并统计各组计数

首先我们不需要手动统计每个区间的计数,用pd.cut给原始数据分箱,再通过分组聚合自动计算每个Photo在每个bin里的数量,这样更准确且高效:

import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

# 原始数据定义
u30_diameter_01 = pd.Series([6.76, 5.03, 6.57, 5.52, 8.81, 9.50, 4.96, 6.19, 5.61, 4.86, 4.41, 6.04, 6.39, 4.61, 6.90, 6.38, 6.20, 6.75, 4.76, 6.18, 6.13, 7.95, 4.22, 6.16, 8.40, 6.90, 6.77, 6.11, 6.15, 6.43])
u30_diameter_02 = pd.Series([4.98, 5.16, 6.43, 3.93, 5.41, 6.39, 6.35, 5.43, 6.69, 7.10, 7.55, 5.92, 6.50, 5.36, 7.27, 4.10, 6.89, 4.82, 4.34, 5.67, 8.95, 7.53, 4.91, 5.30, 6.62, 6.90, 5.98, 6.05, 4.37, 6.42, 5.03, 7.78, 6.28, 7.81, 5.81, 5.99, 6.22, 6.48, 4.43, 5.18])
u30_diameter_03 = pd.Series([5.80, 5.72, 6.19, 5.69, 4.19, 4.82, 6.32, 6.14, 6.59, 4.90, 6.39, 5.93, 5.17, 6.00, 6.64, 7.48, 5.61, 5.79, 4.79, 5.99, 5.11, 6.08, 6.68, 5.84, 6.88, 6.81, 6.07, 4.55, 6.20, 5.50])
u30_diameter_04 = pd.Series([5.21, 7.85, 6.48, 4.44, 6.91, 6.30, 8.51, 5.99, 7.74, 5.47, 6.19, 8.44, 8.03, 8.37, 6.80, 6.49, 5.65, 6.06, 7.04, 4.55, 6.76, 7.99, 5.64, 5.97, 6.21])
u30_diameter_05 = pd.Series([7.24, 7.42, 6.85, 8.14, 7.03, 6.52, 5.82, 6.92, 7.44, 7.91, 7.18, 7.99, 6.19, 7.44, 7.37, 6.54, 7.11, 7.77, 8.67, 8.35, 7.12, 5.10, 8.29, 6.36, 7.81])

# 合并数据,添加Photo标签
u30_diameter = pd.DataFrame({
    'Value': pd.concat([u30_diameter_01, u30_diameter_02, u30_diameter_03, u30_diameter_04, u30_diameter_05], ignore_index=True),
    'Photo': np.repeat(['1','2','3','4','5'], [len(s) for s in [u30_diameter_01, u30_diameter_02, u30_diameter_03, u30_diameter_04, u30_diameter_05]])
})

# 定义分箱区间,和你之前的直方图一致
diameter_range = np.arange(3.75,9.75,0.5)
# 给每个数据点分配对应的bin
u30_diameter['Bin'] = pd.cut(u30_diameter['Value'], bins=diameter_range, include_lowest=True)

# 统计每个Photo在每个Bin里的计数
bin_counts = u30_diameter.groupby(['Bin', 'Photo']).size().reset_index(name='Count')
# 填充缺失的组合(比如某个Photo在某个Bin里没有数据,计数为0)
bin_counts = bin_counts.pivot(index='Bin', columns='Photo', values='Count').fillna(0).stack().reset_index(name='Count')

步骤2:绘制嵌套式的直方图+箱线图

接下来我们把直方图和箱线图绘制在同一坐标轴上,调整箱线图的位置,让它刚好嵌入每个直方图的区间内:

fig, ax = plt.subplots(figsize=(10,6))

# 第一步:绘制重叠直方图,保持你原来的样式
sns.histplot(
    data=u30_diameter, 
    x="Value", 
    hue="Photo", 
    element="step", 
    bins=diameter_range,
    alpha=0.3,  # 增加透明度,避免遮挡箱线图
    ax=ax
)

# 第二步:准备箱线图的x轴位置(每个bin的中间值)
bin_midpoints = (diameter_range[:-1] + diameter_range[1:])/2
# 把Bin转换为对应的中间值,方便定位
bin_counts['Bin_Mid'] = bin_counts['Bin'].apply(lambda x: (x.left + x.right)/2)

# 绘制箱线图,调整宽度让它嵌入bin内
sns.boxplot(
    data=bin_counts,
    x='Bin_Mid',
    y='Count',
    width=0.3,  # 调整箱线图宽度,适配bin的宽度
    color='white',
    boxprops={'edgecolor':'black'},
    whiskerprops={'color':'black'},
    capprops={'color':'black'},
    medianprops={'color':'red'},
    ax=ax
)

# 调整坐标轴标签和样式
ax.set_xlabel('Diameter, μm')
ax.set_ylabel('Count')
# 设置x轴刻度为bin的区间,更清晰
ax.set_xticks(bin_midpoints)
ax.set_xticklabels([f"{left:.1f}-{right:.1f}" for left, right in zip(diameter_range[:-1], diameter_range[1:])])
plt.xticks(rotation=45)

# 调整图例位置,避免遮挡
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')

plt.tight_layout()
plt.savefig('U30_d_nested.png', facecolor='white', edgecolor='none', dpi=300)
plt.show()

关键说明

  • 自动分箱统计:用pd.cut和groupby替代手动计数,避免人为错误,同时能自动处理缺失的计数(填充为0)
  • 箱线图定位:使用每个bin的中间值作为箱线图的x坐标,确保它刚好落在对应的直方图区间内
  • 样式调整:设置直方图的透明度,箱线图用白色填充+黑色边框,中位数用红色突出,让两层可视化清晰区分
  • x轴刻度:显示每个bin的区间范围,提升可读性

这样绘制出来的图形就能实现你想要的嵌套分布可视化:每个直方图区间上叠加该区间内五组数据计数的箱线图,直观展示每组数据在不同直径区间的分布差异。

内容的提问来源于stack exchange,提问作者Oscar Espinosa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 08:12:32