{"spec_id":"swarm-basic","library":"ggplot2","language":"r","code":"#' anyplot.ai\n#' swarm-basic: Basic Swarm Plot\n#' Library: ggplot2 3.5.1 | R 4.4.1\n#' Quality: 84/100 | Created: 2026-07-26\n\nlibrary(ggplot2)\nlibrary(dplyr)\nlibrary(ragg)\nlibrary(scales)\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 ----------------------------------------------------------------\n# Reaction times across a cognitive-load experiment (psychology research)\nconditions <- c(\"Baseline\", \"Low Load\", \"High Load\", \"Time Pressure\")\nmeans <- c(420, 460, 540, 580)\nsds   <- c(45, 50, 65, 70)\nn_per_condition <- 45\n\nreaction_df <- bind_rows(lapply(seq_along(conditions), function(i) {\n  tibble::tibble(\n    condition     = conditions[i],\n    reaction_time = rnorm(n_per_condition, mean = means[i], sd = sds[i])\n  )\n})) %>%\n  mutate(condition = factor(condition, levels = conditions))\n\n# --- Swarm layout ----------------------------------------------------------\n# Bin observations along the value axis, then rank within each bin so\n# points that fall close in value are spread symmetrically around the\n# category's center line - a lightweight beeswarm approximation built\n# entirely from geom_point (ggplot2 has no native beeswarm geom).\nbin_width     <- diff(range(reaction_df$reaction_time)) / 24\npoint_spacing <- 0.05\nmax_offset    <- 0.42\n\nreaction_df <- reaction_df %>%\n  mutate(value_bin = round(reaction_time / bin_width)) %>%\n  group_by(condition, value_bin) %>%\n  mutate(\n    bin_rank = rank(reaction_time, ties.method = \"first\"),\n    bin_n    = n(),\n    x_offset = pmin(pmax((bin_rank - (bin_n + 1) / 2) * point_spacing, -max_offset), max_offset)\n  ) %>%\n  ungroup() %>%\n  mutate(condition_x = as.numeric(condition) + x_offset)\n\ncondition_means <- reaction_df %>%\n  group_by(condition) %>%\n  summarise(mean_rt = mean(reaction_time), x_pos = as.numeric(condition[1]), .groups = \"drop\")\n\n# --- Plot ------------------------------------------------------------------\np <- ggplot(reaction_df, aes(x = condition_x, y = reaction_time)) +\n  geom_point(\n    aes(fill = condition), shape = 21, size = 3.1, stroke = 0.3,\n    color = PAGE_BG, alpha = 0.85\n  ) +\n  geom_line(\n    data = condition_means,\n    aes(x = x_pos, y = mean_rt),\n    color = scales::alpha(INK, 0.4), linewidth = 0.5, linetype = \"dashed\"\n  ) +\n  geom_point(\n    data = condition_means,\n    aes(x = x_pos, y = mean_rt),\n    shape = 18, size = 3.4, color = INK, alpha = 0.9\n  ) +\n  scale_fill_manual(values = IMPRINT_PALETTE[seq_along(conditions)], guide = \"none\") +\n  scale_x_continuous(\n    breaks = seq_along(conditions),\n    labels = conditions,\n    expand = expansion(mult = c(0.1, 0.1))\n  ) +\n  labs(\n    title = \"swarm-basic · r · ggplot2 · anyplot.ai\",\n    x = \"Experimental Condition\",\n    y = \"Reaction Time (ms)\"\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 = scales::alpha(INK, 0.15), linewidth = 0.3),\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    axis.line           = element_line(color = INK_SOFT, linewidth = 0.3),\n    plot.title          = element_text(color = INK, size = 12)\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"}