Pandas Bar plot, как аннотировать сгруппированные горизонтальные гистограммы

Я задаю этот вопрос, потому что я не нашел рабочий пример на как аннотировать сгруппированные горизонтальные панд гистограммы еще. Я знаю о следующих двух:

но они все о столбчатых диаграммах. То есть, либо у вас нет решения для горизонтальной гистограммы, либо она не полностью работает.

после нескольких недель работы над этой проблемой я, наконец, могу задать вопрос с образцом кода, который почти то, что я хочу, просто не 100% работает. Нужна ваша помощь, чтобы достичь этого 100%.

вот полный код выложил здесь. Результат выглядит так:

Pandas chart

вы можете видеть, что он почти работает, просто ярлык не помещается там, где я хочу, и я не могу переместить их в лучшее поставить себя. Кроме того, поскольку верхняя часть диаграммы используется для отображения строки ошибок, поэтому я действительно хочу переместить текст аннотации к оси y,красиво выстраиваются на левой или правой стороне оси Y в зависимости от значения x. Например, вот что мои коллеги могут сделать с MS Excel:

MS Excel chart

возможно ли это для Python сделать это с диаграммой Pandas?

Я включаю код из моего вышеуказанного url для аннотация, один из них-это все, что я могу сделать, а другой - для справки (из In [23]):

# my all-that-I-can-do
def autolabel(rects):
    #if height constant: hbars, vbars otherwise
    if (np.diff([plt.getp(item, 'width') for item in rects])==0).all():
        x_pos = [rect.get_x() + rect.get_width()/2. for rect in rects]
        y_pos = [rect.get_y() + 1.05*rect.get_height() for rect in rects]
        scores = [plt.getp(item, 'height') for item in rects]
    else:
        x_pos = [rect.get_width()+.3 for rect in rects]
        y_pos = [rect.get_y()+.3*rect.get_height() for rect in rects]
        scores = [plt.getp(item, 'width') for item in rects]
    # attach some text labels
    for rect, x, y, s in zip(rects, x_pos, y_pos, scores):
        ax.text(x, 
                y,
                #'%s'%s,
                str(round(s, 2)*100)+'%',
                ha='center', va='bottom')

# for the reference 
ax.bar(1. + np.arange(len(xv)), xv, align='center')
# Annotate with text
ax.set_xticks(1. + np.arange(len(xv)))
for i, val in enumerate(xv):
    ax.text(i+1, val/2, str(round(val, 2)*100)+'%', va='center',
ha='center', color='black')             

пожалуйста, помогите. Спасибо.

1 ответов


Итак, я немного изменил способ построения ваших данных для простоты:

import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns 
sns.set_style("white") #for aesthetic purpose only

# fake data
df = pd.DataFrame({'A': np.random.choice(['foo', 'bar'], 100),
                   'B': np.random.choice(['one', 'two', 'three'], 100),
                   'C': np.random.choice(['I1', 'I2', 'I3', 'I4'], 100),
                   'D': np.random.randint(-10,11,100),
                   'E': np.random.randn(100)})

p = pd.pivot_table(df, index=['A','B'], columns='C', values='D')
e = pd.pivot_table(df, index=['A','B'], columns='C', values='E')

ax = p.plot(kind='barh', xerr=e, width=0.85)

for r in ax.patches:
    if r.get_x() < 0: # it it's a negative bar
        ax.text(0.25, # set label on the opposite side
                r.get_y() + r.get_height()/5., # y
                "{:" ">7.1f}%".format(r.get_x()*100), # text
                bbox={"facecolor":"red", 
                      "alpha":0.5,
                      "pad":1},
                fontsize=10, family="monospace", zorder=10)
    else:
        ax.text(-1.5, # set label on the opposite side
                r.get_y() + r.get_height()/5., # y
                "{:" ">6.1f}%".format(r.get_width()*100), 
                bbox={"facecolor":"green",
                      "alpha":0.5,
                      "pad":1},
                fontsize=10, family="monospace", zorder=10)
plt.tight_layout()

что дает:

barh plot error bar annotated

Я строю метку в зависимости от среднего значения и помещаю ее на другую сторону 0-линии, чтобы Вы были уверены, что она никогда не будет перекрываться с чем-то другим, кроме панели ошибок иногда. Я установил поле позади текста, чтобы оно отражало значение среднего. Есть некоторые значения, которые вам нужно будет настроить в зависимости от вашего размер рисунка, поэтому метки подходят правильно, например:

  • width=0.85
  • +r.get_height()/5. # y
  • "pad":1
  • fontsize=10
  • "{:" ">6.1f}%".format(r.get_width()*100): установите общее количество символов в метке (здесь, минимум 6, заполните пробел справа, если меньше 6 символов). Ему нужно family="monospace"

Скажи мне, если что-то не понятно.

HTH