Correlation Matrix Heatmap — Matplotlib

A heatmap specifically designed to display correlation coefficients between variables, using a diverging color scheme centered at zero. The symmetric matrix visualization makes it easy to identify positive correlations, negative correlations, and independent variables at a glance. Essential for exploratory data analysis, feature engineering, and multicollinearity detection in statistical and machine learning workflows.

Correlation Matrix Heatmap rendered with Matplotlib

Renders

Python source (Matplotlib)

""" anyplot.ai
heatmap-correlation: Correlation Matrix Heatmap
Library: matplotlib 3.11.1 | Python 3.13.15
Quality: 90/100 | Updated: 2026-08-18
"""

import os

import matplotlib.pyplot as plt
import numpy as np
from matplotlib.colors import LinearSegmentedColormap
from matplotlib.patches import Rectangle


# Theme tokens (see prompts/default-style-guide.md "Background" + "Theme-adaptive Chrome")
THEME = os.getenv("ANYPLOT_THEME", "light")
PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"
INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F"
EMPHASIS_POS = "#009E73"  # Imprint palette position 1 — strong positive correlation
EMPHASIS_NEG = "#AE3030"  # Imprint palette position 5 — strong negative correlation

# Data - realistic weather-station correlation matrix
np.random.seed(42)

variables = [
    "Temperature",
    "Humidity",
    "Wind Speed",
    "Precipitation",
    "Air Pressure",
    "Solar Radiation",
    "UV Index",
    "Cloud Cover",
]
n_vars = len(variables)

correlation_matrix = np.eye(n_vars)
correlations = {
    (0, 1): -0.82,  # Temperature - Humidity (strong negative)
    (0, 2): 0.15,  # Temperature - Wind Speed (weak positive)
    (0, 3): -0.48,  # Temperature - Precipitation (negative)
    (0, 4): -0.71,  # Temperature - Air Pressure (strong negative)
    (0, 5): 0.88,  # Temperature - Solar Radiation (strong positive)
    (0, 6): 0.79,  # Temperature - UV Index (strong positive)
    (0, 7): -0.65,  # Temperature - Cloud Cover (negative)
    (1, 2): 0.35,  # Humidity - Wind Speed (weak positive)
    (1, 3): 0.72,  # Humidity - Precipitation (strong positive)
    (1, 4): 0.58,  # Humidity - Air Pressure (positive)
    (1, 5): -0.84,  # Humidity - Solar Radiation (strong negative)
    (1, 6): -0.76,  # Humidity - UV Index (strong negative)
    (1, 7): 0.81,  # Humidity - Cloud Cover (strong positive)
    (2, 3): 0.42,  # Wind Speed - Precipitation (positive)
    (2, 4): -0.31,  # Wind Speed - Air Pressure (weak negative)
    (2, 5): 0.09,  # Wind Speed - Solar Radiation (very weak)
    (2, 6): -0.12,  # Wind Speed - UV Index (very weak negative)
    (2, 7): 0.38,  # Wind Speed - Cloud Cover (weak positive)
    (3, 4): -0.52,  # Precipitation - Air Pressure (negative)
    (3, 5): -0.68,  # Precipitation - Solar Radiation (strong negative)
    (3, 6): -0.61,  # Precipitation - UV Index (strong negative)
    (3, 7): 0.74,  # Precipitation - Cloud Cover (strong positive)
    (4, 5): 0.45,  # Air Pressure - Solar Radiation (positive)
    (4, 6): 0.39,  # Air Pressure - UV Index (positive)
    (4, 7): -0.43,  # Air Pressure - Cloud Cover (negative)
    (5, 6): 0.85,  # Solar Radiation - UV Index (strong positive)
    (5, 7): -0.79,  # Solar Radiation - Cloud Cover (strong negative)
    (6, 7): -0.71,  # UV Index - Cloud Cover (strong negative)
}
for (i, j), corr in correlations.items():
    correlation_matrix[i, j] = corr
    correlation_matrix[j, i] = corr

# Mask the upper triangle so each pair is shown once
mask = np.triu(np.ones_like(correlation_matrix, dtype=bool), k=1)
masked_corr = np.where(mask, np.nan, correlation_matrix)

# Imprint diverging colormap — matte-red (negative) through the theme midpoint to blue (positive)
midpoint = PAGE_BG
imprint_div = LinearSegmentedColormap.from_list("imprint_div", ["#AE3030", midpoint, "#4467A3"])

# Plot — square canvas for this symmetric matrix (see default-style-guide.md "Dimensions")
fig, ax = plt.subplots(figsize=(6, 6), dpi=400, facecolor=PAGE_BG, layout="constrained")
ax.set_facecolor(PAGE_BG)

im = ax.imshow(masked_corr, cmap=imprint_div, vmin=-1, vmax=1, aspect="equal")

# Subtle cell-boundary grid
ax.set_xticks(np.arange(n_vars) - 0.5, minor=True)
ax.set_yticks(np.arange(n_vars) - 0.5, minor=True)
ax.grid(which="minor", color=INK_MUTED, linestyle="-", linewidth=0.6, alpha=0.25)
ax.tick_params(which="minor", bottom=False, left=False)

# Colorbar, fixed to the full correlation range
cbar = ax.figure.colorbar(im, ax=ax, shrink=0.74, pad=0.03)
cbar.ax.set_ylabel("Correlation Coefficient", fontsize=10, labelpad=10, color=INK)
cbar.ax.tick_params(labelsize=8, colors=INK_SOFT, labelcolor=INK_SOFT)
cbar.outline.set_visible(False)

# Ticks and axis labels
ax.set_xticks(np.arange(n_vars))
ax.set_yticks(np.arange(n_vars))
ax.set_xticklabels(variables, fontsize=9, rotation=45, ha="right", rotation_mode="anchor", color=INK_SOFT)
ax.set_yticklabels(variables, fontsize=9, color=INK_SOFT)
ax.tick_params(which="major", bottom=False, left=False)

ax.set_xlabel("Weather Variables", fontsize=10, labelpad=12, color=INK)
ax.set_ylabel("Weather Variables", fontsize=10, labelpad=12, color=INK)

# Emphasis borders on strong correlations — solid for positive, dashed for negative,
# so the sign reads even without checking the colorbar
for i in range(n_vars):
    for j in range(n_vars):
        if not mask[i, j] and i != j and abs(correlation_matrix[i, j]) > 0.75:
            positive = correlation_matrix[i, j] > 0
            rect = Rectangle(
                (j - 0.45, i - 0.45),
                0.9,
                0.9,
                linewidth=1.8,
                edgecolor=EMPHASIS_POS if positive else EMPHASIS_NEG,
                facecolor="none",
                linestyle="-" if positive else "--",
                alpha=0.85,
            )
            ax.add_patch(rect)

# Cell annotations — text color follows the actual cell luminance so it stays
# legible against both saturated correlation colors and the near-neutral midpoint
for i in range(n_vars):
    for j in range(n_vars):
        if not mask[i, j]:
            value = correlation_matrix[i, j]
            r, g, b, _ = imprint_div((value + 1) / 2)
            luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b
            text_color = "white" if luminance < 0.55 else INK_SOFT
            ax.text(j, i, f"{value:.2f}", ha="center", va="center", color=text_color, fontsize=9, fontweight="bold")

# Title on the full figure (not just the heatmap axes) so it stays centered over
# the colorbar too — the square canvas is narrower than the landscape default,
# so the mandated title needs a smaller fontsize than the 12pt landscape baseline
# to stay clear of the colorbar's top tick label.
fig.suptitle(
    "heatmap-correlation · python · matplotlib · anyplot.ai", fontsize=11, y=0.97, fontweight="medium", color=INK
)

for spine in ax.spines.values():
    spine.set_visible(False)

plt.savefig(f"plot-{THEME}.png", dpi=400, facecolor=PAGE_BG)

Retrieve this implementation

Runnable source as JSON, for any HTTP client: https://api.anyplot.ai/specs/heatmap-correlation/matplotlib/code. Any spec id and library id listed in llms-full.txt fit the same URL shape; every URL below is complete and callable.

{
  "spec_id": "heatmap-correlation",
  "language": "python",
  "library": "matplotlib",
  "page": "https://anyplot.ai/heatmap-correlation/python/matplotlib",
  "hub": "https://anyplot.ai/heatmap-correlation",
  "code_json": "https://api.anyplot.ai/specs/heatmap-correlation/matplotlib/code",
  "spec_json": "https://api.anyplot.ai/specs/heatmap-correlation",
  "render_light_png": "https://storage.googleapis.com/anyplot-images/plots/heatmap-correlation/python/matplotlib/plot-light.png",
  "render_dark_png": "https://storage.googleapis.com/anyplot-images/plots/heatmap-correlation/python/matplotlib/plot-dark.png",
  "quality_score": 90.0,
  "license": "MIT",
  "guide": "https://anyplot.ai/llms.txt"
}

Part of Correlation Matrix Heatmap on anyplot.ai.

Other implementations