{"spec_id":"box-grouped","library":"ggplot2","language":"r","code":"#' anyplot.ai\n#' box-grouped: Grouped Box Plot\n#' Library: ggplot2 3.5.1 | R 4.4.1\n#' Quality: 89/100 | Created: 2026-08-18\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\"\nINK         <- if (THEME == \"light\") \"#1A1A17\" else \"#F0EFE8\"\nINK_SOFT    <- if (THEME == \"light\") \"#4A4A44\" else \"#B8B7B0\"\nIMPRINT_PALETTE <- c(\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\",\n                     \"#AE3030\", \"#2ABCCD\", \"#954477\", \"#99B314\")\n\n# --- Data -----------------------------------------------------------------\ndepartments <- c(\"Engineering\", \"Sales\", \"Marketing\", \"Support\")\nlevels_exp  <- c(\"Junior\", \"Mid\", \"Senior\")\n\ndept_baseline <- c(Engineering = 62, Sales = 58, Marketing = 55, Support = 60)\nlevel_shift   <- c(Junior = -8, Mid = 0, Senior = 9)\nlevel_spread  <- c(Junior = 9, Mid = 8, Senior = 7)\nn_per_group   <- 70\n\ndf <- expand.grid(\n  department = departments,\n  experience = levels_exp,\n  stringsAsFactors = FALSE\n) %>%\n  mutate(\n    mean_score = dept_baseline[department] + level_shift[experience],\n    sd_score   = level_spread[experience]\n  ) %>%\n  rowwise() %>%\n  mutate(score = list(rnorm(n_per_group, mean = mean_score, sd = sd_score))) %>%\n  ungroup() %>%\n  unnest(score) %>%\n  transmute(\n    department = factor(department, levels = departments),\n    experience = factor(experience, levels = levels_exp),\n    score\n  )\n\n# --- Plot -------------------------------------------------------------------\np <- ggplot(df, aes(x = department, y = score, fill = experience)) +\n  geom_boxplot(\n    position    = position_dodge(width = 0.75),\n    width       = 0.65,\n    linewidth   = 0.5,\n    color       = INK_SOFT,\n    outlier.size  = 1.8,\n    outlier.alpha = 0.7\n  ) +\n  stat_summary(\n    mapping     = aes(x = department, y = score, group = experience, shape = \"Mean\"),\n    fun         = mean,\n    geom        = \"point\",\n    position    = position_dodge(width = 0.75),\n    size        = 2.2,\n    stroke      = 0.6,\n    color       = INK,\n    fill        = PAGE_BG,\n    inherit.aes = FALSE\n  ) +\n  scale_fill_manual(values = IMPRINT_PALETTE[1:3], name = \"Experience Level\") +\n  scale_shape_manual(values = c(\"Mean\" = 23), name = NULL) +\n  labs(\n    title = \"box-grouped · r · ggplot2 · anyplot.ai\",\n    x     = \"Department\",\n    y     = \"Productivity Score\"\n  ) +\n  guides(\n    fill  = guide_legend(order = 1),\n    shape = guide_legend(order = 2)\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_blank(),\n    panel.grid.minor    = element_blank(),\n    panel.grid.major.y  = element_line(color = INK_SOFT, linewidth = 0.15),\n    axis.title          = element_text(color = INK, size = 10),\n    axis.text           = element_text(color = INK_SOFT, size = 8),\n    axis.ticks          = element_blank(),\n    plot.title          = element_text(color = INK, size = 12, face = \"bold\"),\n    legend.position       = \"top\",\n    legend.justification  = \"right\",\n    legend.background    = element_rect(fill = PAGE_BG, color = NA),\n    legend.key           = 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 = 9)\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"}