{"spec_id":"swimmer-clinical-timeline","library":"ggplot2","language":"r","code":"#' anyplot.ai\n#' swimmer-clinical-timeline: Swimmer Plot for Clinical Trial Timelines\n#' Library: ggplot2 3.5.1 | R 4.4.1\n#' Quality: 87/100 | Created: 2026-06-08\n\nlibrary(ggplot2)\nlibrary(dplyr)\nlibrary(tibble)\nlibrary(ragg)\n\n# Theme tokens (Imprint palette — theme-adaptive chrome)\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\"\nGRID_COLOR  <- if (THEME == \"light\") \"#CCCAC2\" else \"#3D3D3A\"\n\nIMPRINT_PALETTE <- c(\n  \"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\",\n  \"#AE3030\", \"#2ABCCD\", \"#954477\", \"#99B314\"\n)\n\n# Data: Phase II oncology trial — 25 patients across two treatment arms\n# Ordered by duration (descending) for the sorted swimmer layout\npatients <- tibble(\n  patient_id = sprintf(\"PT-%03d\", 1:25),\n  arm        = c(\n    \"Arm A\", \"Arm A\", \"Arm B\", \"Arm A\", \"Arm A\",\n    \"Arm B\", \"Arm A\", \"Arm B\", \"Arm A\", \"Arm B\",\n    \"Arm A\", \"Arm B\", \"Arm A\", \"Arm B\", \"Arm A\",\n    \"Arm B\", \"Arm A\", \"Arm B\", \"Arm A\", \"Arm B\",\n    \"Arm A\", \"Arm B\", \"Arm A\", \"Arm B\", \"Arm B\"\n  ),\n  duration   = c(\n    52, 48, 46, 44, 42,\n    40, 38, 36, 34, 32,\n    30, 28, 26, 24, 20,\n    18, 16, 14, 12, 10,\n     8,  8,  6,  6,  4\n  ),\n  ongoing    = c(\n    TRUE,  FALSE, FALSE, TRUE,  FALSE,\n    TRUE,  FALSE, FALSE, TRUE,  FALSE,\n    FALSE, TRUE,  FALSE, FALSE, TRUE,\n    FALSE, FALSE, FALSE, FALSE, FALSE,\n    FALSE, FALSE, FALSE, FALSE, FALSE\n  )\n) %>%\n  mutate(patient_id = factor(patient_id, levels = rev(patient_id)))\n\nongoing_pts <- patients %>% filter(ongoing)\n\n# Clinical event annotations (event times are within each patient's duration)\nevents <- tibble(\n  patient_id = c(\n    \"PT-001\", \"PT-001\", \"PT-002\", \"PT-002\",\n    \"PT-003\", \"PT-004\", \"PT-004\",\n    \"PT-005\", \"PT-005\", \"PT-006\", \"PT-006\",\n    \"PT-007\", \"PT-008\", \"PT-008\",\n    \"PT-009\", \"PT-010\", \"PT-010\",\n    \"PT-011\", \"PT-012\",\n    \"PT-013\", \"PT-014\", \"PT-014\",\n    \"PT-015\", \"PT-016\", \"PT-016\",\n    \"PT-017\", \"PT-018\", \"PT-020\"\n  ),\n  time       = c(\n     8, 20,  6, 40,\n    10,  8, 18,\n    12, 34, 10, 22,\n    30,  8, 28,\n    10,  8, 24,\n    14,  8,\n    20,  6, 20,\n     8, 10, 16,\n     6, 12,  6\n  ),\n  event_type = factor(c(\n    \"Partial Response\",    \"Complete Response\",   \"Partial Response\",    \"Progressive Disease\",\n    \"Partial Response\",    \"Partial Response\",    \"Complete Response\",\n    \"Partial Response\",    \"Progressive Disease\", \"Partial Response\",    \"Adverse Event\",\n    \"Progressive Disease\", \"Partial Response\",    \"Progressive Disease\",\n    \"Partial Response\",    \"Partial Response\",    \"Progressive Disease\",\n    \"Adverse Event\",       \"Partial Response\",\n    \"Progressive Disease\", \"Partial Response\",    \"Progressive Disease\",\n    \"Partial Response\",    \"Adverse Event\",       \"Progressive Disease\",\n    \"Partial Response\",    \"Progressive Disease\", \"Adverse Event\"\n  ), levels = c(\"Partial Response\", \"Complete Response\", \"Progressive Disease\", \"Adverse Event\"))\n) %>%\n  mutate(patient_id = factor(patient_id, levels = levels(patients$patient_id)))\n\n# Event visual encodings — Imprint palette positions (fixed, theme-independent)\nevent_colors <- c(\n  \"Partial Response\"    = IMPRINT_PALETTE[3],  # blue\n  \"Complete Response\"   = IMPRINT_PALETTE[8],  # lime\n  \"Progressive Disease\" = IMPRINT_PALETTE[5],  # matte red\n  \"Adverse Event\"       = \"#DDCC77\"             # amber warning anchor\n)\nevent_shapes <- c(\n  \"Partial Response\"    = 17L,  # solid triangle up\n  \"Complete Response\"   =  8L,  # star / asterisk\n  \"Progressive Disease\" = 18L,  # solid diamond\n  \"Adverse Event\"       =  4L   # X / cross\n)\n\n# Plot\np <- ggplot() +\n  # Treatment duration bars, colored by arm\n  geom_col(\n    data  = patients,\n    aes(x = patient_id, y = duration, fill = arm),\n    width = 0.65,\n    alpha = 0.82\n  ) +\n  # Clinical event markers\n  geom_point(\n    data = events,\n    aes(x = patient_id, y = time, shape = event_type, color = event_type),\n    size   = 2.5,\n    stroke = 0.9\n  ) +\n  # Arrows for patients still on treatment at data cutoff\n  geom_segment(\n    data        = ongoing_pts,\n    aes(x = patient_id, xend = patient_id, y = duration, yend = duration + 2.2),\n    arrow       = arrow(length = unit(0.13, \"cm\"), type = \"closed\"),\n    color       = INK,\n    linewidth   = 0.7,\n    inherit.aes = FALSE\n  ) +\n  coord_flip() +\n  scale_fill_manual(\n    values = c(\"Arm A\" = IMPRINT_PALETTE[1], \"Arm B\" = IMPRINT_PALETTE[2]),\n    name   = \"Treatment Arm\"\n  ) +\n  scale_shape_manual(values = event_shapes, name = \"Clinical Event\") +\n  scale_color_manual(values = event_colors, name = \"Clinical Event\") +\n  scale_y_continuous(\n    limits = c(0, 56),\n    breaks = seq(0, 52, by = 8),\n    expand = c(0, 0)\n  ) +\n  labs(\n    title   = \"swimmer-clinical-timeline · r · ggplot2 · anyplot.ai\",\n    x       = NULL,\n    y       = \"Time on Study (weeks)\",\n    caption = \"→ Arrow indicates patient still on treatment at data cutoff\"\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.major.x = element_line(color = GRID_COLOR, linewidth = 0.25, linetype = \"dotted\"),\n    panel.grid.major.y = element_blank(),\n    panel.grid.minor   = element_blank(),\n    axis.title.x       = element_text(color = INK,        size = 10),\n    axis.text.x        = element_text(color = INK_SOFT,   size = 8),\n    axis.text.y        = element_text(color = INK_SOFT,   size = 8),\n    plot.title         = element_text(color = INK,        size = 12,\n                                      margin = margin(b = 8)),\n    plot.caption       = element_text(color = INK_MUTED,  size = 7,\n                                      hjust = 0, margin = margin(t = 6)),\n    legend.background  = element_rect(fill = ELEVATED_BG, color = INK_SOFT,\n                                      linewidth = 0.3),\n    legend.text        = element_text(color = INK_SOFT,   size = 7.5),\n    legend.title       = element_text(color = INK,        size = 9),\n    legend.key         = element_rect(fill = ELEVATED_BG, color = NA),\n    legend.position    = \"right\",\n    legend.margin      = margin(6, 8, 6, 8),\n    plot.margin        = margin(15, 15, 10, 10)\n  )\n\n# Save (ragg device — no Cairo dependency)\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"}