{"spec_id":"raincloud-basic","library":"ggplot2","language":"r","code":"#' anyplot.ai\n#' raincloud-basic: Basic Raincloud Plot\n#' Library: ggplot2 3.5.1 | R 4.4.1\n#' Quality: 92/100 | Created: 2026-05-27\n\nlibrary(ggplot2)\nlibrary(dplyr)\nlibrary(tibble)\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\"\nINK_MUTED   <- if (THEME == \"light\") \"#6B6A63\" else \"#A8A79F\"\nGRID        <- if (THEME == \"light\") \"#6B6A63\" else \"#A8A79F\"\nBOX_FILL    <- if (THEME == \"light\") \"#FFFDF6\" else \"#242420\"\n\nIMPRINT_PALETTE <- c(\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\",\n                     \"#AE3030\", \"#2ABCCD\", \"#954477\", \"#99B314\")\n\n# Data — psychology reaction-time experiment across three sleep conditions.\n# Sleep-deprived group is bimodal: a fast subgroup that stayed alert and a\n# slow subgroup whose responses degraded — exactly the kind of structure a\n# box plot alone would hide, which is the canonical raincloud use-case.\ngroups <- c(\"Control\", \"Sleep-deprived\", \"Caffeinated\")\nn_per  <- 140\n\nrt <- tibble(\n    category = factor(rep(groups, each = n_per), levels = groups),\n    value = c(\n        rnorm(n_per, mean = 420, sd = 55),\n        c(\n            rnorm(round(n_per * 0.55), mean = 470, sd = 40),\n            rnorm(n_per - round(n_per * 0.55), mean = 640, sd = 55)\n        ),\n        rnorm(n_per, mean = 365, sd = 45)\n    )\n)\n\n# Cloud — half-violin built from a per-group KDE drawn as a ribbon that rises\n# above each category baseline. Pure ggplot2 has no half-violin geom, so the\n# density is computed manually and clipped to the [baseline, baseline+height]\n# band so it never crosses into the row above.\ncloud_height <- 0.42\ncloud_data <- rt %>%\n    group_by(category) %>%\n    group_modify(~ {\n        d <- density(.x$value, adjust = 1.0, n = 256)\n        tibble(value = d$x, dens = d$y / max(d$y) * cloud_height)\n    }) %>%\n    ungroup() %>%\n    mutate(cat_pos = as.numeric(category))\n\n# Rain — jittered points placed below each category baseline.\nrain_data <- rt %>%\n    mutate(\n        cat_pos = as.numeric(category),\n        y_pos   = cat_pos - 0.18 - runif(n(), 0, 0.20)\n    )\n\nbox_data <- rt %>%\n    mutate(cat_pos = as.numeric(category))\n\n# Plot\np <- ggplot() +\n    geom_ribbon(\n        data = cloud_data,\n        aes(x = value, ymin = cat_pos, ymax = cat_pos + dens,\n            fill = category, color = category),\n        alpha = 0.55, linewidth = 0.5\n    ) +\n    geom_boxplot(\n        data = box_data,\n        aes(x = value, y = cat_pos, group = category, color = category),\n        width = 0.14, fill = BOX_FILL, alpha = 0.95,\n        outlier.shape = NA, linewidth = 0.5,\n        orientation = \"y\"\n    ) +\n    geom_point(\n        data = rain_data,\n        aes(x = value, y = y_pos, color = category),\n        size = 1.1, alpha = 0.55, stroke = 0\n    ) +\n    scale_fill_manual(values = IMPRINT_PALETTE[1:3]) +\n    scale_color_manual(values = IMPRINT_PALETTE[1:3]) +\n    scale_y_continuous(\n        breaks = seq_along(groups),\n        labels = groups,\n        expand = expansion(add = c(0.45, 0.55))\n    ) +\n    scale_x_continuous(\n        breaks = seq(300, 900, by = 100),\n        labels = function(x) paste0(x, \" ms\"),\n        limits = c(NA, 905),\n        expand = expansion(mult = c(0.02, 0.01))\n    ) +\n    labs(\n        title = \"Reaction Time by Condition · raincloud-basic · r · ggplot2 · anyplot.ai\",\n        x = \"Reaction time\",\n        y = NULL\n    ) +\n    guides(fill = \"none\", color = \"none\") +\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, linewidth = 0.1),\n        panel.grid.major.y = element_blank(),\n        panel.grid.minor   = element_blank(),\n        axis.title.x       = element_text(color = INK, size = 10,\n                                          margin = margin(t = 8)),\n        axis.text.x        = element_text(color = INK_SOFT, size = 9),\n        axis.text.y        = element_text(color = INK, size = 11,\n                                          margin = margin(r = 6)),\n        axis.line.x        = element_line(color = INK_SOFT, linewidth = 0.3),\n        axis.ticks         = element_blank(),\n        plot.title         = element_text(color = INK, size = 11,\n                                          margin = margin(b = 14),\n                                          face = \"bold\"),\n        plot.margin        = margin(20, 28, 16, 16)\n    )\n\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"}