How is order of items in matplotlib legend determined?

后端 未结 5 756
迷失自我
迷失自我 2020-12-12 17:43

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,         


        
相关标签:
5条回答
  • 2020-12-12 18:20

    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])
    
    0 讨论(0)
  • 2020-12-12 18:23

    The following function makes control of legend order easy and readable.

    You can specify the order you want by label. It will find the legend handles and labels, drop duplicate labels, and sort or partially sort them according to your given list (order). So you use it like this:

    reorderLegend(ax,['Top', 'Middle', 'Bottom'])
    

    Details are below.

    #  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

    Usage is thus like this:

    fig, ax = plt.subplots(1)
    ax.add_patch(Rectangle((1,1),1,1,color='r',label='Top',alpha=.1))
    ax.bar(1,2,label='Middle')
    ax.add_patch(Rectangle((.8,.5),1,1,color='k',label='Bottom',alpha=.1))
    legend()
    
    
    reorderLegend(ax,['Top', 'Middle', 'Bottom'])
    show()
    

    The optional unique argument makes sure to drop duplicate plot objects which have the same label.

    0 讨论(0)
  • 2020-12-12 18:26

    A simple way to sort the labels based on another list goes like this: after you add all your plots and labels to the axes, do the following steps before displaying the label.

    handles,labels = ax.get_legend_handles_labels()
    sorted_legends= [x for _,x in sorted(zip(k,labels),reverse=True)] 
    #sort the labels based on the list k
    #reverse=True sorts it in descending order
    sorted_handles=[x for _,x in sorted(zip(k,handles),reverse=True)]
    #to sort the colored handles
    ax.legend(sorted_handles,sorted_legends,bbox_to_anchor=(1,0.5), loc='center left')
    #display the legend on the side of your plot.
    

    Example:

    from matplotlib import pyplot as plt
    import numpy as np
    
    
    rollno=np.arange(1,11)
    marks_math=np.random.randint(30,100,10)
    marks_science=np.random.randint(30,100,10)
    marks_english=np.random.randint(30,100,10)
    print("Roll No. of the students: ",rollno)
    print("Marks in Math: ",marks_math)
    print("Marks in Science: ",marks_science)
    print("Marks in English: ",marks_english)
    average=[np.average(marks_math),np.average(marks_science),np.average(marks_english)] #storing the average of each subject in a list
    
    fig1=plt.figure()
    ax=fig1.add_subplot(1,1,1)
    ax.set_xlabel("Roll No.")
    ax.set_ylabel("Marks")
    ax.plot(rollno,marks_math,c="red",label="marks in math, Mean="+str(average[0]))
    ax.plot(rollno,marks_science,c="green",label="marks in science, Mean="+str(average[1]))
    ax.plot(rollno,marks_english,c="blue",label="marks in english, Mean="+str(average[2]))
    #ax.legend() # This would display the legend with red color first, green second and the blue at last
    
    #but we want to sort the legend based on the average marks which must order the labels based on average sorted in decending order
    handles,labels=ax.get_legend_handles_labels()
    sorted_legends= [x for _,x in sorted(zip(average,labels),reverse=True)] #sort the labels based on the average which is on a list
    sorted_handles=[x for _,x in sorted(zip(average,handles),reverse=True)] #sort the handles based on the average which is on a list
    ax.legend(sorted_handles,sorted_legends,bbox_to_anchor=(1,0.5), loc='center left') #display the handles and the labels on the side
    plt.show()
    plt.close()
    

    For a run that had the values like this:

    Roll No. of the students:  [ 1  2  3  4  5  6  7  8  9 10]
    Marks in Math:  [66 46 44 70 37 72 93 32 81 84]
    Marks in Science:  [71 99 99 40 59 80 72 98 91 81]
    Marks in English:  [46 64 74 33 86 49 84 92 67 35]
    The average in each subject [62.5, 79.0, 63.0]
    

    The labels would have come in the order red, green, and blue as they come in that order in the plot but we want to sort them based on the average which would give us an order green, blue and red.

    Check this image

    0 讨论(0)
  • 2020-12-12 18:30

    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

    0 讨论(0)
  • 2020-12-12 18:40

    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.

    0 讨论(0)
提交回复
热议问题