如何按行计算百分比并注释 100% 堆叠条
Posted
技术标签:
【中文标题】如何按行计算百分比并注释 100% 堆叠条【英文标题】:How to calculate percent by row and annotate 100 percent stacked bars 【发布时间】:2022-01-14 11:09:56 【问题描述】:我需要帮助在从数据框中的交叉表创建的 pandas 堆积条形图的每个部分中添加总数的百分比分布(无小数)。
这里是示例数据:
data =
'Name':['Alisa','Bobby','Bobby','Alisa','Bobby','Alisa',
'Alisa','Bobby','Bobby','Alisa','Bobby','Alisa'],
'Exam':['Semester 1','Semester 1','Semester 1','Semester 1','Semester 1','Semester 1',
'Semester 2','Semester 2','Semester 2','Semester 2','Semester 2','Semester 2'],
'Subject':['Mathematics','Mathematics','English','English','Science','Science',
'Mathematics','Mathematics','English','English','Science','Science'],
'Result':['Pass','Pass','Fail','Pass','Fail','Pass','Pass','Fail','Fail','Pass','Pass','Fail']
df = pd.DataFrame(data)
# display(df)
Name Exam Subject Result
0 Alisa Semester 1 Mathematics Pass
1 Bobby Semester 1 Mathematics Pass
2 Bobby Semester 1 English Fail
3 Alisa Semester 1 English Pass
4 Bobby Semester 1 Science Fail
5 Alisa Semester 1 Science Pass
6 Alisa Semester 2 Mathematics Pass
7 Bobby Semester 2 Mathematics Fail
8 Bobby Semester 2 English Fail
9 Alisa Semester 2 English Pass
10 Bobby Semester 2 Science Pass
11 Alisa Semester 2 Science Fail
这是我的代码:
#crosstab
pal = ["royalblue", "dodgerblue", "lightskyblue", "lightblue"]
ax= pd.crosstab(df['Name'], df['Subject']).apply(lambda r: r/r.sum()*100, axis=1)
ax.plot.bar(figsize=(10,10),stacked=True, rot=0, color=pal)
display(ax)
plt.legend(loc='best', bbox_to_anchor=(0.1, 1.0),title="Subject",)
plt.xlabel('Name')
plt.ylabel('Percent Distribution')
plt.show()
我知道我需要添加 plt.text
一些方法,但无法弄清楚。我希望将总数的百分比嵌入堆叠的条形图中。
【问题讨论】:
这个问题实际上不是重复的,因为它特别询问堆叠 【参考方案1】:让我们试试吧:
# crosstab
pal = ["royalblue", "dodgerblue", "lightskyblue", "lightblue"]
ax= pd.crosstab(df['Name'], df['Subject']).apply(lambda r: r/r.sum()*100, axis=1)
ax_1 = ax.plot.bar(figsize=(10,10), stacked=True, rot=0, color=pal)
display(ax)
plt.legend(loc='upper center', bbox_to_anchor=(0.1, 1.0), title="Subject")
plt.xlabel('Name')
plt.ylabel('Percent Distribution')
for rec in ax_1.patches:
height = rec.get_height()
ax_1.text(rec.get_x() + rec.get_width() / 2,
rec.get_y() + height / 2,
":.0f%".format(height),
ha='center',
va='bottom')
plt.show()
输出:
Subject English Mathematics Science
Name
Alisa 33.333333 33.333333 33.333333
Bobby 33.333333 33.333333 33.333333
【讨论】:
这部分“f'height:.0f%',”对我来说是一个无效的语法错误。你能帮忙吗? 是的,这是 Python 3.6+ f-string 格式。 试试这个":.0f%".format(height) 成功了! ":.0f%".format(height) 返回 33%。感谢 Scott 的所有帮助!【参考方案2】: 来自matplotlib 3.4.2
使用matplotlib.pyplot.bar_label
请参阅此answer,了解有关使用该方法的详细说明以及其他示例。
使用label_type='center'
会标注每个段的值,label_type='edge'
会标注段的累计总和。
使用pandas.DataFrame.plot
和kind='bar'
和stacked=True
最容易绘制堆叠条形图
以矢量化方式获取百分比(不带.apply
):
-
使用
pd.crosstab
获取频率计数
将ct
与axis=0
除以ct.sum(axis=1)
使用.div
和.sum
指定正确的轴很重要。
乘以 100,然后四舍五入。
.crosstab
完成此操作,因为它会生成具有正确形状的数据框以绘制堆叠条形图。 .groupby
需要进一步重塑数据框。
在python 3.10
、pandas 1.3.4
、matplotlib 3.5.0
测试
import pandas as pd
import matplotlib.pyplot as plt
# get a frequency count using crosstab
ct = pd.crosstab(df['Name'], df['Subject'])
# vectorized calculation of the percent per row
ct = ct.div(ct.sum(axis=1), axis=0).mul(100).round(2)
# display(ct)
Subject English Mathematics Science
Name
Alisa 33.33 33.33 33.33
Bobby 33.33 33.33 33.33
# specify custom colors
pal = ["royalblue", "dodgerblue", "lightskyblue", "lightblue"]
# plot
ax = ct.plot(kind='bar', figsize=(10, 10), stacked=True, rot=0, color=pal, xlabel='Name', ylabel='Percent Distribution')
# move the legend
ax.legend(title='Subject', bbox_to_anchor=(1, 1.02), loc='upper left')
# iterate through each bar container
for c in ax.containers:
# add the annotations
ax.bar_label(c, fmt='%0.0f%%', label_type='center')
plt.show()
使用label_type='edge'
注解累计和
【讨论】:
以上是关于如何按行计算百分比并注释 100% 堆叠条的主要内容,如果未能解决你的问题,请参考以下文章