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

如何为带hue的多级柱状图添加柱形标签?

解决带Hue的Seaborn柱状图标注问题

问题分析

你当前通过两次调用sns.barplot绘制总用户数和购买者数的柱状图,但因共享x='age'和hue='expenses',导致两组柱子重叠,原有标注逻辑失效——核心原因是带hue参数时,柱子的位置、数量与无hue场景不同,需重新匹配柱子与数据的对应关系,同时处理重复图例问题。

解决方案

方案1:修正绘图逻辑(推荐,避免重叠)

将宽格式数据转为长格式,用hue区分用户类型(总用户/购买者)、style区分消费档位,让柱子并列显示而非重叠,简化标注逻辑:

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

df = pd.DataFrame({'age': ['20-30', '20-30', '20-30', '30-40', '30-40', '30-40', '40-50', '40-50', '40-50', '50-60', '50-60', '50-60'],
                   'expenses':['50$', '100$', '200$', '50$', '100$', '200$', '50$', '100$', '200$', '50$', '100$', '200$'],
                   'users': [59, 42, 57, 68, 47, 98, 75, 73, 54, 81, 52, 43],
                   'buyers': [22, 35, 18, 27, 12, 57, 19, 29, 31, 47, 10, 5],
                   'percentage': [37.2881, 83.3333, 31.5789, 39.7058, 25.5319, 58.1632, 25.3333, 39.7260, 57.4074, 58.0246, 19.2307, 11.6279]})

# 转换为长格式,合并users和buyers为统一列
df_melt = df.melt(id_vars=['age', 'expenses', 'percentage'],
                  value_vars=['users', 'buyers'],
                  var_name='user_type',
                  value_name='count')

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

# 绘制分组柱状图:x为年龄,hue区分用户类型,style区分消费档位
sns.barplot(x='age', y='count', hue='user_type', style='expenses',
            data=df_melt, palette={'users': '#639df0', 'buyers': '#1a56db'},
            hatch={'buyers': '//'}, edgecolor='grey', ax=ax)

# 添加标注
patches = ax.patches
for idx, patch in enumerate(patches):
    # 匹配对应数据索引
    data_idx = idx // 2 * 3 + idx % 3
    count_val = df_melt['count'].iloc[data_idx]
    # 标注柱子数值
    ax.text(patch.get_x() + patch.get_width()/2, patch.get_height(),
            f'{count_val}', ha='center', va='bottom', fontsize=12)
    
    # 购买者柱子额外标注转化率百分比
    if df_melt['user_type'].iloc[data_idx] == 'buyers':
        pct_val = df_melt['percentage'].iloc[data_idx]
        ax.text(patch.get_x() + patch.get_width()/2, patch.get_height() + 3,
                f'({pct_val:.1f}%)', ha='center', va='bottom', fontsize=10, color='#8b0000')

# 调整图例与图表样式
ax.legend(loc='upper right', bbox_to_anchor=(1.15, 1), fontsize=12)
plt.title('不同年龄组的用户数与购买者数分布', fontsize=16)
plt.xlabel('年龄区间', fontsize=14)
plt.ylabel('人数', fontsize=14)
plt.tick_params(labelsize=12)
plt.show()

方案2:保留原重叠绘图逻辑

若需保持原有的重叠绘制(总用户柱为底层,购买者柱为上层),可通过调整柱子宽度区分两组,并分别标注:

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

# 绘制总用户柱,设置较大宽度
users_plot = sns.barplot(x='age', y='users', data=df, hue='expenses',
                         palette='Blues', edgecolor='grey', alpha=0.7, ax=ax, width=0.8)
# 绘制购买者柱,设置较小宽度避免完全重叠
buyers_plot = sns.barplot(x='age', y='buyers', data=df, hue='expenses',
                          palette='Blues', edgecolor='darkgrey', hatch='//', ax=ax, width=0.4)

# 标注总用户数值
for i, patch in enumerate(users_plot.patches):
    height = patch.get_height()
    ax.text(patch.get_x() + patch.get_width()/2, height,
            f'{df["users"].iloc[i]}', ha='center', va='bottom', fontsize=12)

# 标注购买者数值与百分比
for i, patch in enumerate(buyers_plot.patches):
    height = patch.get_height()
    ax.text(patch.get_x() + patch.get_width()/2, height,
            f'{df["buyers"].iloc[i]}\n({df["percentage"].iloc[i]:.1f}%)',
            ha='center', va='bottom', fontsize=10)

# 合并重复图例
handles, labels = ax.get_legend_handles_labels()
unique_handles = handles[:3] + handles[6:9]
unique_labels = labels[:3] + ['购买者_' + lab for lab in labels[6:9]]
ax.legend(unique_handles, unique_labels, loc='upper right', fontsize=12)

plt.show()

关键要点

  • 带hue的柱状图中,ax.patches的顺序为:按x轴类别分组,每组内按hue的排序依次排列,需严格匹配数据索引顺序。
  • 多组柱子场景下,优先用长格式数据统一绘制,可大幅降低标注复杂度。
  • 标注时可灵活调整文本位置(顶部/内部)、字体大小与颜色,提升图表可读性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 11:15:27