{"spec_id":"line-growth-percentile","library":"ggplot2","language":"r","code":"#' anyplot.ai\n#' line-growth-percentile: Pediatric Growth Chart with Percentile Curves\n#' Library: ggplot2 3.5.1 | R 4.4.1\n#' Quality: 90/100 | Created: 2026-06-20\n\nlibrary(ggplot2)\nlibrary(dplyr)\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\"\nINK_MUTED   <- if (THEME == \"light\") \"#6B6A63\" else \"#A8A79F\"\n# Approximate 15%-opacity INK blended onto PAGE_BG (ggplot2 lacks alpha on grid lines)\nGRID        <- if (THEME == \"light\") \"#D8D6D0\" else \"#3A3936\"\n\n# Imprint palette\nIMPRINT_PALETTE <- c(\n  \"#009E73\",  # 1 — brand green (first categorical series)\n  \"#C475FD\",  # 2 — lavender\n  \"#4467A3\",  # 3 — blue\n  \"#BD8233\",  # 4 — ochre\n  \"#AE3030\",  # 5 — matte red\n  \"#2ABCCD\",  # 6 — cyan\n  \"#954477\",  # 7 — rose\n  \"#99B314\"   # 8 — lime\n)\nBRAND <- IMPRINT_PALETTE[1]  # #009E73 — patient data (contrasting color)\nROSE  <- IMPRINT_PALETTE[7]  # #954477 — girls' reference percentile bands\n\n# Reference data: WHO weight-for-age for girls, 0–60 months (synthetic)\nage_months <- 0:60\n\n# Smooth parametric model approximating WHO LMS growth curves (girls)\np50_fn <- function(t) 3.3 + 2.0 * sqrt(t) - 0.016 * t\nsd_fn  <- function(t) 0.45 + 0.12 * sqrt(t)\n\nref_df <- data.frame(\n  age = age_months,\n  P3  = p50_fn(age_months) - 1.880 * sd_fn(age_months),\n  P10 = p50_fn(age_months) - 1.280 * sd_fn(age_months),\n  P25 = p50_fn(age_months) - 0.675 * sd_fn(age_months),\n  P50 = p50_fn(age_months),\n  P75 = p50_fn(age_months) + 0.675 * sd_fn(age_months),\n  P90 = p50_fn(age_months) + 1.280 * sd_fn(age_months),\n  P97 = p50_fn(age_months) + 1.880 * sd_fn(age_months)\n)\n\n# Individual patient: girl tracking near the 30th percentile\npatient_ages  <- c(0, 2, 4, 6, 9, 12, 15, 18, 24, 30, 36, 42, 48, 54, 60)\nbase_weight   <- p50_fn(patient_ages) - 0.45 * sd_fn(patient_ages)\npatient_df    <- data.frame(\n  age    = patient_ages,\n  weight = base_weight + rnorm(length(patient_ages), 0, 0.10)\n)\n\n# Right-margin percentile labels at age = 60 (last row of ref_df)\nn_last   <- nrow(ref_df)\nlabel_df <- data.frame(\n  x     = 61.5,\n  y     = c(ref_df$P3[n_last], ref_df$P10[n_last], ref_df$P25[n_last],\n            ref_df$P50[n_last], ref_df$P75[n_last], ref_df$P90[n_last],\n            ref_df$P97[n_last]),\n  label = c(\"P3\", \"P10\", \"P25\", \"P50\", \"P75\", \"P90\", \"P97\")\n)\n\n# Title (49 chars < 67 baseline → default font size is fine)\nplot_title <- \"line-growth-percentile · r · ggplot2 · anyplot.ai\"\n\n# Plot\np <- ggplot(ref_df, aes(x = age)) +\n  # Percentile bands: graduated intensity (darker at extremes, lighter near median)\n  geom_ribbon(aes(ymin = P3,  ymax = P10), fill = ROSE, alpha = 0.45) +\n  geom_ribbon(aes(ymin = P90, ymax = P97), fill = ROSE, alpha = 0.45) +\n  geom_ribbon(aes(ymin = P10, ymax = P25), fill = ROSE, alpha = 0.28) +\n  geom_ribbon(aes(ymin = P75, ymax = P90), fill = ROSE, alpha = 0.28) +\n  geom_ribbon(aes(ymin = P25, ymax = P75), fill = ROSE, alpha = 0.12) +\n  # Percentile boundary lines (subtle)\n  geom_line(aes(y = P3),  color = ROSE, linewidth = 0.4, alpha = 0.6) +\n  geom_line(aes(y = P10), color = ROSE, linewidth = 0.4, alpha = 0.6) +\n  geom_line(aes(y = P25), color = ROSE, linewidth = 0.4, alpha = 0.6) +\n  geom_line(aes(y = P75), color = ROSE, linewidth = 0.4, alpha = 0.6) +\n  geom_line(aes(y = P90), color = ROSE, linewidth = 0.4, alpha = 0.6) +\n  geom_line(aes(y = P97), color = ROSE, linewidth = 0.4, alpha = 0.6) +\n  # Emphasized median (P50) line\n  geom_line(aes(y = P50), color = ROSE, linewidth = 1.4) +\n  # Individual patient data (brand green — contrasting with rose reference)\n  geom_line(\n    data      = patient_df,\n    aes(x = age, y = weight),\n    color     = BRAND,\n    linewidth = 1.2\n  ) +\n  geom_point(\n    data  = patient_df,\n    aes(x = age, y = weight),\n    color = BRAND,\n    size  = 2.5,\n    shape = 16\n  ) +\n  # Percentile labels in right margin (clip = \"off\" allows drawing past panel edge)\n  geom_text(\n    data      = label_df,\n    aes(x = x, y = y, label = label),\n    color     = INK_MUTED,\n    size      = 2.8,\n    hjust     = 0,\n    fontface  = \"plain\"\n  ) +\n  # Axis scales\n  scale_x_continuous(\n    breaks = seq(0, 60, by = 12),\n    labels = c(\"Birth\", paste0(1:5, \" yr\"))\n  ) +\n  scale_y_continuous(\n    breaks       = seq(0, 30, by = 5),\n    minor_breaks = seq(0, 30, by = 1),\n    expand       = expansion(mult = c(0.02, 0.05))\n  ) +\n  labs(\n    title = plot_title,\n    x     = \"Age\",\n    y     = \"Weight (kg)\"\n  ) +\n  # clip = \"off\" lets right-margin labels render past the panel boundary\n  coord_cartesian(clip = \"off\") +\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.major = element_line(color = GRID, linewidth = 0.3),\n    panel.grid.minor = element_line(color = GRID, linewidth = 0.15),\n    panel.border     = element_rect(color = INK_SOFT, fill = NA, linewidth = 0.4),\n    axis.line        = element_blank(),\n    axis.title       = element_text(color = INK,      size = 10),\n    axis.text        = element_text(color = INK_SOFT, size = 8),\n    plot.title       = element_text(color = INK,      size = 12, face = \"bold\"),\n    plot.margin      = margin(t = 10, r = 55, b = 10, l = 10, unit = \"pt\"),\n    legend.position  = \"none\"\n  )\n\n# Save\nggsave(\n  filename = sprintf(\"plot-%s.png\", THEME),\n  plot     = p,\n  device   = ragg::agg_png,\n  width    = 8,\n  height   = 4.5,\n  units    = \"in\",\n  dpi      = 400\n)\n"}