1

I am using matplotlib 2.1.0 to display a horizontal bar graph. I've sorted the data by value, but matplotlib is displaying the bars in alphabetic order of their labels.

fig, [ax1,ax2] = plt.subplots(nrows=1,ncols=2, figsize = (8,3))
probs = [ 0.748, 0.052, 0.0376, 0.022, 0.017 ]
names = [ 'hibiscus', 'petunia', 'iris', 'rose', 'hyacinth' ]
ax1.barh( names, probs )

The result looks roughly like this (sorry I cannot paste the image):

    rose - **
 petunia - *****
    iris - ***
hyacinth - *
hibiscus - ************************************

As you can see, the bars are displayed in alphabetic (or reverse-alphabetic) order. I want them displayed in list order:

hibiscus - ************************************
 petunia - *****
    iris - ***
    rose - **
hyacinth - *

I found a related question here on SO (I've lost the link now) that indicated this was a problem with an older version of matplotlib. This was the suggested solution:

ax1.barh( range(len(names)), probs )
ax1.set_yticks( range(len(names)), names )

This approach displays the data in the proper order, or at least in reverse order. But it doesn't display the text labels. Instead, matplotlib labels the bars by index and ignores the names entirely.

       4 - *
       3 - **
       2 - ***
       1 - *****
       0 - ************************************

I don't have control over the version of matplotlib being used, so I'm looking for a way to tell matplotlib to display data in the proper order with the label names.

I also found this question: How to determine the order of bars in a matplotlib bar chart. That solution is using a pandas DataFrame. I'm not using pandas and it seems like overkill to convert the data to a DataFrame just to convince matplotlib to behave nicely.

How do I control the order in which matplotlib displays the bars?


Addendum

This question has been tagged as a duplicate of the other related question I had lost reference to: Pyplot sorting y-values automatically. The difference is subtle, but there is an important distinction between the two ways of calling barh(). The code in this question first uses the return value of a call to subplots() like this:

fig, [ax1,ax2] = plt.subplots(...)
ax1.barh(...)

The code in the other question uses a different interface:

plot.subplot(...)
plot.barh(...)

I don't know why these should work differently, but they do. Specifically, the answer to the other question (quoted below) did not work with the code in this question.

plt.barh(range(len(w)),n)
plt.yticks(range(len(w)),w)

So I tried that solution, but it failed.

ax1.barh(range(len(names)),probs)
 ### this next call generated a runtime error because ax1 has no method yticks()
ax1.yticks(range(len(names)),names)

However, I discovered that there was an apparently similar call, so I tried it:

ax1.barh( range(len(names)), probs )
ax1.set_yticks( range(len(names)), names )

As I discussed in the original post, this approach produced axis labels from the numeric index values and ignored the name strings.

One final remark: the actual answer to this question has been provided in the comments. I would like this question unlocked so Lorren or I can write a formal answer for anyone who runs across this same problem in the future.

Lee Jenkins
  • 2,299
  • 3
  • 24
  • 39
  • 1
    Try to use `ax1.set_yticklabels(names)` – Lorran Sutter Jul 19 '18 at 00:25
  • 1
    Thank you @LorranSutter! I had to experiment some more to discover that, in addition to your suggestion, I also had to remove the second argument to `set_yticks()` so that it looked like this: `ax1.set_yticks( range(len(names)) )`. Now it's working perfectly. Thanks! – Lee Jenkins Jul 19 '18 at 02:20

1 Answers1

0

Thanks go to @LorranSutter, who gave me the necessary hint to answer this question.

ax1.barh( range(len(names)), probs )
ax1.set_yticks( range(len(names))  )
ax1.set_yticklabels( names )
Lee Jenkins
  • 2,299
  • 3
  • 24
  • 39