0

I have recently discovered matplotlib as a much better alternative to Matlab for plots. Unfortunately, my knowledge of python is close to zero.

Considering the following minimal working example: I would like the entries in the legend to appear in the order in which they are declared. I learned from this post that the entries are ordered first based on type and then based on order (in this case, the two line plots always come before the scatter plot).

I tried to apply the methods from the above post to my example, i.e., creating handles and passing them to plt.legend, but it doesn't work. Unfortunately, I'm doing it quite blindly as I don't fully understand what I'm doing, so it might be the wrong approach or some syntax error. How would one adapt the simple code below to enforce the desired legend order?

import numpy as np
import scipy.io
from matplotlib import pyplot as plt

plt.figure(figsize=[3.3, 3.3])
plt.rcParams.update({'font.size': 8, 'text.usetex': True})

plt.scatter([1, 2, 3], [5, 10, 15], label='1st entry')
plt.plot([1, 2, 3, 4], [1, 4, 9, 16], label='2nd entry')
plt.plot([1, 2, 3, 4], [1, 2, 3, 4], label='3st entry')

plt.grid()
plt.xlabel('x')
plt.ylabel('y')
plt.legend(loc='lower right', labelspacing=0.25, handlelength=0.4, handletextpad=0.7)

plt.savefig('filename.pdf', format='pdf')

plt.show()

enter image description here

TheDon
  • 159
  • 7

1 Answers1

0

There is a solution (but I don't find it very elegant) that consists in reordering the legend after it has been automatically generated by matplotlib:

fig = plt.figure(figsize=[3.3, 3.3])
ax = fig.add_subplot(111)
plt.rcParams.update({'font.size': 8, 'text.usetex': True})

ax.scatter([1, 2, 3], [5, 10, 15], label='1st entry')
ax.plot([1, 2, 3, 4], [1, 4, 9, 16], label='2nd entry')
ax.plot([1, 2, 3, 4], [1, 2, 3, 4], label='3rd entry')

ax.grid()
ax.set_xlabel('x')
ax.set_ylabel('y')

handles,labels = ax.get_legend_handles_labels()

handles = [handles[2], handles[0], handles[1]]
labels = [labels[2], labels[0], labels[1]]

ax.legend(handles,labels,loc='lower right', labelspacing=0.25, handlelength=0.4, handletextpad=0.7)

EDIT: in a comment, OP asks for a version without creating an axis explicitly, here it is (but I would advise getting used to working with axis for making more advanced plots):

plt.figure(figsize=[3.3, 3.3])
plt.rcParams.update({'font.size': 8, 'text.usetex': True})

plt.scatter([1, 2, 3], [5, 10, 15], label='1st entry')
plt.plot([1, 2, 3, 4], [1, 4, 9, 16], label='2nd entry')
plt.plot([1, 2, 3, 4], [1, 2, 3, 4], label='3rd entry')

plt.grid()
plt.xlabel('x')
plt.ylabel('y')

handles,labels = plt.gca().get_legend_handles_labels()

handles = [handles[2], handles[0], handles[1]]
labels = [labels[2], labels[0], labels[1]]

plt.legend(handles,labels,loc='lower right', labelspacing=0.25, handlelength=0.4, handletextpad=0.7)
Guillaume Ansanay-Alex
  • 1,212
  • 2
  • 9
  • 20
  • Thank you for your answer. Is there a way to do the same without replacing "plt." with "ax." everywhere? – TheDon Apr 10 '21 at 12:11
  • `handles[::-1]`You can specify the list in reverse order like this. – r-beginners Apr 10 '21 at 12:11
  • @TheDon I edited my answer to add a version without explicit use of the axis variable, but I would advise getting used to it for the next steps of your journey using matplotlib ;-) – Guillaume Ansanay-Alex Apr 10 '21 at 12:17
  • Thanks a lot! It works perfectly. I'd really appreciate if you could explain (very briefly) why I should work with "ax." instead of "plt.". – TheDon Apr 10 '21 at 12:26
  • 1
    Very briefly: using `plt.` works perfectly for a quick single plot, but as soon as you will want to fine-tune it, or add a second plot under or next to your first one for context, it will be much easier with `ax.`. That's why, even for single plots, I now use `ax.`, so that it is easy to augment from there. But it might be a very personal approach ;-) – Guillaume Ansanay-Alex Apr 10 '21 at 14:41
  • Thanks for the suggestion, I'll take it into consideration :) – TheDon Apr 10 '21 at 15:08