简体   繁体   English

如何在 Matplotlib 中向堆栈百分比条形图添加注释

[英]How to add annotations to Stack Percentage Barplot in Matplotlib

I want to add values to stacked bar chart using matplotlib.我想使用 matplotlib 向堆积条形图添加值。 So far I've been able to create the stacked bar chart, but I am confused on how to add the annotations.到目前为止,我已经能够创建堆积条形图,但我对如何添加注释感到困惑。

A similar question has been answered here , but for ggplot. 这里已经回答一个类似的问题,但对于 ggplot。

I want an output similar to that not the entire graph but just the annotations in middle.我想要的输出类似于不是整个图形,而是中间的注释。

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


data = {'Range':['<10','>10', '>= 20', '<10','>10', '>= 20', '<10','>10', '>= 20'],
    'Price':[50,25,25,70,20,10,80,10,10]
    'Value':[100,50,50,140,40,20,160,20,20]}    

df1 = pd.DataFrame(data)

b1 = df1[(df1['Range'] == '<10']['Price']
b2 = df1[df1['Range'] == '>10']['Price']
b3 = df1[df1['Range'] == '>= 20']['Price']

totals = [i+j+k for i,j,k in zip(b1,b2,b3)]
greenBars = [i / j * 100 for i,j in zip(b1, totals)]
orangeBars = [i / j * 100 for i,j in zip(b2, totals)]
blueBars = [i / j * 100 for i,j in zip(b3, totals)]


barWidth = 0.5

names = ('low', 'medium', 'high')
r = [0,1,2]
plt.bar(r, greenBars, color='#b5ffb9', edgecolor='white', width=barWidth, label = '$<10')
plt.bar(r, orangeBars, bottom=greenBars, color='#f9bc86', edgecolor='white', width=barWidth, label = '$>10')
plt.bar(r, blueBars, bottom=[i+j for i,j in zip(greenBars, orangeBars)], color='#a3acff', edgecolor='white', width=barWidth, label = '$>=20')


plt.xticks(r, names)
plt.xlabel("group")

plt.legend(loc='upper left', bbox_to_anchor=(1,1), ncol=1)

plt.show()

Added code above to create the stacked plot.添加了上面的代码以创建堆叠图。 Desired Output:期望输出:

For Low category add annotations on stacks by extracting values from column Value that would be 100, 50 & 50通过提取从列值上叠层低类别添加注解Value ,这将是100,50&50

For Medium values would be 140, 40 & 20.对于中值,值为 140、40 和 20。

For High values would be 160, 20 & 20.高值将是 160、20 和 20。

  • This answer will simplify the plotting, based on code from Quang Hoang这个答案将根据Quang Hoang 的代码简化绘图
  • A bar plot can be annotated by extracting the bar position locations from ax.patches .条形图可以通过从ax.patches提取条形位置来ax.patches
    • The patch data doesn't contain labels corresponding to the dataframe, so associating different sets of data values becomes a bespoke process.补丁数据不包含与数据框对应的标签,因此关联不同的数据值集成为一个定制的过程。
  • In order to annotate with Value instead of Price , there needs to be a way to associate the corresponding values.为了使用Value而不是Price进行注释,需要有一种方法来关联相应的值。
    • A dictionary won't work, because there are repeat values字典不起作用,因为有重复值
    • Make a pivoted dataframe for Value and a corresponding dataframe for Price .Value制作一个透视数据框,为Price一个相应的数据框。 This will ensure corresponding data is in the same location.这将确保相应的数据位于同一位置。
  • col_idx and row_idx will be used with .iloc to find the correct value in df_value , with which to annotate the plot. col_idxrow_idx将被用来.iloc找到正确的值df_value ,与注释的情节。
    • col_idx and row_idx can both be reset or updated in if i%3 == 0 , because there are 3 bars and 3 segments, however, if there are differing numbers of bars and segments, there will need to be different reset conditions. col_idxrow_idx都可以在if i%3 == 0重置或更新,因为有 3 个柱和 3 个段,但是,如果柱和段的数量不同,则需要不同的重置条件。
import pandas as pd
import matplotlib.pyplot as plt

# create the dataframe
data = {'Range':['<10','>10', '>= 20', '<10','>10', '>= 20', '<10','>10', '>= 20'],
        'Price':[50,25,25,70,20,10,80,10,10],
        'Value':[100,50,50,140,40,20,160,20,20]}    

df1 = pd.DataFrame(data)

# pivot the price data
df_price = df1.assign(idx=df1.groupby('Range').cumcount()).pivot(index='idx', columns='Range', values='Price')

Range  <10  >10  >= 20
idx                   
0       50   25     25
1       70   20     10
2       80   10     10

# pivot the value data
df_value = df1.assign(idx=df1.groupby('Range').cumcount()).pivot(index='idx', columns='Range', values='Value')

Range  <10  >10  >= 20
idx                   
0      100   50     50
1      140   40     20
2      160   20     20

# set colors
colors = ['#b5ffb9', '#f9bc86', '#a3acff']

# plot the price
ax = df_price.plot.bar(stacked=True, figsize=(8, 6), color=colors, ec='w')

# label the x-axis
plt.xticks(ticks=range(3), labels=['low', 'med', 'high'], rotation=0)

# x-axis title
plt.xlabel('group')

# position the legend
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')

# annotate the bar segments
# col and row iloc indices for df_value
col_idx = 0
row_idx = 0

# iterate through each bar patch from ax
for i, p in enumerate(ax.patches, 1):

    left, bottom, width, height = p.get_bbox().bounds
    v = df_value.iloc[row_idx, col_idx]
    if width > 0:
        ax.annotate(f'{v:0.0f}', xy=(left+width/2, bottom+height/2), ha='center', va='center')

        # use this line to add commas for thousands
#        ax.annotate(f'{v:,}', xy=(left+width/2, bottom+height/2), ha='center', va='center')
    
    row_idx += 1
    if i%3 == 0:  # there are three bars, so update the indices 
        col_idx += 1
        row_idx = 0

在此处输入图片说明

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM