Calendar heatmaps in Python and Matplotlib

logo of a chart:Heatmap

This post will explain everything you need to know to create calendar heatmaps in Python using matplotlib and the dayplot package. It starts from simple examples to more advanced usages.

Libraries

The easiest way to create calendar heatmaps is to use the dayplot library, which is based on matplotlib.

import matplotlib.pyplot as plt
import dayplot as dp

Install with pip install dayplot requests pandas. Note that pandas and requests are only required to load the built-in dataset, used below.

If you don't need it, just use: pip install dayplot

Dataset

dayplot provides a built-in dataset with simple features, that we can access with:

df = dp.load_dataset()
df
dates values
0 2024-06-17 3
1 2024-08-22 4
2 2024-04-03 1
3 2024-09-20 5
4 2024-05-22 5
... ... ...
495 2025-07-27 6
496 2025-03-26 6
497 2025-05-12 5
498 2025-11-13 5
499 2025-12-14 2

500 rows × 2 columns

Quick start

The easiest way to create your first calendar heatmap is to call the calendar() function, with the following argument:

  • dates: a list of dates
  • values: value for each date
  • start_date: when should the calendar heatmap start?
  • end_date: when should the calendar heatmap end?
  • ax: the matplotlib Axes to use. Optionnal but recommended to avoid unexpected results.
fig, ax = plt.subplots(figsize=(15, 6), dpi=300)
dp.calendar(
    dates=df["dates"],
    values=df["values"],
    start_date="2024-01-01",
    end_date="2024-12-31",
    ax=ax,
)
plt.show()

Colors

Colormap

All colormap from matplotlib, or any colormap you might have created (using pypalettes or manually with a LinearSegmentedColormap object) can be used to change the colormap

fig, ax = plt.subplots(figsize=(15, 6), dpi=300)
dp.calendar(
    dates=df["dates"],
    values=df["values"],
    start_date="2024-01-01",
    end_date="2024-12-31",
    cmap="Reds",  # red-ish colormap
    ax=ax,
)
plt.show()

You can also control the range of the colormap with vmin, vmax and vcenter. Those can be important arguments as they control how the colormap behaves depending on the values.

fig, ax = plt.subplots(figsize=(15, 6), dpi=300)
dp.calendar(
    dates=df["dates"],
    values=df["values"],
    start_date="2024-01-01",
    end_date="2024-12-31",
    cmap="Reds",
    vmin=2,
    vcenter=3,
    vmax=10,
    ax=ax,
)
plt.show()

Color for missing values

By default, dates with no values are lightgrey, but we can change that with the color_for_none argument:

fig, ax = plt.subplots(figsize=(15, 6), dpi=300)
dp.calendar(
    dates=df["dates"],
    values=df["values"],
    start_date="2024-01-01",
    end_date="2024-12-31",
    color_for_none="skyblue",  # light blue color
    ax=ax,
)
plt.show()

Edgecolor

fig, ax = plt.subplots(figsize=(15, 6), dpi=300)
dp.calendar(
    dates=df["dates"],
    values=df["values"],
    start_date="2024-01-01",
    end_date="2024-12-31",
    edgecolor="black",  # black egdecolor
    edgewidth=1,  # required to make the edges visible
    mutation_scale=0.8,  # smaller boxes
    ax=ax,
)
plt.show()

Boxstyle

By default, dayplot draws square, but we can easily change it with the boxstyle argument:

fig, ax = plt.subplots(figsize=(15, 6), dpi=300)
dp.calendar(
    dates=df["dates"],
    values=df["values"],
    start_date="2024-01-01",
    end_date="2024-12-31",
    boxstyle="circle",  # circle
    ax=ax,
)
plt.show()

fig, ax = plt.subplots(figsize=(15, 6), dpi=300)
dp.calendar(
    dates=df["dates"],
    values=df["values"],
    start_date="2024-01-01",
    end_date="2024-12-31",
    boxstyle="round",  # round
    ax=ax,
)
plt.show()

fig, ax = plt.subplots(figsize=(15, 6), dpi=300)
dp.calendar(
    dates=df["dates"],
    values=df["values"],
    start_date="2024-01-01",
    end_date="2024-12-31",
    boxstyle="sawtooth",  # sawtooth
    ax=ax,
)
plt.show()

Multiple years

dayplot draws the calendar heatmap on a matplotlib Axes (or subplot, if you prefer).

This gives full control in the layout of the final chart. For example, in order to plot 2 calendar heatmaps on top of each other, we just have to create a Figure with 2 Axes:

fig, (ax1, ax2) = plt.subplots(nrows=2, figsize=(16, 4), dpi=300)

# 2025 heatmap
dp.calendar(
    dates=df["dates"],
    values=df["values"],
    start_date="2025-01-01",
    end_date="2025-12-31",
    cmap="Blues",
    ax=ax1,  # top axes
)

# 2024 heatmap
dp.calendar(
    dates=df["dates"],
    values=df["values"],
    start_date="2024-01-01",
    end_date="2024-12-31",
    cmap="Blues",
    ax=ax2,  # bottom axes
)

plt.show()

In order to make it obvious which year is which heatmap, let's add labels:

fig, (ax1, ax2) = plt.subplots(nrows=2, figsize=(16, 4), dpi=300)

# 2025 heatmap
dp.calendar(
    dates=df["dates"],
    values=df["values"],
    start_date="2025-01-01",
    end_date="2025-12-31",
    cmap="Blues",
    ax=ax1,  # top axes
)

# 2024 heatmap
dp.calendar(
    dates=df["dates"],
    values=df["values"],
    start_date="2024-01-01",
    end_date="2024-12-31",
    cmap="Blues",
    ax=ax2,  # bottom axes
)

# labels
text_args = dict(x=-4, y=3.5, size=30, rotation=90, color="#aaa", va="center")
ax1.text(s="2025", **text_args)
ax2.text(s="2024", **text_args)

plt.show()

🚨 Grab the Data To Viz poster!


Do you know all the chart types? Do you know which one you should pick? I made a decision tree that answers those questions. You can download it for free!

    dataviz decision tree poster