Correlation Matrix Heatmap — ggplot2

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 ggplot2

Renders

R source (ggplot2)

#' anyplot.ai
#' heatmap-correlation: Correlation Matrix Heatmap
#' Library: ggplot2 3.5.1 | R 4.4.1
#' Quality: 91/100 | Created: 2026-08-20

library(ggplot2)
library(dplyr)
library(tidyr)
library(ragg)

set.seed(42)

# --- Theme tokens (see prompts/default-style-guide.md "Theme-adaptive Chrome")
THEME        <- Sys.getenv("ANYPLOT_THEME", "light")
PAGE_BG      <- if (THEME == "light") "#FAF8F1" else "#1A1A17"
INK          <- if (THEME == "light") "#1A1A17" else "#F0EFE8"
INK_SOFT     <- if (THEME == "light") "#4A4A44" else "#B8B7B0"
MIDPOINT     <- PAGE_BG
# Fill colors (imprint_div extremes) are theme-independent, so the label
# color that reads on top of them must be fixed rather than theme-adaptive.
TEXT_ON_FILL <- "#F0EFE8"
# Hairline tile border: ink at 15% alpha (matches the style guide's rule-divider
# token), so it stays visible even for near-zero cells whose fill equals PAGE_BG.
HAIRLINE     <- if (THEME == "light") "#1A1A1726" else "#F0EFE826"
# Diagonal (self-correlation = 1.00) carries no analytical information; give it
# a fixed, desaturated fill so it recedes behind the informative off-diagonal cells.
DIAG_FILL    <- "#5C5B54"

# --- Data --------------------------------------------------------------
# Feature correlations among car design/performance attributes (mtcars) -
# a classic multicollinearity-detection scenario before regression modeling.
vars <- c("mpg", "disp", "hp", "drat", "wt", "qsec", "gear", "carb")
display_labels <- c(
  mpg  = "MPG", disp = "Displacement", hp   = "Horsepower", drat = "Axle Ratio",
  wt   = "Weight", qsec = "1/4 Mile Time", gear = "Gears", carb = "Carburetors"
)

cor_matrix <- cor(mtcars[, vars])
cor_matrix[upper.tri(cor_matrix)] <- NA  # mask redundant upper triangle

df <- as.data.frame(cor_matrix) %>%
  mutate(row_var = factor(vars, levels = rev(vars))) %>%
  pivot_longer(cols = -row_var, names_to = "col_var", values_to = "correlation") %>%
  mutate(col_var = factor(col_var, levels = vars)) %>%
  filter(!is.na(correlation)) %>%
  mutate(is_diag = as.character(row_var) == as.character(col_var))

# --- Plot ----------------------------------------------------------------
title_text <- "heatmap-correlation · r · ggplot2 · anyplot.ai"

p <- ggplot(df, aes(x = col_var, y = row_var)) +
  geom_tile(
    data = filter(df, !is_diag), aes(fill = correlation),
    color = HAIRLINE, linewidth = 0.6
  ) +
  geom_tile(
    data = filter(df, is_diag), fill = DIAG_FILL,
    color = HAIRLINE, linewidth = 0.6
  ) +
  geom_text(
    aes(label = sprintf("%.2f", correlation), color = abs(correlation) > 0.55),
    size = 3.3, show.legend = FALSE
  ) +
  scale_color_manual(values = c(`TRUE` = TEXT_ON_FILL, `FALSE` = INK), guide = "none") +
  scale_fill_gradient2(
    low = "#AE3030", mid = MIDPOINT, high = "#4467A3",
    midpoint = 0, limits = c(-1, 1), breaks = c(-1, -0.5, 0, 0.5, 1),
    name = "Correlation"
  ) +
  scale_x_discrete(labels = display_labels, expand = c(0, 0)) +
  scale_y_discrete(labels = display_labels, expand = c(0, 0)) +
  coord_fixed() +
  labs(title = title_text, x = NULL, y = NULL) +
  theme_minimal(base_size = 8) +
  theme(
    plot.background    = element_rect(fill = PAGE_BG, color = PAGE_BG),
    panel.background   = element_rect(fill = PAGE_BG, color = NA),
    panel.grid         = element_blank(),
    axis.title         = element_blank(),
    axis.text.x        = element_text(color = INK_SOFT, size = 9, angle = 35, hjust = 1),
    axis.text.y        = element_text(color = INK_SOFT, size = 9),
    axis.ticks         = element_blank(),
    plot.title         = element_text(color = INK, size = 12, margin = margin(b = 14)),
    legend.background  = element_rect(fill = PAGE_BG, color = NA),
    legend.text        = element_text(color = INK_SOFT, size = 8),
    legend.title       = element_text(color = INK, size = 10),
    legend.key.height  = unit(1.6, "cm"),
    plot.margin        = margin(t = 20, r = 20, b = 20, l = 20)
  )

# --- Save ------------------------------------------------------------------
ggsave(
  filename = sprintf("plot-%s.png", THEME),
  plot     = p,
  device   = ragg::agg_png,
  width    = 6,
  height   = 6,
  units    = "in",
  dpi      = 400
)

Part of Correlation Matrix Heatmap on anyplot.ai.

Other implementations