Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to prevent alphabetical sorting for python bars with matplotlib?

I'm plotting some categorical data using a bar chart. Matplotlib keeps sorting my x-axis alphabetically even when I sort my dataframe values. Here's my code :

fig3, new_ax = plt.subplots(1,1, figsize=(25/3,5))
summary = tsa.source.sum().sort_values(ascending=False)
new_ax.bar(summary.index, summary.values, color=my_colors)
new_ax.legend()
bar_heights(new_ax) # custom function to get the values on top of bars
simpleaxis(new_ax) # custom function to define an axis to please my boss...
new_ax.set_ylabel('Effectifs')
new_ax.set_xlabel("Type d'arme")
new_ax.grid(False)

Output : enter image description here

And yet here's what summary looks like and that's the order I want to see on my chart :

famas            2214.0
aut_typebruit     759.0
grena             200.0
flg                78.0
douze              72.0
sept               53.0
dtype: float64

Here's a link to a sample of my data : https://files.fm/u/wumscb4q import it with this line :

tsa.source = pd.read_csv('sample.csv', sep=';', index_col=0)

And here my functions :

def simpleaxis(ax):
    ax.spines['top'].set_visible(False)
    ax.spines['right'].set_visible(False)
    ax.get_xaxis().tick_bottom()
    ax.get_yaxis().tick_left()

def bar_heights(axes):
    for rect in axes.containers[0]:
        height = rect.get_height()
        rect.axes.text(rect.get_x() + rect.get_width()/2., height+3,
            '%d' % int(height),
            ha='center', va='bottom')
like image 750
Martin B Avatar asked Apr 04 '18 09:04

Martin B


1 Answers

The issue

This is a bug in matplotlib < 2.2.0 where the X axis is always sorted even if the values are strings. That is why the order of the bars is reversed in the following plot.

x = ['foo', 'bar']
y = [1, 2]
plt.bar(x, y, color=['b', 'g'])

enter image description here

The fix

Upgrade matplotlib to 2.2.0 or higher. The same code produces the expected result with these versions.

enter image description here

The workaround

If you cannot or do not want to upgrade matplotlib you can use numbers instead of strings, then set the ticks and labels to the correct values:

index = range(len(x))
plt.bar(x, y, color=['b', 'g'])  # use numbers in the X axis.
plt.xticks(index, x)  # set the X ticks and labels

enter image description here

The Pandas way

You can use Series.plot which might be convenient for you since your data is already in the form of a Series, but be aware that pandas plots things differently. Use the keyword argument rot to control the rotation of the labels.

s = pd.Series(y, index=x)
s.plot(kind='bar',rot=0, color=['b', 'g'])

enter image description here

like image 70
Stop harming Monica Avatar answered Sep 20 '22 03:09

Stop harming Monica