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

基于两个分类变量的百分比条形图绘制需求(含N与%标签)

问题需求

我有一个包含两个分类变量的数据集,想要绘制y轴为百分比的条形图,条形标签需同时显示百分比和观测数N(格式如65.5% (N=12))。要求百分比按x轴的survey分组,每个survey下的level占比总和为100%(即baseline和endline各自的level百分比总和均为100%)。尝试用Seaborn的countplot但y轴是计数而非百分比,标签也不符合需求,现有代码如下:

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

data = {
'id': [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20],
'survey': ['baseline', 'endline', 'baseline', 'endline', 'baseline', 'endline', 'baseline', 'endline', 'baseline', 'endline', 'baseline', 'endline', 'baseline', 'endline', 'baseline', 'endline', 'baseline', 'endline', 'baseline', 'endline', 'baseline', 'endline', 'baseline', 'endline', 'baseline', 'endline', 'baseline', 'endline', 'baseline', 'endline', 'baseline', 'endline', 'baseline', 'endline', 'baseline', 'endline', 'baseline', 'endline', 'baseline', 'endline'],
'level': ['low', 'high', 'medium', 'low', 'high', 'medium', 'medium', 'high', 'low', 'low', 'medium', 'high', 'low', 'medium', 'low', 'high', 'low', 'low', 'medium', 'high', 'high', 'high', 'high', 'medium', 'low', 'low', 'medium', 'high', 'low', 'medium', 'high', 'medium', 'low', 'high', 'high', 'medium', 'medium', 'low', 'high', 'low']
}

df = pd.DataFrame(data)
df.survey.value_counts()

plt.figure(figsize=(8,5))
sns.countplot(x = 'survey', data = df, palette = 'rainbow', hue = 'level')

N = df.groupby(['survey','level']).count().sort_index(ascending=False)
N_it = '$\it{N}$'

labels = [
    [f'{pct:.1f}% $(N={_n})$' for pct, _n in zip(c.datavalues, n)]
    for c, n in zip(ax.containers, N.to_numpy())
]

for container, label in zip(ax.containers, labels):
    ax.bar_label(container, label, fontsize=10)
    
sns.despine(ax = ax, left = True)
ax.grid(True, axis = 'y')
ax.yaxis.set_major_formatter(PercentFormatter(100))
ax.set_xlabel('')
ax.set_ylabel('')
plt.tight_layout()
plt.show()
修正后的代码
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import numpy as np
from matplotlib.ticker import PercentFormatter

data = {
    'id': [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20],
    'survey': ['baseline', 'endline', 'baseline', 'endline', 'baseline', 'endline', 'baseline', 'endline', 'baseline', 'endline', 'baseline', 'endline', 'baseline', 'endline', 'baseline', 'endline', 'baseline', 'endline', 'baseline', 'endline', 'baseline', 'endline', 'baseline', 'endline', 'baseline', 'endline', 'baseline', 'endline', 'baseline', 'endline', 'baseline', 'endline', 'baseline', 'endline', 'baseline', 'endline', 'baseline', 'endline', 'baseline', 'endline'],
    'level': ['low', 'high', 'medium', 'low', 'high', 'medium', 'medium', 'high', 'low', 'low', 'medium', 'high', 'low', 'medium', 'low', 'high', 'low', 'low', 'medium', 'high', 'high', 'high', 'high', 'medium', 'low', 'low', 'medium', 'high', 'low', 'medium', 'high', 'medium', 'low', 'high', 'high', 'medium', 'medium', 'low', 'high', 'low']
}

df = pd.DataFrame(data)

# 计算每个survey分组下各level的计数和百分比
grouped = df.groupby(['survey', 'level'])['id'].count().reset_index(name='count')
total_per_survey = grouped.groupby('survey')['count'].transform('sum')
grouped['percentage'] = (grouped['count'] / total_per_survey) * 100

plt.figure(figsize=(8,5))
# 使用barplot绘制百分比,替代countplot的计数展示
ax = sns.barplot(x='survey', y='percentage', hue='level', data=grouped, palette='rainbow')

# 生成符合要求的标签:百分比 + (N=观测数)
labels = [f'{p:.1f}% (N={c})' for p, c in zip(grouped['percentage'], grouped['count'])]
# 给每个分组的条形添加标签
for container, label_slice in zip(ax.containers, [labels[:3], labels[3:]]):
    ax.bar_label(container, label_slice, fontsize=10)

# 调整图表样式
sns.despine(ax=ax, left=True)
ax.grid(True, axis='y')
ax.yaxis.set_major_formatter(PercentFormatter())  # 自动将y轴转为百分比格式
ax.set_xlabel('')
ax.set_ylabel('')
plt.tight_layout()
plt.show()
核心调整说明
  • 替换countplot为barplot:countplot默认展示原始计数,改用barplot直接绘制预计算的百分比,满足分组占比100%的需求
  • 预计算分组占比:先统计每个survey-level组合的观测数,再除以对应survey的总观测数,得到该level在分组内的百分比
  • 修复标签生成:直接用计算好的百分比和观测数生成标签,避免原代码中未定义ax、提取错误数值的问题
  • 优化轴格式化:使用PercentFormatter()无需额外参数,自动将y轴数值转为百分比显示

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 14:16:24