Basic Heatmap — plotnine

A heatmap displaying values in a matrix format using color intensity. Each cell's color represents the magnitude of the value, making it easy to identify patterns, clusters, and outliers in two-dimensional data. Essential for visualizing correlations, frequencies, and relationships between variables.

Basic Heatmap rendered with plotnine

Python source (plotnine)

""" anyplot.ai
heatmap-basic: Basic Heatmap
Library: plotnine 0.15.4 | Python 3.13.13
Quality: 85/100 | Updated: 2026-05-28
"""

import os

import numpy as np
import pandas as pd
from plotnine import (
    aes,
    element_blank,
    element_rect,
    element_text,
    geom_text,
    geom_tile,
    ggplot,
    labs,
    scale_color_identity,
    scale_fill_gradient2,
    scale_x_discrete,
    scale_y_discrete,
    theme,
    theme_minimal,
)


# Theme tokens
THEME = os.getenv("ANYPLOT_THEME", "light")
PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420"
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"

# Imprint diverging colormap: matte-red → theme-adaptive midpoint → blue
CMAP_MID = "#FAF8F1" if THEME == "light" else "#1A1A17"

# Data — quarterly growth rates (%) across 8 departments over 8 quarters
np.random.seed(42)
departments = ["Engineering", "Marketing", "Sales", "Finance", "Operations", "HR", "Research", "Support"]
quarters = ["Q1 '23", "Q2 '23", "Q3 '23", "Q4 '23", "Q1 '24", "Q2 '24", "Q3 '24", "Q4 '24"]

base_trend = np.linspace(-18, 22, 8)
dept_offsets = np.array([-6, 10, 14, -3, 4, -10, 8, -5])
values = np.round(base_trend[np.newaxis, :] + dept_offsets[:, np.newaxis] + np.random.normal(0, 4, (8, 8)), 1)

values[5, 0] = -32.5  # HR deep crisis Q1 '23
values[2, 7] = 38.2  # Sales strong recovery Q4 '24
values[6, 6] = 33.7  # Research surge Q3 '24

dept_idx, qtr_idx = np.meshgrid(np.arange(8), np.arange(8), indexing="ij")
df = pd.DataFrame(
    {
        "Department": pd.Categorical(
            [departments[i] for i in dept_idx.ravel()], categories=departments[::-1], ordered=True
        ),
        "Quarter": pd.Categorical([quarters[j] for j in qtr_idx.ravel()], categories=quarters, ordered=True),
        "Growth (%)": values.ravel(),
    }
)

# Theme-adaptive text color: white on dark extremes, INK on mid-range
if THEME == "light":
    df["text_color"] = np.where(df["Growth (%)"] < -20, "white", np.where(df["Growth (%)"] > 28, "white", INK))
else:
    df["text_color"] = INK  # light cream on all dark cells in dark theme

df["label"] = [f"{v:+.1f}" for v in df["Growth (%)"]]

# Standard title format — no descriptive prefix needed on square canvas (2400px wide)
title = "heatmap-basic · python · plotnine · anyplot.ai"
n = len(title)
title_fs = max(8, round(12 * 67 / n)) if n > 67 else 12

# Plot
plot = (
    ggplot(df, aes(x="Quarter", y="Department"))
    + geom_tile(aes(fill="Growth (%)"), color=INK_SOFT, size=0.5)
    + geom_text(aes(label="label", color="text_color"), size=3, fontweight="bold", show_legend=False)
    + scale_fill_gradient2(
        low="#AE3030",
        mid=CMAP_MID,
        high="#4467A3",
        midpoint=0,
        name="Growth (%)",
        limits=(-35, 40),
        breaks=[-30, -15, 0, 15, 30],
    )
    + scale_color_identity()
    + scale_x_discrete(expand=(0, 0.5))
    + scale_y_discrete(expand=(0, 0.5))
    + labs(x="Quarter", y="Department", title=title, subtitle="Year-over-year growth rate (%) · Q1 2023 – Q4 2024")
    + theme_minimal()
    + theme(
        figure_size=(6, 6),
        text=element_text(family="sans-serif", color=INK),
        plot_title=element_text(size=title_fs, ha="center", weight="bold", color=INK),
        plot_subtitle=element_text(size=8, ha="center", color=INK_SOFT),
        axis_title_x=element_text(size=10, color=INK, margin={"t": 6}),
        axis_title_y=element_text(size=10, color=INK, margin={"r": 6}),
        axis_text_x=element_text(size=8, color=INK_SOFT, rotation=45, ha="right"),
        axis_text_y=element_text(size=8, color=INK_SOFT, ha="right"),
        legend_title=element_text(size=8, weight="bold", color=INK),
        legend_text=element_text(size=7, color=INK_SOFT),
        legend_position="right",
        legend_key_height=12,
        legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),
        panel_grid_major=element_blank(),
        panel_grid_minor=element_blank(),
        panel_background=element_rect(fill=PAGE_BG),
        panel_border=element_rect(color=INK_SOFT, fill=None),
        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),
        plot_margin=0.02,
    )
)

plot.save(f"plot-{THEME}.png", dpi=400, width=6, height=6, units="in", verbose=False)

Part of Basic Heatmap on anyplot.ai.

Other implementations