{"spec_id":"heatmap-correlation","library":"ggplot2","language":"r","code":"#' anyplot.ai\n#' heatmap-correlation: Correlation Matrix Heatmap\n#' Library: ggplot2 3.5.1 | R 4.4.1\n#' Quality: 91/100 | Created: 2026-08-20\n\nlibrary(ggplot2)\nlibrary(dplyr)\nlibrary(tidyr)\nlibrary(ragg)\n\nset.seed(42)\n\n# --- Theme tokens (see prompts/default-style-guide.md \"Theme-adaptive Chrome\")\nTHEME        <- Sys.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG      <- if (THEME == \"light\") \"#FAF8F1\" else \"#1A1A17\"\nINK          <- if (THEME == \"light\") \"#1A1A17\" else \"#F0EFE8\"\nINK_SOFT     <- if (THEME == \"light\") \"#4A4A44\" else \"#B8B7B0\"\nMIDPOINT     <- PAGE_BG\n# Fill colors (imprint_div extremes) are theme-independent, so the label\n# color that reads on top of them must be fixed rather than theme-adaptive.\nTEXT_ON_FILL <- \"#F0EFE8\"\n# Hairline tile border: ink at 15% alpha (matches the style guide's rule-divider\n# token), so it stays visible even for near-zero cells whose fill equals PAGE_BG.\nHAIRLINE     <- if (THEME == \"light\") \"#1A1A1726\" else \"#F0EFE826\"\n# Diagonal (self-correlation = 1.00) carries no analytical information; give it\n# a fixed, desaturated fill so it recedes behind the informative off-diagonal cells.\nDIAG_FILL    <- \"#5C5B54\"\n\n# --- Data --------------------------------------------------------------\n# Feature correlations among car design/performance attributes (mtcars) -\n# a classic multicollinearity-detection scenario before regression modeling.\nvars <- c(\"mpg\", \"disp\", \"hp\", \"drat\", \"wt\", \"qsec\", \"gear\", \"carb\")\ndisplay_labels <- c(\n  mpg  = \"MPG\", disp = \"Displacement\", hp   = \"Horsepower\", drat = \"Axle Ratio\",\n  wt   = \"Weight\", qsec = \"1/4 Mile Time\", gear = \"Gears\", carb = \"Carburetors\"\n)\n\ncor_matrix <- cor(mtcars[, vars])\ncor_matrix[upper.tri(cor_matrix)] <- NA  # mask redundant upper triangle\n\ndf <- as.data.frame(cor_matrix) %>%\n  mutate(row_var = factor(vars, levels = rev(vars))) %>%\n  pivot_longer(cols = -row_var, names_to = \"col_var\", values_to = \"correlation\") %>%\n  mutate(col_var = factor(col_var, levels = vars)) %>%\n  filter(!is.na(correlation)) %>%\n  mutate(is_diag = as.character(row_var) == as.character(col_var))\n\n# --- Plot ----------------------------------------------------------------\ntitle_text <- \"heatmap-correlation · r · ggplot2 · anyplot.ai\"\n\np <- ggplot(df, aes(x = col_var, y = row_var)) +\n  geom_tile(\n    data = filter(df, !is_diag), aes(fill = correlation),\n    color = HAIRLINE, linewidth = 0.6\n  ) +\n  geom_tile(\n    data = filter(df, is_diag), fill = DIAG_FILL,\n    color = HAIRLINE, linewidth = 0.6\n  ) +\n  geom_text(\n    aes(label = sprintf(\"%.2f\", correlation), color = abs(correlation) > 0.55),\n    size = 3.3, show.legend = FALSE\n  ) +\n  scale_color_manual(values = c(`TRUE` = TEXT_ON_FILL, `FALSE` = INK), guide = \"none\") +\n  scale_fill_gradient2(\n    low = \"#AE3030\", mid = MIDPOINT, high = \"#4467A3\",\n    midpoint = 0, limits = c(-1, 1), breaks = c(-1, -0.5, 0, 0.5, 1),\n    name = \"Correlation\"\n  ) +\n  scale_x_discrete(labels = display_labels, expand = c(0, 0)) +\n  scale_y_discrete(labels = display_labels, expand = c(0, 0)) +\n  coord_fixed() +\n  labs(title = title_text, x = NULL, y = NULL) +\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_blank(),\n    axis.text.x        = element_text(color = INK_SOFT, size = 9, angle = 35, hjust = 1),\n    axis.text.y        = element_text(color = INK_SOFT, size = 9),\n    axis.ticks         = element_blank(),\n    plot.title         = element_text(color = INK, size = 12, margin = margin(b = 14)),\n    legend.background  = element_rect(fill = PAGE_BG, color = NA),\n    legend.text        = element_text(color = INK_SOFT, size = 8),\n    legend.title       = element_text(color = INK, size = 10),\n    legend.key.height  = unit(1.6, \"cm\"),\n    plot.margin        = margin(t = 20, r = 20, b = 20, l = 20)\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"}