【问题标题】:How to annotate barplot with percent by hue/legend group如何按色调/图例组用百分比注释条形图
【发布时间】:2021-10-21 07:15:40
【问题描述】:

我想根据色调在条形顶部添加百分比。这意味着所有红色和蓝色条分别等于 100%。

我可以使蓝条等于 100%,但红条不能。哪些部分需要修改?

导入和示例数据

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

# sample data
np.random.seed(365)
rows = 100000
data = {'Call_ID': np.random.normal(10000, 8000, size=rows).astype(int),
        'with_client_nmbr': np.random.choice([False, True], size=rows, p=[.17, .83]),
        'Type_of_Caller': np.random.choice(['Agency', 'EE', 'ER'], size=rows, p=[.06, .77, .17])}
all_call = pd.DataFrame(data)

   Call_ID  with_client_nmbr Type_of_Caller
0    11343              True             EE
1    14188              True         Agency
2    16539             False             EE
3    23630              True             ER
4    -7175              True             EE

聚合和绘图

df_agg= all_call.groupby(['Type_of_Caller','with_client_nmbr'])['Call_ID'].nunique().reset_index()

ax = sns.barplot(x='Type_of_Caller', y='Call_ID', hue='with_client_nmbr',
                 data=df_agg,palette=['orangered', 'skyblue'])

hue_order = all_call['with_client_nmbr'].unique()
df_f = sum(all_call.query("with_client_nmbr==False").groupby('Type_of_Caller')['Call_ID'].nunique())
df_t = sum(all_call.query("with_client_nmbr==True").groupby('Type_of_Caller')['Call_ID'].nunique())

for bars in ax.containers:
    if bars.get_label() == hue_order[0]:
        group_total = df_f
    else:
        group_total = df_t
    for p in ax.patches:
        width = p.get_width()
        height = p.get_height()
        x, y = p.get_xy()
        ax.annotate(f'{(height/group_total):.1%}', (x + width/2, y + height*1.02), ha='center')
plt.show()

  • print(hue_order)['False', 'True']

【问题讨论】:

  • 希望答案是有帮助的。彻底回答问题很费时间。如果您的问题已解决,请接受解决方案 位于答案左上角的 ▲/▼ 箭头下方。如果出现更好的解决方案,则可以接受新的解决方案。如果您的声望超过 15,您还可以使用 ▲/▼ 箭头对答案的有用性进行投票。 如果解决方案无法回答问题,请发表评论。 What should I do when someone answers my question?。谢谢。

标签: python pandas matplotlib annotations bar-chart


【解决方案1】:
  • 通常不需要使用seaborn 来绘制分组条形图,这只是塑造数据框的问题,通常使用.pivot.pivot_table。有关更多示例,请参阅How to create a grouped bar plot
    • 在这种情况下,将pandas.DataFrame.plot 与宽数据框一起使用会比在seaborn.barplot 中使用长数据框更容易,因为列/条形顺序和totals 重合。
    • 这将代码从 16 行减少到 8 行。
  • 请参阅此answer,了解添加注释占总人口的百分比。
  • python 3.8.11pandas 1.3.1matplotlib 3.4.2 中测试

导入和 DataFrame 转换

import pandas as pd
import matplotlib.pyplot as plt

# transform the sample data from the OP with pivot_table
dfp = all_call.pivot_table(index='Type_of_Caller', columns='with_client_nmbr', values='Call_ID', aggfunc='nunique')

# display(dfp)
with_client_nmbr  False   True
Type_of_Caller                
Agency              994   4593
EE                10554  27455
ER                 2748  11296

使用matplotlib.pyplot.bar_label

  • 需要matplotlib >= 3.4.2
  • 每一列都按顺序绘制,df.sum() 创建的pandas.Series 与数据框列的顺序相同。因此,ziptotals 到绘图容器,并使用 labels 中的值 tot 来按色调组计算百分比。
  • 使用labels 参数根据色调组百分比添加自定义注释。
    • (v.get_height()/tot)*100 在列表理解中,计算百分比。
  • 看到这个answer使用.bar_label的其他选项
# get the total value for the column
totals = dfp.sum()

# plot
p1 = dfp.plot(kind='bar', figsize=(8, 4), rot=0, color=['orangered', 'skyblue'], ylabel='Value of Bar', title="The value and percentage (by hue group)")

# add annotations
for tot, p in zip(totals, p1.containers):
    
    labels = [f'{(v.get_height()/tot)*100:0.2f}%' for v in p]
    
    p1.bar_label(p, labels=labels, label_type='edge', fontsize=8, rotation=0, padding=2)

p1.margins(y=0.2)
plt.show()

【讨论】:

    猜你喜欢
    • 2022-01-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-29
    • 1970-01-01
    • 2020-12-09
    • 1970-01-01
    相关资源
    最近更新 更多