{"spec_id":"heatmap-adjacency","library":"ggplot2","language":"r","code":"#' anyplot.ai\n#' heatmap-adjacency: Network Adjacency Matrix Heatmap\n#' Library: ggplot2 3.5.1 | R 4.4.1\n#' Quality: 92/100 | Created: 2026-09-05\n\nlibrary(ggplot2)\nlibrary(dplyr)\nlibrary(tidyr)\nlibrary(ragg)\n\nset.seed(42)\n\n# --- Theme tokens ------------------------------------------------------------\nTHEME       <- Sys.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG     <- if (THEME == \"light\") \"#FAF8F1\" else \"#1A1A17\"\nELEVATED_BG <- if (THEME == \"light\") \"#FFFDF6\" else \"#242420\"\nINK         <- if (THEME == \"light\") \"#1A1A17\" else \"#F0EFE8\"\nINK_SOFT    <- if (THEME == \"light\") \"#4A4A44\" else \"#B8B7B0\"\n\n# --- Data: coworker collaboration network ------------------------------------\n# 30 employees across 3 departments; node labels carry the department prefix\n# so group boundaries are visible directly in the axis ticks (per spec note\n# on large networks). Within-department pairs collaborate more often and more\n# intensely than cross-department pairs, producing block-diagonal structure.\ndepartments <- c(\"Engineering\", \"Marketing\", \"Sales\")\ndept_short  <- c(Engineering = \"ENG\", Marketing = \"MKT\", Sales = \"SLS\")\nn_per_dept  <- 10\n\nnodes <- tibble::tibble(department = rep(departments, each = n_per_dept)) %>%\n  group_by(department) %>%\n  mutate(idx = row_number()) %>%\n  ungroup() %>%\n  mutate(node = sprintf(\"%s-%02d\", dept_short[department], idx))\n\nn_nodes <- nrow(nodes)\n\nweight_mat <- matrix(NA_real_, n_nodes, n_nodes)\nfor (i in seq_len(n_nodes)) {\n  for (j in seq_len(n_nodes)) {\n    if (j <= i) next\n    same_dept <- nodes$department[i] == nodes$department[j]\n    edge_prob <- if (same_dept) 0.75 else 0.12\n    if (runif(1) < edge_prob) {\n      weight <- if (same_dept) runif(1, 4, 10) else runif(1, 1, 4)\n      weight_mat[i, j] <- weight\n      weight_mat[j, i] <- weight # undirected graph: fill both triangles\n    }\n  }\n}\nrownames(weight_mat) <- nodes$node\ncolnames(weight_mat) <- nodes$node\n\nadjacency_df <- as.data.frame(weight_mat) %>%\n  tibble::rownames_to_column(\"source\") %>%\n  pivot_longer(-source, names_to = \"target\", values_to = \"weight\") %>%\n  mutate(\n    source = factor(source, levels = nodes$node),\n    target = factor(target, levels = nodes$node)\n  )\n\n# --- Department-boundary markers ---------------------------------------\n# Positions along each discrete axis where one department's block ends and\n# the next begins, drawn as subtle divider lines to reinforce the\n# block-diagonal structure beyond what tick labels alone convey.\nboundaries <- head(cumsum(table(nodes$department)[departments]), -1) + 0.5\n\n# --- Plot ---------------------------------------------------------------\np <- ggplot(adjacency_df, aes(x = target, y = source, fill = weight)) +\n  geom_tile(color = PAGE_BG, linewidth = 0.15) +\n  geom_vline(xintercept = boundaries, color = INK_SOFT, linewidth = 0.3, alpha = 0.5) +\n  geom_hline(yintercept = boundaries, color = INK_SOFT, linewidth = 0.3, alpha = 0.5) +\n  scale_fill_gradient(\n    low      = \"#009E73\",\n    high     = \"#4467A3\",\n    na.value = ELEVATED_BG, # absent edges (incl. diagonal): near-bg, distinct from data\n    name     = \"Meetings\\nper month\",\n    guide    = guide_colorbar(barheight = unit(9, \"cm\"), barwidth = unit(0.4, \"cm\"))\n  ) +\n  scale_x_discrete(expand = c(0, 0)) +\n  scale_y_discrete(expand = c(0, 0), limits = rev(nodes$node)) +\n  coord_fixed(ratio = 1) +\n  labs(\n    title    = \"heatmap-adjacency · r · ggplot2 · anyplot.ai\",\n    subtitle = \"Color intensity = meetings per month; near-background tiles indicate no regular contact\",\n    x = \"Employee\", y = \"Employee\"\n  ) +\n  theme_minimal(base_size = 8) +\n  theme(\n    plot.background    = element_rect(fill = PAGE_BG, color = PAGE_BG),\n    panel.background   = element_rect(fill = PAGE_BG, color = NA),\n    panel.grid         = element_blank(),\n    axis.title         = element_text(color = INK, size = 10),\n    axis.text.x        = element_text(color = INK_SOFT, size = 8, angle = 90, hjust = 1, vjust = 0.5),\n    axis.text.y        = element_text(color = INK_SOFT, size = 8),\n    plot.title         = element_text(color = INK, size = 12),\n    plot.subtitle      = element_text(color = INK_SOFT, size = 8),\n    legend.text        = element_text(color = INK_SOFT, size = 8),\n    legend.title       = element_text(color = INK, size = 10),\n    legend.background  = element_rect(fill = ELEVATED_BG, color = NA),\n    plot.margin        = margin(10, 10, 10, 10)\n  )\n\n# --- Save ---------------------------------------------------------------\nggsave(\n  filename = sprintf(\"plot-%s.png\", THEME),\n  plot     = p,\n  device   = ragg::agg_png,\n  width    = 6,\n  height   = 6,\n  units    = \"in\",\n  dpi      = 400\n)\n"}