Network Adjacency Matrix Heatmap — ggplot2

A matrix-based representation of a network or graph where rows and columns represent nodes and cell color indicates the presence or weight of edges between them. This visualization complements node-link diagrams by excelling at revealing clusters, structural patterns, and density in large or dense networks where node-link layouts become cluttered. Reordering nodes by cluster, degree, or community membership exposes block-diagonal structure and makes group boundaries immediately visible.

Network Adjacency Matrix Heatmap rendered with ggplot2

Renders

R source (ggplot2)

#' anyplot.ai
#' heatmap-adjacency: Network Adjacency Matrix Heatmap
#' Library: ggplot2 3.5.1 | R 4.4.1
#' Quality: 92/100 | Created: 2026-09-05

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

set.seed(42)

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

# --- Data: coworker collaboration network ------------------------------------
# 30 employees across 3 departments; node labels carry the department prefix
# so group boundaries are visible directly in the axis ticks (per spec note
# on large networks). Within-department pairs collaborate more often and more
# intensely than cross-department pairs, producing block-diagonal structure.
departments <- c("Engineering", "Marketing", "Sales")
dept_short  <- c(Engineering = "ENG", Marketing = "MKT", Sales = "SLS")
n_per_dept  <- 10

nodes <- tibble::tibble(department = rep(departments, each = n_per_dept)) %>%
  group_by(department) %>%
  mutate(idx = row_number()) %>%
  ungroup() %>%
  mutate(node = sprintf("%s-%02d", dept_short[department], idx))

n_nodes <- nrow(nodes)

weight_mat <- matrix(NA_real_, n_nodes, n_nodes)
for (i in seq_len(n_nodes)) {
  for (j in seq_len(n_nodes)) {
    if (j <= i) next
    same_dept <- nodes$department[i] == nodes$department[j]
    edge_prob <- if (same_dept) 0.75 else 0.12
    if (runif(1) < edge_prob) {
      weight <- if (same_dept) runif(1, 4, 10) else runif(1, 1, 4)
      weight_mat[i, j] <- weight
      weight_mat[j, i] <- weight # undirected graph: fill both triangles
    }
  }
}
rownames(weight_mat) <- nodes$node
colnames(weight_mat) <- nodes$node

adjacency_df <- as.data.frame(weight_mat) %>%
  tibble::rownames_to_column("source") %>%
  pivot_longer(-source, names_to = "target", values_to = "weight") %>%
  mutate(
    source = factor(source, levels = nodes$node),
    target = factor(target, levels = nodes$node)
  )

# --- Department-boundary markers ---------------------------------------
# Positions along each discrete axis where one department's block ends and
# the next begins, drawn as subtle divider lines to reinforce the
# block-diagonal structure beyond what tick labels alone convey.
boundaries <- head(cumsum(table(nodes$department)[departments]), -1) + 0.5

# --- Plot ---------------------------------------------------------------
p <- ggplot(adjacency_df, aes(x = target, y = source, fill = weight)) +
  geom_tile(color = PAGE_BG, linewidth = 0.15) +
  geom_vline(xintercept = boundaries, color = INK_SOFT, linewidth = 0.3, alpha = 0.5) +
  geom_hline(yintercept = boundaries, color = INK_SOFT, linewidth = 0.3, alpha = 0.5) +
  scale_fill_gradient(
    low      = "#009E73",
    high     = "#4467A3",
    na.value = ELEVATED_BG, # absent edges (incl. diagonal): near-bg, distinct from data
    name     = "Meetings\nper month",
    guide    = guide_colorbar(barheight = unit(9, "cm"), barwidth = unit(0.4, "cm"))
  ) +
  scale_x_discrete(expand = c(0, 0)) +
  scale_y_discrete(expand = c(0, 0), limits = rev(nodes$node)) +
  coord_fixed(ratio = 1) +
  labs(
    title    = "heatmap-adjacency · r · ggplot2 · anyplot.ai",
    subtitle = "Color intensity = meetings per month; near-background tiles indicate no regular contact",
    x = "Employee", y = "Employee"
  ) +
  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_text(color = INK, size = 10),
    axis.text.x        = element_text(color = INK_SOFT, size = 8, angle = 90, hjust = 1, vjust = 0.5),
    axis.text.y        = element_text(color = INK_SOFT, size = 8),
    plot.title         = element_text(color = INK, size = 12),
    plot.subtitle      = element_text(color = INK_SOFT, size = 8),
    legend.text        = element_text(color = INK_SOFT, size = 8),
    legend.title       = element_text(color = INK, size = 10),
    legend.background  = element_rect(fill = ELEVATED_BG, color = NA),
    plot.margin        = margin(10, 10, 10, 10)
  )

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

Retrieve this implementation

Runnable source as JSON, for any HTTP client: https://api.anyplot.ai/specs/heatmap-adjacency/ggplot2/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-adjacency",
  "language": "r",
  "library": "ggplot2",
  "page": "https://anyplot.ai/heatmap-adjacency/r/ggplot2",
  "hub": "https://anyplot.ai/heatmap-adjacency",
  "code_json": "https://api.anyplot.ai/specs/heatmap-adjacency/ggplot2/code",
  "spec_json": "https://api.anyplot.ai/specs/heatmap-adjacency",
  "render_light_png": "https://storage.googleapis.com/anyplot-images/plots/heatmap-adjacency/r/ggplot2/plot-light.png",
  "render_dark_png": "https://storage.googleapis.com/anyplot-images/plots/heatmap-adjacency/r/ggplot2/plot-dark.png",
  "quality_score": 92.0,
  "license": "MIT",
  "guide": "https://anyplot.ai/llms.txt"
}

Part of Network Adjacency Matrix Heatmap on anyplot.ai.

Other implementations