web-dev-qa-db-fra.com

Matplotlib, l'histogramme horizontal (barh) est à l'envers

TL'DR, les diagrammes à barres verticales sont représentés de manière conventionnelle - les éléments sont alignés de gauche à droite. Toutefois, lorsqu’il est converti en histogramme horizontal (de bar à barh), tout est inversé. En d'autres termes, pour un diagramme à barres groupées, non seulement l'ordre des barres est erroné, mais l'ordre de chaque groupe est également incorrect.

Par exemple, le graphique de http://dwheelerau.com/2014/05/28/pandas-data-analysis-new-zealanders-and-their-sheep/

enter image description here

Si vous regardez de plus près, vous constaterez que la barre et la légende sont dans l'ordre inverse. Beef apparaît en haut de la légende mais en bas du graphique. 

Comme démonstration la plus simple, j'ai changé kind='bar', en kind='barh', De ce graphique https://plot.ly/pandas/bar-charts/#pandas-grouped-bar-chart } _ et le résultat ressemble à ceci: https://plot.ly/7/~xpt/

C'est-à-dire que les barres du graphique à barres groupées horizontales sont ordonnées à l'envers. 

Comment le réparer?

EDIT: @Ajean, en fait, non seulement l'ordre des barres groupées est incorrect, mais l'ordre de chaque groupe est également incorrect. Le graphique de Personnalisation simple du diagramme à barres matplotlib/pandas (étiquettes, ticks, etc.) } le montre clairement:

the order of the each group is wrong

Nous pouvons voir que l'ordre est également non conventionnel, car les gens s'attendent à ce que le graphique soit top-down, avec "AAA" en haut, pas en bas. 

Si vous recherchez "Excel à l'envers", vous constaterez que les gens se plaignent à ce sujet dans Excel un peu partout. Microsoft Excel a un correctif, est-ce que Matplotlib/Panda/Searborn/Ploty/etc a un correctif? 

11
xpt

Je crois que le mauvais ordre conjoint des groupes et des sous-groupes se résume à une seule caractéristique: que l’axe y augmente vers le haut, comme dans un graphique ordinaire. Essayez d’inverser l’axe y de vos axes comme dans cet exemple sans pandas:

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()
17
Andras Deak

Je crois que la solution la plus simple à ce problème consiste à inverser le cadre de données des pandas avant de tracer. Par exemple:

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

A mon avis, c'est un bug dans la fonction des Pandas Barh. Au moins, les utilisateurs devraient pouvoir passer un argument tel que reverse_order = True, etc.

9
Philipp Schwarz

Je considérerai cela comme un bogue, c’est-à-dire que la position y des barres n’est pas affectée correctement. Le patch est cependant relativement simple:

Ceci est seulement un bon ordre de barres, et cela s'appelle ..., le bon ordre. Tout ce qui n'est pas dans le bon ordre est donc un ordre de buggy. : p

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)

 enter image description here

1
CT Zhu

La solution générale est simple:

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