{"spec_id":"subplot-grid","library":"ggplot2","language":"r","code":"#' anyplot.ai\n#' subplot-grid: Subplot Grid Layout\n#' Library: ggplot2 3.5.1 | R 4.4.1\n#' Quality: 88/100 | Created: 2026-09-09\n\nlibrary(ggplot2)\nlibrary(dplyr)\nlibrary(scales)\nlibrary(ragg)\nlibrary(gridExtra)\n\ngrDevices::pdf(NULL)  # null device so building text grobs pre-render doesn't leave a stray Rplots.pdf\nset.seed(42)\n\n# --- Theme tokens ------------------------------------------------------\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\"\n\nIMPRINT_PALETTE <- c(\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\",\n                     \"#AE3030\", \"#2ABCCD\", \"#954477\", \"#99B314\")\n\n# --- Data: a 12-week weather-station log combining four complementary ------\n# views of the same station (daily temperature, weekly rainfall totals,\n# humidity distribution, temperature-humidity relationship) — the point of a\n# subplot grid is mixing distinct plot types, not repeating one geom.\nn_days <- 84\ndates  <- seq(as.Date(\"2024-06-01\"), by = \"day\", length.out = n_days)\nday_idx <- seq_len(n_days)\n\ntemp_c <- 17 + 7 * sin(pi * day_idx / n_days) + rnorm(n_days, mean = 0, sd = 1.3)\nhumidity_pct <- 78 - 0.55 * (temp_c - min(temp_c)) + rnorm(n_days, mean = 0, sd = 5)\nhumidity_pct <- pmin(pmax(humidity_pct, 25), 95)\nprecip_mm <- rgamma(n_days, shape = 0.35, scale = 6) * rbinom(n_days, 1, 0.4)\n\nstation <- tibble::tibble(\n  date         = dates,\n  week         = ((day_idx - 1) %/% 7) + 1,\n  temp_c       = temp_c,\n  humidity_pct = humidity_pct,\n  precip_mm    = precip_mm\n)\n\nweekly_precip <- station %>%\n  group_by(week) %>%\n  summarise(total_precip_mm = sum(precip_mm), week_start = min(date), .groups = \"drop\")\n\n# Shared x-axis range (date) for the top row: temperature and rainfall come\n# from the same 12-week station log, so aligning them on one time axis lets\n# the two panels be compared directly -- the spec's \"shared axes\" mode,\n# alongside the independent y-scales used by every other panel. Padded by\n# half the rainfall bar width so the first/last bars aren't clipped by the\n# shared limits.\ndate_range <- range(station$date) + c(-2.5, 2.5)\n\n# --- Title ---------------------------------------------------------------\ntitle_text <- \"subplot-grid · r · ggplot2 · anyplot.ai\"\ntitle_fontsize <- if (nchar(title_text) > 67) round(12 * 67 / nchar(title_text)) else 12\ntitle_fontsize <- max(title_fontsize, 8)\nFONT_FAMILY <- \"sans\"\n\n# --- Shared chrome ---------------------------------------------------------\nanyplot_theme <- theme_minimal(base_size = 7, base_family = FONT_FAMILY) +\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.minor    = element_blank(),\n    panel.grid.major.x  = element_blank(),\n    panel.grid.major.y  = element_line(color = alpha(INK, 0.12), linewidth = 0.4),\n    axis.line           = element_line(color = INK_SOFT, linewidth = 0.35),\n    axis.ticks          = element_blank(),\n    axis.title          = element_text(color = INK, size = 9),\n    axis.text           = element_text(color = INK_SOFT, size = 7),\n    plot.title          = element_text(color = INK, size = 10, face = \"plain\"),\n    plot.margin         = margin(t = 14, r = 16, b = 10, l = 12)\n  )\n\n# --- Panel 1: daily temperature (line) -- shared date axis with panel 2 ----\np_temp <- ggplot(station, aes(x = date, y = temp_c)) +\n  geom_line(color = IMPRINT_PALETTE[1], linewidth = 1.0) +\n  scale_x_date(limits = date_range, date_labels = \"%b\") +\n  labs(title = \"Daily Temperature\", x = \"Date\", y = \"Temp (°C)\") +\n  anyplot_theme\n\n# --- Panel 2: weekly rainfall (bar) -- shared date axis with panel 1 ------\n# blue for the water association; x uses the same date range/scale as panel 1\n# so the two time series line up for direct visual comparison.\np_precip <- ggplot(weekly_precip, aes(x = week_start, y = total_precip_mm)) +\n  geom_col(fill = IMPRINT_PALETTE[3], width = 5) +\n  scale_x_date(limits = date_range, date_labels = \"%b\") +\n  labs(title = \"Weekly Rainfall\", x = \"Date\", y = \"Rain (mm)\") +\n  anyplot_theme\n\n# --- Panel 3: humidity distribution (histogram) -----------------------------\np_humidity <- ggplot(station, aes(x = humidity_pct)) +\n  geom_histogram(fill = IMPRINT_PALETTE[2], color = PAGE_BG, bins = 14) +\n  labs(title = \"Humidity Distribution\", x = \"Humidity (%)\", y = \"Days\") +\n  anyplot_theme\n\n# --- Panel 4: temperature vs humidity (scatter) -----------------------------\np_scatter <- ggplot(station, aes(x = temp_c, y = humidity_pct)) +\n  geom_point(color = IMPRINT_PALETTE[1], size = 2.5, alpha = 0.7) +\n  geom_smooth(method = \"lm\", formula = y ~ x, se = FALSE,\n              color = INK_SOFT, linewidth = 0.8, linetype = \"dashed\") +\n  labs(title = \"Temp vs Humidity\", x = \"Temp (°C)\", y = \"Humidity (%)\") +\n  anyplot_theme\n\n# --- Combine into a 2x2 grid with a shared anyplot title ---------------------\ncombined <- arrangeGrob(\n  p_temp, p_precip, p_humidity, p_scatter,\n  ncol = 2, nrow = 2,\n  top = grid::textGrob(\n    title_text,\n    gp = grid::gpar(col = INK, fontsize = title_fontsize, fontfamily = FONT_FAMILY)\n  ),\n  padding = grid::unit(10, \"pt\")\n)\n\n# --- Save --------------------------------------------------------------------\nggsave(\n  filename = sprintf(\"plot-%s.png\", THEME),\n  plot     = combined,\n  device   = ragg::agg_png,\n  width    = 6,\n  height   = 6,\n  units    = \"in\",\n  dpi      = 400,\n  bg       = PAGE_BG\n)\n"}