I am having to reorder items in a legend, when I don't think I should have to. I try:
from pylab import *
clf()
ax=gca()
ht=ax.add_patch(Rectangle((1,1),1,1,color='r',label='Top',alpha=.01))
h1=ax.bar(1,2,label='Middle')
hb=ax.add_patch(Rectangle((1,1),1,1,color='k',label='Bottom',alpha=.01))
legend()
show()
and end up with Bottom above Middle. How can I get the right order? Is it not determined by creation order?

Update: The following can be used to force the order. I think this may be the simplest way to do it, and that seems awkward. The question is what determines the original order?
hh=[ht,h1,hb]
legend([ht,h1.patches[0],hb],[H.get_label() for H in hh])
The order is deterministic, but part of the private guts so can be changed at any time, see the code here (the self.*
elements are lists of the artists that have been added, hence the handle list is sorted first by type, second by order they were added).
If you want to explicitly control the order of the elements in your legend then assemble a list of handlers and labels like you did in the your edit.
Here's a quick snippet to sort the entries in a legend. It assumes that you've already added your plot elements with a label, for example, something like
ax.plot(..., label='label1')
ax.plot(..., label='label2')
and then the main bit:
handles, labels = ax.get_legend_handles_labels()
# sort both labels and handles by labels
labels, handles = zip(*sorted(zip(labels, handles), key=lambda t: t[0]))
ax.legend(handles, labels)
This is just a simple adaptation from the code listed at http://matplotlib.org/users/legend_guide.html
A slight variation on some other aswers. The list order
should have the same length as the number of legend items, and specifies the new order manually.
handles, labels = plt.gca().get_legend_handles_labels()
order = [0,2,1]
plt.legend([handles[idx] for idx in order],[labels[idx] for idx in order])
The following function looks for the legend handles and labels, and sorts, or partially sorts, them according to a given list (order
):
# Returns tuple of handles, labels for axis ax, after reordering them to conform to the label order `order`, and if unique is True, after removing entries with duplicate labels.
def reorderLegend(ax=None,order=None,unique=False):
if ax is None: ax=plt.gca()
handles, labels = ax.get_legend_handles_labels()
labels, handles = zip(*sorted(zip(labels, handles), key=lambda t: t[0])) # sort both labels and handles by labels
if order is not None: # Sort according to a given list (not necessarily complete)
keys=dict(zip(order,range(len(order))))
labels, handles = zip(*sorted(zip(labels, handles), key=lambda t,keys=keys: keys.get(t[0],np.inf)))
if unique: labels, handles= zip(*unique_everseen(zip(labels,handles), key = labels)) # Keep only the first of each handle
ax.legend(handles, labels)
return(handles, labels)
def unique_everseen(seq, key=None):
seen = set()
seen_add = seen.add
return [x for x,k in zip(seq,key) if not (k in seen or seen_add(k))]
The function in updated form is in cpblUtilities.mathgraph
at https://gitlab.com/cpbl/cpblUtilities/blob/master/mathgraph.py
Citations: Kevin (this page) and Markus Jarderot (How do you remove duplicates from a list whilst preserving order?).
来源:https://stackoverflow.com/questions/22263807/how-is-order-of-items-in-matplotlib-legend-determined