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

如何用Pandas绘制带双重分组轴的水平堆叠条形图

问题:如何实现带主次分组标签的水平堆叠条形图

我正在绘制一幅图表,需要在一个轴上展示两个分组(major_group和minor_group),第三个分组(legend_group)作为填充值。但执行以下代码绘图后,y轴显示为元组形式:

import numpy as np
import pandas as pd

data_dict = {'major_group': list(np.array([['A']*10, ['B']*10]).flat), 
'minor_group': ['q ','r ','s ','t ']*5,
'legend_group':np.repeat(['d','e','f','g','h','i'],[7,3,1,5,1,3])}
(pd.DataFrame(data= data_dict)
.groupby(['major_group', 'minor_group','legend_group'], observed = True)
.size()
.unstack()
.plot(kind='barh', stacked=True))

当前y轴会显示('A', 'q ')这类元组形式的标签,我期望实现的是将major_group作为大组标签、minor_group作为子标签的分组展示效果。


解决方案

要实现这种主次分组的y轴展示,需要手动自定义轴标签和布局,pandas默认不会自动处理多级索引的分组展示。以下是具体实现方式:

方法1:加粗区分大组标签

通过修改y轴标签文本,将大组标签加粗并与子标签合并,快速实现视觉区分:

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

# 数据准备
data_dict = {'major_group': list(np.array([['A']*10, ['B']*10]).flat), 
             'minor_group': ['q ','r ','s ','t ']*5,
             'legend_group':np.repeat(['d','e','f','g','h','i'],[7,3,1,5,1,3])}
df = pd.DataFrame(data=data_dict)

# 分组统计并填充缺失值
grouped = df.groupby(['major_group', 'minor_group','legend_group'], observed=True).size().unstack(fill_value=0)

# 绘图
fig, ax = plt.subplots(figsize=(10,6))
grouped.plot(kind='barh', stacked=True, ax=ax)

# 自定义y轴标签
y_labels = grouped.index
new_ytick_labels = []
current_major = None

for major, minor in y_labels:
    if major != current_major:
        # 首次出现的大组,加粗显示大组名
        new_ytick_labels.append(f'**{major}**\n{minor}')
        current_major = major
    else:
        new_ytick_labels.append(minor)

ax.set_yticklabels(new_ytick_labels)
plt.tight_layout()
plt.show()

方法2:添加分组分隔线与独立大组标题

如果想要更规整的分组效果(类似表格的合并单元格标题),可以添加分隔线和独立的大组标题:

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

# 数据准备与分组统计
data_dict = {'major_group': list(np.array([['A']*10, ['B']*10]).flat), 
             'minor_group': ['q ','r ','s ','t ']*5,
             'legend_group':np.repeat(['d','e','f','g','h','i'],[7,3,1,5,1,3])}
df = pd.DataFrame(data=data_dict)
grouped = df.groupby(['major_group', 'minor_group','legend_group'], observed=True).size().unstack(fill_value=0)

# 绘图
fig, ax = plt.subplots(figsize=(10,6))
grouped.plot(kind='barh', stacked=True, ax=ax)

# 整理大组对应的y轴位置范围
major_ranges = {}
for idx, (major, _) in enumerate(grouped.index):
    if major not in major_ranges:
        major_ranges[major] = [idx, idx]
    else:
        major_ranges[major][1] = idx

# 绘制分组分隔线和大组标题
for major, (start_idx, end_idx) in major_ranges.items():
    # 绘制分组顶部的分隔线
    ax.axhline(y=end_idx + 0.5, color='#333333', linewidth=1.2)
    # 计算大组标题的垂直中心位置
    center_y = (start_idx + end_idx) / 2
    # 在y轴外侧添加大组标题
    ax.text(-0.08, center_y, major, ha='right', va='center', 
            fontweight='bold', fontsize=12, transform=ax.get_yaxis_transform())

# 仅保留子标签作为y轴刻度文本
ax.set_yticklabels([minor for _, minor in grouped.index])
plt.tight_layout()
plt.show()

关于主次轴标签的说明

不需要设置真正的主次轴,通过上述自定义y轴标签的方式,就能实现视觉上的主次分组效果——将major_group作为大组标题,minor_group作为子标签,完美匹配你期望的展示样式。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 02:55:08