为堆叠水平条形图注释数值

5

我正在尝试使用pandas创建一个堆叠的水平条形图,并为其注释值。以下是当前代码:

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

d = {'group 1': [1, 2, 5, 7, 4, 5, 10],
     'group 2': [5, 6, 1, 8, 2, 6, 2],
     'group 3': [12, 2, 2, 4, 4, 8, 4]}
df = pd.DataFrame(d)

ax = df.plot.barh(stacked=True, figsize=(10,12))

for p in ax.patches:
    ax.annotate(str(p.get_x()), xy=(p.get_x(), p.get_y()+0.2))

plt.legend(bbox_to_anchor=(0, -0.15), loc=3, prop={'size': 14}, frameon=False)

问题在于我使用的注释方法给出了x轴起始点,而不是每个段落的值。我希望能够为每个柱状图的每个段落注释中心点的每个段落的值。
编辑:为了清晰起见,我想实现的是像这样水平(和垂直)居中每个段落的值的效果。
3个回答

7
您可以使用补丁bbox来获取所需信息。
ax = df.plot.barh(stacked=True, figsize=(10, 12))
for p in ax.patches:
    left, bottom, width, height = p.get_bbox().bounds
    ax.annotate(str(width), xy=(left+width/2, bottom+height/2), 
                ha='center', va='center')

enter image description here


4

另一个可能的解决方案是通过 values = df.values.flatten("F") 将您的 df.values 转换为扁平数组。

%matplotlib inline
import pandas as pd 
import matplotlib.pyplot as plt
import numpy as np

d = {'group 1': [1, 2, 5, 7, 4, 5, 10],
     'group 2': [5, 6, 1, 8, 2, 6, 2],
     'group 3': [12, 2, 2, 4, 4, 8, 4]}
df = pd.DataFrame(d)

ax = df.plot.barh(stacked=True, figsize=(10,12))

values = df.values.flatten("F")

for i, p in enumerate(ax.patches):
    ax.annotate(str(values[i]), xy=(p.get_x()+ values[i]/2, p.get_y()+0.2))

plt.legend(bbox_to_anchor=(0, -0.15), loc=3, prop={'size': 14}, frameon=False);

enter image description here


0
  • matplotlib 3.4.0 开始使用 matplotlib.pyplot.bar_label
    • labels 参数可用于自定义注释,但不是必需的。
    • 有关详细信息和示例,请参见此 answer
  • 必须遍历每组容器以添加标签。
  • python 3.10pandas 1.4.2matplotlib 3.5.1 中测试通过

水平堆叠

d = {'group 1': [1, 2, 5, 7, 4, 5, 10],
     'group 2': [5, 6, 1, 8, 2, 6, 2],
     'group 3': [12, 2, 2, 4, 4, 8, 4]}
df = pd.DataFrame(d)

# add tot to sort the bars
df['tot'] = df.sum(axis=1)

# sort
df = df.sort_values('tot')

# plot all columns except tot
ax = df.iloc[:, :-1].plot.barh(stacked=True, figsize=(10, 12))

# iterate through each group of bars
for c in ax.containers:

    # format the number of decimal places (if needed) and replace 0 with an empty string
    labels = [f'{w:.0f}' if (w := v.get_width()) > 0 else '' for v in c ]
    
    ax.bar_label(c, labels=labels, label_type='center')

enter image description here

水平分组

  • 不堆叠是数据更好的展示方式,因为可以更容易地通过视觉比较条形长度。
# plot all columns except tot
ax = df.iloc[:, :-1].plot.barh(stacked=False, figsize=(8, 9))

# iterate through each group of bars
for c in ax.containers:

    # format the number of decimal places (if needed) and replace 0 with an empty string
    labels = [f'{w:.0f}' if (w := v.get_width()) > 0 else '' for v in c ]
    
    ax.bar_label(c, labels=labels, label_type='center')

enter image description here

df 视图

   group 1  group 2  group 3  tot
2        5        1        2    8
1        2        6        2   10
4        4        2        4   10
6       10        2        4   16
0        1        5       12   18
3        7        8        4   19
5        5        6        8   19

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接