Matplotlib, горизонтальная гистограмма (barh) перевернута

TL'DR, вертикальные гистограммы показаны обычным способом - все выстраивается слева направо. Однако, когда он конвертируется в горизонтальную гистограмму (из bar в barh), все переворачивается. То есть, для сгруппированной гистограммы не только порядок сгруппированной гистограммы неправильный, но и порядок каждой группы неправильный.

Например, график из http://dwheelerau.com/2014/05/28/pandas-data-analysis-new-zealanders-and-their-sheep/

enter image description here

Если вы внимательно присмотритесь, вы обнаружите, что полоса и легенда расположены в обратном порядке - говядина показана сверху в легенде, но внизу на графике.

В качестве простейшей демонстрации я изменил kind='bar', на kind='barh', из этого графика https://plot.ly/pandas/bar-charts/#pandas-grouped-bar-chart и результат выглядит так: https://plot.ly/7/~xpt/

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

Как это исправить?

ОБНОВЛЕНИЕ: @Ajean, это на самом деле не только неправильный порядок сгруппированной панели, неправильный порядок каждой группы. График из простой настройки гистограммы matplotlib/pandas (метки, метки и т.д.) четко показывает это:

the order of the each group is wrong

Мы видим, что порядок тоже нетрадиционный, потому что люди ожидают, что график будет сверху вниз, с "AAA" сверху, а не снизу.

Если вы ищете "Excel вверх ногами", вы обнаружите, что люди жалуются на это в Excel повсюду. В Microsoft Excel есть исправление, есть ли у Matplotlib/Panda/Searborn/Ploty/etc исправление?

Ответ 1

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

import numpy as np
import matplotlib.pyplot as plt

x=range(5)
y=np.random.randn(5)

#plot1: bar
plt.figure()
plt.bar(x,y)

#plot2: barh, wrong order
plt.figure()
plt.barh(x,y)

#plot3: barh with correct order: top-down y axis
plt.figure()
plt.barh(x,y)
plt.gca().invert_yaxis()

Специально для панд pandas.DataFrame.plot и его различные подметоды построения возвращают объект осей matplotlib, так что вы можете напрямую инвертировать его ось y:

ax = df.plot.barh()  # or df.plot(), or similar
ax.invert_yaxis()

Ответ 2

Я считаю, что самым простым решением этой проблемы является обратный фреймворк pandas перед построением графика. Например:

df = df.iloc[::-1]
df.plot.barh(stacked=True);

По-моему, это ошибка в функции pandas barh. По крайней мере, пользователи должны иметь возможность передавать аргумент, например reverse_order = True и т.д.

Ответ 3

Я считаю, что это ошибка, т.е. позиция y в барах неправильно назначена. Патч, однако, относительно прост:

Это только один правильный порядок баров, и это называется..., правильный порядок. Все, что не соответствует правильному порядку, является, таким образом, ошибкой.: Р

In [63]:

print df
      Total_beef_cattle  Total_dairy_cattle  Total_sheep  Total_deer  \
1994           0.000000            0.000000     0.000000    0.000000   
2002         -11.025827           34.444950   -20.002034   33.858009   
2003          -8.344764           32.882482   -20.041908   37.229441   
2004         -11.895128           34.207998   -20.609926   42.707754   
2005         -12.366101           32.506699   -19.379727   38.499840   

      Total_pigs  Total_horses  
1994    0.000000      0.000000  
2002  -19.100637     11.811093  
2003  -10.766476     18.504488  
2004   -8.072078     13.376472  
2005  -19.230733   -100.000000  
In [64]:

ax = df.plot(kind='barh', sort_columns=True)

#Get the actual bars
bars = [item for item in ax.get_children() if isinstance(item, matplotlib.patches.Rectangle)]
bars = bars[:df.size]

#Reset the y positions for each bar
bars_y = [plt.getp(item, 'y') for item in bars]
for B, Y in zip(bars, np.flipud(np.array(bars_y).reshape(df.shape[::-1])).ravel()):
    B.set_y(Y)

введите описание изображения здесь

Ответ 4

Общее исправление прост:

handles, labels = axis.get_legend_handles_labels()
# reverse to keep order consistent
axis.legend(reversed(handles), reversed(labels), loc='upper left')