Skip to content

Calendar heatmaps


dayplot.calendar(dates, values, start_date=None, end_date=None, color_for_none=None, edgecolor='black', edgewidth=0.0, cmap='Greens', week_starts_on='Sunday', month_kws=None, day_kws=None, day_x_margin=0.02, month_y_margin=0.4, vmin=None, vmax=None, vcenter=None, boxstyle='square', ax=None, **kwargs)

Create a calendar heatmap (GitHub-style) from input dates and values, supporting both positive and negative values via a suitable colormap scale.

This function generates a calendar heatmap similar to GitHub's contribution graph, where each cell represents a day colored according to the corresponding value. The chart is organized by weeks (columns) and days of the week (rows), starting from a specified start date to an end date.

When vmin, vmax, and vcenter are not specified, they default to the data's minimum, maximum, and zero (if data spans negative and positive values), respectively. Providing any of vmin, vmax, or vcenter manually will override the automatic calculation for that parameter.

Parameters
  • dates: A list of date-like objects (e.g., datetime.date, datetime.datetime, or strings in "YYYY-MM-DD" format). Must have the same length as values.

  • values: A list of numeric values corresponding to each date in dates. These values represent contributions or counts for each day and must have the same length as dates.

  • start_date: The earliest date to display on the chart. Can be a date, datetime, or a string in "YYYY-MM-DD" format. If not provided, the minimum date found in dates will be used.

  • end_date: The latest date to display on the chart. Can be a date, datetime, or a string in "YYYY-MM-DD" format. If not provided, the maximum date found in dates will be used.

  • color_for_none: Color to use for days with no contributions (i.e., count zero). Defaults to "#e8e8e8", a light gray color. This parameter is ignored when values has negative values.

  • edgecolor: Color of the edges for each day's rectangle. Defaults to "black".

  • edgewidth: Line width for the edges of each day's rectangle. Defaults to 0.5.

  • cmap: A valid Matplotlib colormap name or a LinearSegmentedColormap instance. Defaults to "Greens". The colormap is used to determine the fill color intensity of each day's cell based on its value.

  • week_starts_on: The starting day of the week, which can be specified as a string ("Sunday", "Monday", ..., "Saturday"). Defaults to "Sunday".

  • month_kws: Additional keyword arguments passed to the matplotlib.axes.Axes.text function when labeling month names (outside of x, y and s).

  • day_kws: Additional keyword arguments passed to the matplotlib.axes.Axes.text function when labeling weekday names on the y-axis (outside of x, y and s).

  • day_x_margin: Distance between the day labels (Monday, Tuesday, etc.) and the graph. The greater the distance, the further to the left the text will be.

  • month_y_margin: Distance between the month labels (January, February, etc.) and the graph. The greater the distance, the more text will appear at the top.

  • vmin: The lower bound for the color scale. If None, it is determined automatically from the data. If data contains both positive and negative values and vcenter is not provided, vmin will default to the data's minimum. Providing vmin overrides the automatic calculation.

  • vmax: The upper bound for the color scale. If None, it is determined automatically from the data. If data contains both positive and negative values and vcenter is not provided, vmax will default to the data's maximum. Providing vmax overrides the automatic calculation.

  • vcenter: The midpoint for the color scale, typically used with diverging colormaps (e.g., "RdBu") to position a central reference (e.g., zero). If None and the data spans negative and positive values, vcenter will default to 0. Providing vcenter overrides this automatic setting.

  • boxstyle: The style of each box. This will be passed to matplotlib.patches.FancyBboxPatch. Available values are: "square", "circle", "ellipse", "larrow"

  • ax: A matplotlib axes. If None, plt.gca() will be used. It is advisable to make this explicit to avoid unexpected behaviour, particularly when manipulating a figure with several axes.

  • kwargs: Any additional arguments that will be passed to matplotlib.patches.FancyBboxPatch. For example, you can set alpha, hatch, linestyle, etc. You can find them all here.

Returns

A list of matplotlib.patches.FancyBboxPatch (one for each cell).

Notes
  • The function aggregates multiple entries for the same date by summing their values.


Examples

Basic usage

# mkdocs: render
import matplotlib.pyplot as plt
import dayplot as dp

df = dp.load_dataset()

fig, ax = plt.subplots(figsize=(15, 5))
dp.calendar(
   df["dates"],
   df["values"],
   start_date="2024-01-01",
   end_date="2024-12-31"
)

Change colormap

# mkdocs: render
import matplotlib.pyplot as plt
import dayplot as dp

df = dp.load_dataset()

fig, ax = plt.subplots(figsize=(15, 5))
dp.calendar(
   df["dates"],
   df["values"],
   cmap="Reds",
   start_date="2024-01-01",
   end_date="2024-12-31"
)

Change other colors

# mkdocs: render
import matplotlib.pyplot as plt
import dayplot as dp

df = dp.load_dataset()

fig, ax = plt.subplots(figsize=(15, 5))
dp.calendar(
   df["dates"],
   df["values"],
   start_date="2024-01-01",
   end_date="2024-12-31",
   color_for_none="pink",
   edgecolor="white",
   edgewidth=0.4,
   day_kws={"color": "skyblue"},
   month_kws={"color": "red"},
   ax=ax,
)
fig.set_facecolor("black")
ax.set_facecolor("black")

Boxstyle

# mkdocs: render
import matplotlib.pyplot as plt
import dayplot as dp

df = dp.load_dataset()

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

Fill the gap

# mkdocs: render
import matplotlib.pyplot as plt
import dayplot as dp

df = dp.load_dataset()

fig, ax = plt.subplots(figsize=(15, 5))
dp.calendar(
   dates=df["dates"],
   values=df["values"],
   start_date="2024-01-01",
   end_date="2024-12-31",
   mutation_scale=1.22, # 22% bigger boxes
   ax=ax,
)

Label style

# mkdocs: render
import matplotlib.pyplot as plt
import dayplot as dp

df = dp.load_dataset()

fig, ax = plt.subplots(figsize=(15, 5))
dp.calendar(
   dates=df["dates"],
   values=df["values"],
   start_date="2024-01-01",
   end_date="2024-12-31",
   day_kws={"weight": "bold", "size": 12},
   month_kws={"size": 20, "color": "red"},
   day_x_margin=0.05,  # default = 0.02
   month_y_margin=0.8,  # default = 0.4
   ax=ax,
)

Combine calendars

# mkdocs: render
import matplotlib.pyplot as plt
import dayplot as dp

df = dp.load_dataset()

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

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

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

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

Advanced

See advanced usage here.