Lesson 20 · Data visualisation in python
How to Use Annotations and Highlights to Enhance Data Visualization in Plots
In this lesson, you'll learn how to add notes and highlights to your plots. We will use real-world time series data to make our graphs clear and…
- CourseData visualisation in python
- Lesson20 of 34
- Video14 min
- FormatJupyter notebook · 17 code cells
What you'll learn
Data
No separate download needed — the notebook creates or downloads everything it uses.
📓 Full notebook
Download .ipynbWelcome to Python Plot Annotations & Highlights!#
In this lesson, you'll learn how to add notes and highlights to your plots. We will use real-world time series data to make our graphs clear and informative. By the end, you'll be able to add arrows, boxes, and colored spans to your charts.
Let's get started!
# Let's import the basic libraries we need for plotting.
import warnings; warnings.filterwarnings('ignore')
import pandas as pd
import matplotlib.pyplot as plt
# Data setup
url = "https://raw.githubusercontent.com/jbrownlee/Datasets/master/airline-passengers.csv"
df = pd.read_csv(url)
df.head()
# Check the shape of the data
print('Shape:', df.shape)
# Convert Month to datetime for proper plotting
df['Month'] = pd.to_datetime(df['Month'])
df = df.set_index('Month')
# Plot the basic time series
plt.figure(figsize=(10,5))
plt.plot(df.index, df['Passengers'], label='Monthly Passengers')
plt.title('Monthly Airline Passengers')
plt.xlabel('Month')
plt.ylabel('Number of Passengers')
plt.legend()
plt.show()
Why Add Annotations?#
Annotations help highlight important points in your data. You can add arrows, text notes, or colored areas to tell a story with your chart.
We use them to draw attention or explain special moments in the data.
# Add a simple text annotation
plt.figure(figsize=(10,5))
plt.plot(df.index, df['Passengers'], label='Monthly Passengers')
plt.title('Passengers with Text Annotation')
plt.xlabel('Month')
plt.ylabel('Number of Passengers')
plt.legend()
plt.annotate('See big jump',
xy=(df.index[20], df['Passengers'].iloc[20]),
xytext=(df.index[10], 250),
arrowprops=dict(facecolor='black', arrowstyle='->'))
plt.show()
# Highlight the highest point
max_idx = df['Passengers'].idxmax()
max_val = df['Passengers'].max()
plt.figure(figsize=(10,5))
plt.plot(df.index, df['Passengers'], label='Monthly Passengers')
plt.scatter([max_idx], [max_val], color='red', zorder=5)
plt.annotate(f'Peak: {max_val}', (max_idx, max_val),
xytext=(max_idx, max_val+30),
ha='center', color='red',
arrowprops=dict(facecolor='red', arrowstyle='->'))
plt.title('Highlight the Peak')
plt.legend()
plt.show()
# Drawing an important time window using a colored span
plt.figure(figsize=(10,5))
plt.plot(df.index, df['Passengers'])
plt.axvspan(df.index[12], df.index[24], color='yellow', alpha=0.3)
plt.title('Highlight a Time Period')
plt.show()
# Highlight values above a certain threshold
threshold = 400
plt.figure(figsize=(10,5))
plt.plot(df.index, df['Passengers'])
plt.axhline(threshold, color='green', linestyle='--', lw=2, label='Threshold')
over_idx = df[df['Passengers'] > threshold].index
over_val = df['Passengers'][df['Passengers'] > threshold]
plt.scatter(over_idx, over_val, color='green')
plt.title('Highlight All Points Above 400 Passengers')
plt.legend()
plt.show()
Using Shapes to Explain Events#
You can use rectangles or vertical spans to show holidays, events, or actions in your plot. This helps viewers see related changes easily. Let us learn how.
# Add a rectangle using patches
import matplotlib.patches as patches
fig, ax = plt.subplots(figsize=(10,5))
ax.plot(df.index, df['Passengers'])
rect = patches.Rectangle((df.index[40], 100),
df.index[10]-df.index[0], 400,
linewidth=2, edgecolor='purple', facecolor='none')
ax.add_patch(rect)
plt.title('Rectangle Highlight for Special Period')
plt.show()
# Highlight weekends over a year (example of iteration and annotation)
import numpy as np
plt.figure(figsize=(10,5))
plt.plot(df.index, df['Passengers'])
for idx in df.index[:12]:
if idx.month in [6,7,8]:
plt.axvline(idx, color='pink', alpha=0.3)
plt.title('Highlight Summer Months')
plt.show()
# Annotating with images (advanced, optional)
from matplotlib.offsetbox import OffsetImage, AnnotationBbox
# Example uses a small matrix as an image (no file needed)
arr = np.zeros((10,10))
arr[2:8,2:8] = 1
fig, ax = plt.subplots(figsize=(10,5))
ax.plot(df.index, df['Passengers'])
imagebox = OffsetImage(arr, cmap='Blues', zoom=1)
ab = AnnotationBbox(imagebox, (df.index[30], df['Passengers'].iloc[30]), frameon=False)
ax.add_artist(ab)
plt.title('Add a Simple Icon Annotation')
plt.show()
# Add multiple annotations in a loop (quick summary markers)
highlight_idx = [5, 15, 25]
values = df['Passengers'].iloc[highlight_idx]
plt.figure(figsize=(10,5))
plt.plot(df.index, df['Passengers'])
for i, idx in enumerate(highlight_idx):
plt.annotate(f'Point {i+1}', (df.index[idx], values.iloc[i]),
xytext=(df.index[idx], values.iloc[i]+20),
ha='center', arrowprops=dict(arrowstyle='->'))
plt.title('Multiple Highlighted Events')
plt.show()
# Interactive annotation: ask user for a label and point
point = int(input('Which data point (0-143) would you like to annotate? '))
label = input('What label should I use? ')
plt.figure(figsize=(10,5))
plt.plot(df.index, df['Passengers'])
plt.annotate(label, (df.index[point], df['Passengers'].iloc[point]),
xytext=(df.index[point], df['Passengers'].iloc[point] + 30),
arrowprops=dict(arrowstyle='->', color='blue'))
plt.title('Your Annotation')
plt.show()
Recap and Practice#
You have learned
- How to add notes (annotations) to charts
- How to use arrows, boxes, colored spans, and even images
- How to highlight events in data
Practice by choosing your own highlight on a chart today!
# Challenge: highlight largest year-over-year jump
diff = df['Passengers'].diff(12)
biggest = diff.idxmax()
plt.figure(figsize=(10,5))
plt.plot(df.index, df['Passengers'])
plt.annotate('Largest jump', (biggest, df.loc[biggest, 'Passengers']),
xytext=(biggest, df.loc[biggest, 'Passengers']+40),
arrowprops=dict(facecolor='orange', arrowstyle='->'))
plt.title('Challenge: Largest Yearly Increase')
plt.show()
# Troubleshooting: Overlapping labels? Use adjust_text for smart layout
# !pip install adjustText # Uncomment to install if needed
from adjustText import adjust_text
plt.figure(figsize=(10,5))
plt.plot(df.index, df['Passengers'])
texts = []
for i in [10, 20, 30, 40, 50]:
texts.append(plt.text(df.index[i], df['Passengers'].iloc[i]+20, f'P{i}', color='crimson'))
adjust_text(texts, arrowprops=dict(arrowstyle='->'))
plt.title('No Overlap: Smart Label Placement')
plt.show()
# Extra tip: Remove an annotation (refresh the plot)
plt.figure(figsize=(10,5))
plt.plot(df.index, df['Passengers'])
plt.title('No Annotation - Clean Slate')
plt.show()
You finished! Next steps...#
Try building your own story or project with annotations. Share your creations or questions in the comments.
Like this lesson? Subscribe and check out more videos on our channel!
Found this useful?
All lessons, notebooks and datasets here are free. If they helped you, a coffee keeps new lessons coming.



