Matplotlib, horizontal bar chart (barh) is upside-down

55,185

Solution 1

I believe the joint wrong order of groups and subgroups boils down to a single feature: that the y axis increases upwards, as in a usual plot. Try reversing the y axis of your axes as in this pandas-less example:

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()

Specifically for pandas, pandas.DataFrame.plot and its various plotting submethods return a matplotlib axes object, so you can invert its y axis directly:

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

Solution 2

I believe the simplest solution for this problem is to reverse the pandas dataframe before plotting. For example:

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

In my opinion that is a bug in the pandas barh function. At least users should be able to pass an argument like reverse_order = True etc.

Solution 3

I will consider this to be a bug, i.e., the y position of the bars are not assigned correctly. The patch is however relatively simple:

This is only one right order of bars, and that is called..., the right order. Anything that is not the right order, is thus a buggy order. :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

Share:
55,185
xpt
Author by

xpt

#SOreadytohelp On 2020-10-31 On 2020-09-15: On 2020-04-15: On 2020-04-12: On 2020-04-02:

Updated on September 24, 2020

Comments

  • xpt
    xpt over 3 years

    TL'DR, the vertical bar charts are shown in a conventional way -- things line up from left to right. However, when it is converted to horizontal bar chart (from bar to barh), everything is upside-down. I.e., for a grouped bar chart, not only the order of the grouped bar is wrong, the order of the each group is wrong as well.

    For e.g., the graph from http://dwheelerau.com/2014/05/28/pandas-data-analysis-new-zealanders-and-their-sheep/

    enter image description here

    If you look closely, you will find that the the bar and legend are in reverse order -- Beef shows on top in legend but on bottom in the graph.

    As the simplest demo, I changed kind='bar', to kind='barh', from this graph https://plot.ly/pandas/bar-charts/#pandas-grouped-bar-chart and the result looks like this: https://plot.ly/7/~xpt/

    I.e., the bars in the horizontal grouped bar chart is ordered upside-down.

    How to fix it?

    EDIT: @Ajean, it is actually not only the order of the grouped bar is wrong, the order of the each group is wrong as well. The graph from Simple customization of matplotlib/pandas bar chart (labels, ticks, etc.) shows it clearly:

    the order of the each group is wrong

    We can see that the order is unconventional too, because people would expect the graph to be top-down, with "AAA" at the top, not the bottom.

    If you search for "Excel upside-down", you will find people are complaining about this in Excel all over the places. The Microsoft Excel has a fix for it, do Matplotlib/Panda/Searborn/Ploty/etc has a fix for it?

  • CT Zhu
    CT Zhu over 8 years
    No, it will hardly do. You will end up reversing the order of years: 1994-2012 to its reverse order.
  • Andras Deak -- Слава Україні
    Andras Deak -- Слава Україні over 8 years
    @CTZhu but that's kinda the point, isn't it? If in a bar plot 1994->2012 goes from left to right, then for a barh one might want the same order from top to bottom. OP's edit saying "it is actually not only the order of the grouped bar is wrong, the order of the each group is wrong as well" suggested to me that this is a valid solution. If he comes and tells me that it's not, I'll delete the answer.
  • xpt
    xpt over 8 years
    @AndrasDeak, Yes, Andras, that's exactly what I wanted. Could you throw in a pandas solution as well please? This is as far I can go myself. You start from there if you like. Thanks.
  • Andras Deak -- Слава Україні
    Andras Deak -- Слава Україні over 8 years
    @xpt 1. your gist won't load for me for some reason. 2. I don't have pandas installed, hence my pandas-free answer. 3. I think you should be able to use the same solution: plt.gca() is an axes, and df.plot is supposed to return an axes too. So you should be able to do something along the lines of ax=df.plot(...); ax.invert_yaxis(). Can you confirm?
  • xpt
    xpt over 8 years
    YEP! Thank you very much! Demo here: gist.github.com/suntong/6572c4d339bdb98388a9
  • xpt
    xpt over 8 years
    @AndrasDeak and others, anyone know how to do invert_yaxis for Plotly? I'm having a hard time making it works...
  • Andras Deak -- Слава Україні
    Andras Deak -- Слава Україні over 8 years
    @xpt take a look at the pyplot online tutorial, they tell you how to invoke the help. It would seem to me that after getting a handle to a figure using py.get_figure(...), you can do something like fig['layout']['yaxis'].update(mirror=True). I don't know anything about plotly nor ipython notebook, so I can only guess. You should have access to the help, on the other hand.