Problem

For various reasons you may want to add a legend with handles that consist of squares or rectangles. For some plots, such as the ones obtained with plt.fill_between() the legend handle is going to be a rectangle by default (see this example).

However, for other types of charts, you will have to build them up from scratch.

Example

Let's see this problem live with a scatterplot:

import palmerpenguins
import matplotlib.pyplot as plt
from matplotlib.patches import Patch
import numpy as np

Load the data:

penguins = palmerpenguins.load_penguins().dropna()

Now we define a bunch of properties for the chart such as the colors and the list of species:

FLIPPER_LENGTH = penguins["flipper_length_mm"].values
BILL_LENGTH = penguins["bill_length_mm"].values

SPECIES = penguins["species"].values
SPECIES_ = np.unique(SPECIES)

COLORS = ["#1B9E77", "#D95F02", "#7570B3"]

The following code is extracted from the mentioned post on custom legends. Let's see what is the default legend we get for a scatterplot

fig, ax = plt.subplots(figsize=(8, 6))
for species, color in zip(SPECIES_, COLORS):
    idxs = np.where(SPECIES == species)
    ax.scatter(
        FLIPPER_LENGTH[idxs], BILL_LENGTH[idxs], label=species,
        s=50, color=color, alpha=0.7
    )
legend = ax.legend()

plt.show()

Here we are, a scatterplot with circles used in the legend. How to use rectangles instead?

Using rectangles in legend

Let's see how we can override this default behavior and use a rectangle instead. The following function is created to make it simpler to replicate the same plot several times.

def scatterplot():
    fig, ax = plt.subplots(figsize=(8, 6))
    for species, color in zip(SPECIES_, COLORS):
        idxs = np.where(SPECIES == species)
        ax.scatter(
            FLIPPER_LENGTH[idxs], BILL_LENGTH[idxs],
            s=50, color=color, alpha=0.7
        )
    return fig, ax

Let's generate the chart and create the handles for the legend. This is as simple as using matplotlib.patches.Patch.

fig, ax = scatterplot()

handles = [
    Patch(facecolor=color, label=label) 
    for label, color in zip(SPECIES_, COLORS)
]

ax.legend(handles=handles)

plt.show()

Customizing the rectangle

It's also possible to remove the fill and just leave the color of the borders.

fig, ax = scatterplot()

handles = [
    Patch(edgecolor=color, label=label, fill=False) 
    for label, color in zip(SPECIES_, COLORS)
]

ax.legend(handles=handles);

Different colors for fill and border

Or use one color for the fill, and another for the border:

fig, ax = scatterplot()

handles = [
    Patch(facecolor=color, edgecolor="k", label=label) 
    for label, color in zip(SPECIES_, COLORS)
]

ax.legend(handles=handles)

plt.show()

Change shape

And if you want to make them squared, you only need to set both handlelength and handleheight to the same value when creating the legend.

fig, ax = scatterplot()

handles = [
    Patch(facecolor=color, edgecolor="k", label=label, alpha=0.7) 
    for label, color in zip(SPECIES_, COLORS)
]

legend = ax.legend(handles=handles, handlelength=1.4, handleheight=1.4)

plt.show()

Going further

This post explains how to use rectangles in the legend of a chart built with matplotlib.

You might be interested in how to customize your layout and how to customize title.

Contact & Edit


👋 This document is a work by Yan Holtz. You can contribute on github, send me a feedback on twitter or subscribe to the newsletter to know when new examples are published! 🔥

This page is just a jupyter notebook, you can edit it here. Please help me making this website better 🙏!