【问题标题】:How to add custom annotations, from the dataframe, to a stacked bar chart?如何将自定义注释从数据框中添加到堆叠条形图?
【发布时间】:2016-09-01 19:24:25
【问题描述】:

我正在绘制特定类别中各个办公室的交叉表。我想整理一个水平堆叠的条形图,其中每个办公室及其价值都被标记。

下面是一些示例代码:

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

# create dataframe
df = pd.DataFrame({'office1': [1, np.nan, np.nan],
                   'office2': [np.nan, 8, np.nan],
                   'office3': [12, np.nan, np.nan],
                   'office4': [np.nan, np.nan, 3],
                   'office5': [np.nan, 5, np.nan],
                   'office6': [np.nan, np.nan, 7],
                   'office7': [3, np.nan, np.nan],
                   'office8': [np.nan, np.nan, 11],
                   'office9': [np.nan, 6, np.nan]},
                  index=['catA', 'catB', 'catC'])

# plot dataframe
ax = df.plot.barh(title="Office Breakdown by Category",
                  legend=False,
                  figsize=(10,7), stacked=True)

这给了我一个很好的起点:

但是,我想要的是:

经过一番研究,我想出了以下代码,可以正确排列“类别”轴上的标签:

def annotateBars(row, ax=ax):
    for col in row.index:
        value = row[col]
        if (str(value) != 'nan'):
            ax.text(value/2, labeltonum(row.name), col+","+str(value))

def labeltonum(label):
    if label == 'catA':
        return 0
    elif label == 'catB':
        return 1
    elif label == 'catC':
        return 2

df.apply(annotateBars, ax=ax, axis=1)

但这并不考虑条形的“堆叠”。我还尝试遍历 plot 命令返回的patches 容器(它可以让我检索每个矩形的 x 和 y 位置),但是我失去了与办公室标签的任何连接。

【问题讨论】:

标签: python pandas matplotlib


【解决方案1】:

想通了。如果我遍历数据框每一行的列,我可以建立一个与ax.patches 中矩形的进展相匹配的标签列表。解决方法如下:

labels = []
for j in df.columns:
    for i in df.index:
        label = str(j)+": " + str(df.loc[i][j])
        labels.append(label)

patches = ax.patches

for label, rect in zip(labels, patches):
    width = rect.get_width()
    if width > 0:
        x = rect.get_x()
        y = rect.get_y()
        height = rect.get_height()
        ax.text(x + width/2., y + height/2., label, ha='center', va='center')

当添加到上面的代码中时,会产生:

现在只处理为太小的条重新排列标签。

【讨论】:

    【解决方案2】:

    您也可以将函数 annotateBars() 更改为:

    def annotateBars(row, ax=ax):
        curr_value = 0 
        for col in row.index:
            value = row[col]
            if (str(value) != 'nan'):
                ax.text(curr_value + (value)/2, labeltonum(row.name), col+","+str(value), ha='center',va='center')
                curr_value += value
    

    【讨论】:

    • 你是对的!虽然我喜欢我想出的新答案,但现在我不需要单独的 labeltonum() 函数(我的真实数据有更多类别,有时会重新排序)。虽然最后它是一个六个,另一个六个......
    猜你喜欢
    • 2022-08-03
    • 1970-01-01
    • 2020-12-27
    • 1970-01-01
    • 1970-01-01
    • 2022-01-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多