117

I am using matlibplot and I would like to manually add items to the legend that are a color and a label. I am adding data to to the plot to specifying there would lead to a lot of duplicates.

My thought was to do:

    ax2.legend(self.labels,colorList[:len(self.labels)])
    plt.legend()

Where self.labels is the number of items I want legend lables for that takes a subset of the large color list. However this yields nothing when I run it.

Am I missing anything?

Thanks

Trenton McKinney
  • 43,885
  • 25
  • 111
  • 113
Brady forcier
  • 1,217
  • 2
  • 10
  • 10
  • 1
    Possible duplicate of [How to make custom legend in matplotlib](http://stackoverflow.com/questions/13303928/how-to-make-custom-legend-in-matplotlib) – gabra Sep 14 '16 at 22:06

6 Answers6

139

Have you checked the Legend Guide?

For practicality, I quote the example from the guide.

Not all handles can be turned into legend entries automatically, so it is often necessary to create an artist which can. Legend handles don’t have to exists on the Figure or Axes in order to be used.

Suppose we wanted to create a legend which has an entry for some data which is represented by a red color:

import matplotlib.patches as mpatches
import matplotlib.pyplot as plt

red_patch = mpatches.Patch(color='red', label='The red data')
plt.legend(handles=[red_patch])

plt.show()

enter image description here

Edit

To add two patches you can do this:

import matplotlib.patches as mpatches
import matplotlib.pyplot as plt

red_patch = mpatches.Patch(color='red', label='The red data')
blue_patch = mpatches.Patch(color='blue', label='The blue data')

plt.legend(handles=[red_patch, blue_patch])

enter image description here

gabra
  • 7,934
  • 3
  • 26
  • 42
  • 1
    How would this work for adding multiple? Can I make a list of patches and then just do plt.legend(handles=patchList) – Brady forcier Sep 14 '16 at 23:09
  • 2
    @Bradyforcier exactly. – gabra Sep 14 '16 at 23:26
  • I tried to add my custom patches like this and I see two black dots and Patch'> in the legend. I tried adding the red_patch above and I get the same thing. Patch was added as plt.legend(mpatches.Patch(color='red', label='The red data')). Turns out the version I'm stuck with is super old 0.99. Not sure how to find out how this older API works – Brady forcier Sep 15 '16 at 15:33
  • @Bradyforcier I added an example with two patches. I am not sure it will work on v0.99. – gabra Sep 15 '16 at 18:14
  • is it also possible to assign one color manually to one specific value? – Varlor Jul 24 '17 at 12:41
  • Instead of `mpatches` can we do "lines"? – Tanasis Nov 08 '18 at 15:05
  • how can one add markers to the legend without using the funny example with a Line2D in the background of a marker? Thanks! – whiletrue Feb 21 '19 at 08:19
88

For those wanting to add manual legend items into a single/common legend with automatically generated items:

#Imports
import matplotlib.patches as mpatches

# where some data has already been plotted to ax
handles, labels = ax.get_legend_handles_labels()

# manually define a new patch 
patch = mpatches.Patch(color='grey', label='Manual Label')

# handles is a list, so append manual patch
handles.append(patch) 

# plot the legend
plt.legend(handles=handles, loc='upper center')

Example of common legend with manual and auto-generated items:
example of common legend with manual and auto-generated items

ADDED 2021-05-23

complete example with manual line and patch

import matplotlib.pyplot as plt
from matplotlib.lines import Line2D
import matplotlib.patches as mpatches

plt.plot([1,2,3,4], [10,20,30,40], label='My Data', color='red')

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

patch = mpatches.Patch(color='grey', label='manual patch')   
line = Line2D([0], [0], label='manual line', color='k')

handles.extend([patch,line])

plt.legend(handles=handles)
plt.show()

enter image description here

CreekGeek
  • 1,319
  • 1
  • 12
  • 18
57

Here's a solution which let's you control the width and style of your legend lines (among a lot of other things).

import matplotlib.pyplot as plt
from matplotlib.lines import Line2D

colors = ['black', 'red', 'green']
lines = [Line2D([0], [0], color=c, linewidth=3, linestyle='--') for c in colors]
labels = ['black data', 'red data', 'green data']
plt.legend(lines, labels)
plt.show()

output of the code above

For even more options, take a look at this matplotlib gallery sample.

R2-D2
  • 1,394
  • 11
  • 24
27

I'm adding some code to build on the answer from https://stackoverflow.com/users/2029132/gabra and the comment from https://stackoverflow.com/users/5946578/brady-forcier. Here, I manually add elements to a legend via a 'for' loop.

First I create a dictionary with my legend names and desired colours. I actually do this as I load in my data, but here I'm just explicitly defining:

import matplotlib.patches as mpatches
import matplotlib.pyplot as plt    

legend_dict = { 'data1' : 'green', 'data2' : 'red', 'data3' : 'blue' }

Then I loop through the dictionary and for each entry define a patch and append to a list, 'patchList'. I then use this list to create my legend.

patchList = []
for key in legend_dict:
        data_key = mpatches.Patch(color=legend_dict[key], label=key)
        patchList.append(data_key)

plt.legend(handles=patchList)
plt.savefig('legend.png', bbox_inches='tight')

Here's my output: legend example

I'm not bothered about the legend entries being in a particular order, but you could probably achieve this with

plt.legend(handles=sorted(patchList))

This is my first answer, so apologies in advance for any errors/faux pas.

birac
  • 498
  • 8
  • 18
1

I ended up writing this out:

def plot_bargraph_with_groupings(df, groupby, colourby, title, xlabel, ylabel):
    """
    Plots a dataframe showing the frequency of datapoints grouped by one column and coloured by another.
    df : dataframe
    groupby: the column to groupby
    colourby: the column to color by
    title: the graph title
    xlabel: the x label,
    ylabel: the y label
    """

    import matplotlib.patches as mpatches

    # Makes a mapping from the unique colourby column items to a random color.
    ind_col_map = {x:y for x, y in zip(df[colourby].unique(),
                               [plt.cm.Paired(np.arange(len(df[colourby].unique())))][0])}


    # Find when the indicies of the soon to be bar graphs colors.
    unique_comb = df[[groupby, colourby]].drop_duplicates()
    name_ind_map = {x:y for x, y in zip(unique_comb[groupby], unique_comb[colourby])}
    c = df[groupby].value_counts().index.map(lambda x: ind_col_map[name_ind_map[x]])

    # Makes the bargraph.
    ax = df[groupby].value_counts().plot(kind='bar',
                                         figsize=FIG_SIZE,
                                         title=title,
                                         color=[c.values])
    # Makes a legend using the ind_col_map
    legend_list = []
    for key in ind_col_map.keys():
        legend_list.append(mpatches.Patch(color=ind_col_map[key], label=key))

    # display the graph.
    plt.legend(handles=legend_list)
    ax.set_xlabel(xlabel)
    ax.set_ylabel(ylabel)
tlanigan
  • 699
  • 1
  • 8
  • 19
0

An easy way is to just force the labels if they are needed:

# add the following line after the code that plots your graph

plt.legend(['Name1', 'Name2'])

Example:

axes[ax] = sns.lineplot(x='days', y='value', data=dataset, palette = 'coolwarm', lw=5)
axes[ax].set_title('Value Across Days')
axes[ax].set_xlabel('Days')
axes[ax].set_ylabel('Value')
axes[ax].legend(['Measurement1', 'Measurement2'], loc='best')

Result:

enter image description here

Guillaume Jacquenot
  • 10,118
  • 5
  • 41
  • 48
PeJota
  • 159
  • 2
  • 8