How to create a heatmap in Matplotlib

Heatmaps make rectangular numeric data readable by turning each cell into a color-coded value. In Matplotlib, they fit matrices, queue counts, sensor grids, and other two-dimensional measurements where both row and column labels carry meaning.

An Axes.imshow() call draws the matrix as an image, while tick labels identify the columns and rows. Keeping the returned image object lets fig.colorbar() use the same colormap and numeric range as the cells.

Per-cell text labels make small grids easier to inspect, but the label color needs enough contrast against the colormap. A saved PNG with visible row labels, column labels, annotations, and colorbar ticks confirms the heatmap is ready to use outside the Python session.

Steps to create a Matplotlib heatmap:

  1. Save the heatmap script as create_heatmap.py.
    create_heatmap.py
    from pathlib import Path
     
    import matplotlib
    import matplotlib.pyplot as plt
    import numpy as np
     
     
    queues = np.array(
        [
            [42, 35, 29, 21, 18],
            [55, 48, 40, 32, 24],
            [38, 44, 51, 46, 39],
            [26, 31, 37, 43, 49],
        ]
    )
     
    teams = ["Platform", "Security", "Billing", "Support"]
    days = ["Mon", "Tue", "Wed", "Thu", "Fri"]
    output = Path("heatmap-create.png")
     
    fig, ax = plt.subplots(figsize=(6.6, 4.2), layout="constrained")
    image = ax.imshow(queues, cmap="magma", vmin=15, vmax=60)
     
    ax.set_title("Open support tickets by team")
    ax.set_xlabel("Day")
    ax.set_ylabel("Team")
    ax.set_xticks(range(len(days)), days)
    ax.set_yticks(range(len(teams)), teams)
     
    colorbar = fig.colorbar(image, ax=ax, label="Open tickets")
     
    for row in range(queues.shape[0]):
        for column in range(queues.shape[1]):
            value = queues[row, column]
            text_color = "white" if image.norm(value) < 0.45 else "black"
            ax.text(column, row, value, ha="center", va="center", color=text_color)
     
    fig.savefig(output, dpi=160)
    plt.close(fig)
     
    matplotlib_version = matplotlib.__version__.split("+", 1)[0]
    print(f"matplotlib {matplotlib_version}")
    print(f"matrix shape: {queues.shape[0]} rows x {queues.shape[1]} columns")
    print(f"colorbar label: {colorbar.ax.get_ylabel()}")
    print(f"saved: {output}")
    print(f"bytes: {output.stat().st_size}")

    image is the AxesImage returned by imshow(). Passing it to fig.colorbar() keeps the colorbar tied to the heatmap colormap and value range.

  2. Run the script from the Python environment that has Matplotlib installed.
    $ python create_heatmap.py
    matplotlib 3.10.7
    matrix shape: 4 rows x 5 columns
    colorbar label: Open tickets
    saved: heatmap-create.png
    bytes: 55940

    The byte count can change with Matplotlib, fonts, backend, or DPI differences. A nonzero byte count confirms that the PNG file was written.

  3. Open heatmap-create.png and confirm the grid, labels, annotations, and colorbar.

    The image should show four team rows, five weekday columns, numeric values inside each cell, and a vertical colorbar labeled Open tickets.

  4. Replace the queue matrix and label lists with the real rectangular dataset.
    queues = np.array(
        [
            [12, 18, 21],
            [16, 24, 30],
        ]
    )
     
    teams = ["North", "South"]
    days = ["Mon", "Tue", "Wed"]

    The number of row labels must match queues.shape[0], and the number of column labels must match queues.shape[1].

  5. Remove the practice files when the heatmap was only a test.
    $ rm create_heatmap.py heatmap-create.png