How to create advanced annotations in Matplotlib

logo of a chart:ScatterPlot

With matplotlib, you can create annotations to highlight specific parts of a chart, but it's a limited tool. In a older post on how to custom titles, we have seen how to use the highlight_text package to create much better annotations with ease.

In this post, we will see how to use this package to create advanced annotations like customizing background color, creating path effects and adding title and subtitle in one annotation.

Annotations

In data visualization, annotations have a major role in available information to the reader.

They can be used to:

  • highlight specific points
  • provide additional information
  • simply explain the data in more depth

However, it can be quite technicaly challenging to customize annotations in matplotlib, especially if you want specific text highlighting such as word in bold, italic or colored differently.

Let's see how we can create nice annotations in a few examples.

Libraries

Default tools from matplotlib are unfortunately not enough to create nice annotations. We will use the highlight-text package that simplifies the process.

Keep in mind that it would have been possible to use the flexitext package that can do approximately the same thing.

And we will also need to load matplotlib and numpy libraries.

import numpy as np
import matplotlib.pyplot as plt
from highlight_text import fig_text, ax_text

Simple annotation

When creating a plot with annotation in matplotlib, we mainly use 2 functions:

  • annotate()
  • text()

Concretely, the annotate() function allows to add a text at a specific position, with an arrow pointing to a specific point, while the text() function allows to add a text at a specific position, without any arrow.

Let's see how they behave with a concrete example:

fig, ax = plt.subplots(ncols=2, figsize=(8, 5))
x = [1, 2, 3, 4]
y = [10, 11, 12, 13]

point_to_highlight = (2, 11)

# annotation with .text()
ax[0].plot(x, y)
ax[0].text(
    x=point_to_highlight[0],  # position on x-axis
    y=point_to_highlight[1],  # position on y-axis
    s='Text near the point',
    fontsize=12
)
ax[0].set_title('text()')

# annotation with .annotate()
ax[1].plot(x, y)
ax[1].annotate(
    text='Important Point',
    xy=point_to_highlight,  # position of the point
    xytext=(1, 14),  # position of the text
    arrowprops=dict(facecolor='black', shrink=0.05),
    fontsize=12
)
ax[1].set_title('        annotation()')

plt.show()

Boldness and Italics

Thanks to the highlight_text package, it is possible to bold and italic tp a specific part of the text. This is what will make the annotation stand out from the rest of the text.

The syntax is that we have to put the part of the text that we want to highlight between < and >. Then, we use the highlight_textprops argument to specify the style of the selected part.

fig, ax = plt.subplots(figsize=(8, 5))
x = [1, 2, 3, 4]
y = [10, 11, 12, 13]
point_to_highlight = (2, 11)

ax.plot(x, y)
text = '<Wow>, this annotation is <much better!>'
ax_text(
    x=point_to_highlight[0],  # position on x-axis
    y=point_to_highlight[1],  # position on y-axis
    s=text,
    fontsize=12,
    highlight_textprops=[
        {"fontstyle": 'italic'},
        {"fontweight": 'bold'}
    ],
    ax=ax
)

plt.show()

Background color

You can easily add a background color to your annotation by using the bbox argument. You can customize lots of parameters:

  • facecolor is the color of the background
  • edgecolor is the color of the border
  • linewidth is the width of the border
  • pad is the padding between the text and the border
# create a simple plot
fig, ax = plt.subplots(figsize=(6, 6))
ax.plot(
    [1, 2, 3, 4],
    [10, 11, 12, 13]
)

# add annotation on top
text = 'Wow, this annotation is  <pretty cool!>'
ax_text(
    x=1,  # position on x-axis
    y=12.5,  # position on y-axis
    s=text,
    fontsize=12,
    ax=ax,
    highlight_textprops=[
        {"bbox": {"edgecolor": "red",
                  "facecolor": "yellow",
                  "linewidth": 3,
                  "pad": 4}}
    ]
)

plt.show()

Path effects

The path effect is a way to add a shadow to your text, and it's highly customizable! In highlight_text, it relies on matplotlib's PathEffects module.

With it, we can specify:

  • linewidth is the width of the shadow
  • foreground is the color of the shadow
  • alpha is the transparency of the shadow
  • and more! You can see the official matplotlib documentation for more information.
# create a simple plot
import matplotlib.patheffects as path_effects
fig, ax = plt.subplots(figsize=(6, 6))
ax.plot(
    [1, 2, 3, 4],
    [10, 11, 12, 13]
)

# path effects


def define_path_effect(**kwargs):
    return [path_effects.Stroke(**kwargs), path_effects.Normal()]


my_path_effect = define_path_effect(
    linewidth=6,
    foreground="red",
    alpha=0.4
)


# add annotation on top
text = 'Wow, this annotation is  <pretty cool!>'
ax_text(
    x=1,  # position on x-axis
    y=12.5,  # position on y-axis
    s=text,
    fontsize=12,
    ax=ax,
    highlight_textprops=[
        {"path_effects": my_path_effect,
         "color": "yellow"}
    ]
)

plt.show()

Going further

This post explains how to create nice annotations in matplotlib with the highlight_text package.

You might be interested in how to have different colors in annotations or how to add an image in a plot

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 🙏!