云雨图

作者

[编辑] 王诗翔

[贡献] Claude Code —— AI 生成,独立 AI 复核;

修改于

2026-10-19

🤖 AI Skill — Download the Bizard unified skill for your AI assistant ⬇️ Download Skill ZIP
🔌 MCP Server — Let AI agents (Claude Code, Cursor, …) query Bizard live: claude mcp add bizard -- npx @openbiox/bizard-mcp — or the free hosted instance (no install, faster from mainland China): claude mcp add --transport http bizard http://biotree.top:38123/bizard-mcp/mcp 📖 MCP Docs
📖 Cite Bizard — if Bizard helps your research, please cite: Li, K., Zheng, H., Huang, K., et al. (2026). Bizard: A Community-Driven Platform for Accelerating and Enhancing Biomedical Data Visualization. iMetaMed, e70038. 🔗 DOI / BibTeX

云雨图(raincloud plot)一次展示分布的三个层面:半小提琴(云)呈现核密度形状、窄箱线图标出中位数与四分位、蜂群散点(雨)逐点暴露每一次观测。与只画柱状图加误差棒或只画箱线图不同,离群点、偏态与样本量都保持可见——这正是当各组分布形状(而非仅均值)不同时,推荐用它替代柱状图的原因。本教程复现《顶刊图社》系列(第017期)介绍的图表类型,见文末参考文献。

示例

RaincloudPlot DEMO

这张云雨图比较了三种企鹅的体重(palmerpenguins 数据集)。每种企鹅左侧的半小提琴勾勒密度形状,中间的窄箱线图标出中位数与两个四分位,右侧的蜂群散点把每一只鸟沿数值轴逐一画出——Gentoo 企鹅明显更重(中位数 5,050 g,对比 3,700 g),另外两种企鹅则在 5,000 g 以下大量重叠。

环境配置

  • 系统要求:跨平台(Linux/MacOS/Windows)

  • 编程语言:R

  • 依赖包:tidyverse;ggbeeswarm;palmerpenguins

# Installing necessary packages
if (!requireNamespace("tidyverse", quietly = TRUE)) {
  install.packages("tidyverse")
}
if (!requireNamespace("ggbeeswarm", quietly = TRUE)) {
  install.packages("ggbeeswarm")
}
if (!requireNamespace("palmerpenguins", quietly = TRUE)) {
  install.packages("palmerpenguins")
}

# Load packages
library(tidyverse)
library(ggbeeswarm)
library(palmerpenguins)

数据准备

示例使用 palmerpenguins 内置的 penguins 数据集(344 条观测、三种企鹅)。云雨图需要每行一条观测,并包含一个分组列(species)与一个数值列(body_mass_g)。

data("penguins")

penguins_clean <- penguins |>
  filter(!is.na(body_mass_g)) |>
  mutate(species = factor(species))

head(penguins_clean, 3)
# A tibble: 3 × 8
  species island    bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
  <fct>   <fct>              <dbl>         <dbl>             <int>       <int>
1 Adelie  Torgersen           39.1          18.7               181        3750
2 Adelie  Torgersen           39.5          17.4               186        3800
3 Adelie  Torgersen           40.3          18                 195        3250
# ℹ 2 more variables: sex <fct>, year <int>

小提琴的左半边并不是特殊图层——我们对每组估计核密度,再把它画成从组中心向左展开的多边形。下面的辅助函数让教程其余部分保持简洁。

# Half-violin polygons: for each group, the density curve is mirrored to the
# left of the group centre (x = group position) and closed as a polygon.
half_violin <- function(data, group_col, value_col, width = 0.33) {
  groups <- levels(factor(data[[group_col]]))
  map_dfr(groups, function(g) {
    values <- data[[value_col]][as.character(data[[group_col]]) == g]
    d <- density(values, n = 200)
    tibble(
      group = g,
      x = which(groups == g) - d$y / max(d$y) * width,
      y = d$x)
  })
}

可视化

1. 基础绘图

三个组件按每组逐渐增大的 x 偏移堆叠:半小提琴在组中心,窄箱线图紧随其右,蜂群散点在最外侧。

violin_data <- half_violin(penguins_clean, "species", "body_mass_g")
centres <- set_names(seq_along(levels(penguins_clean$species)),
                     levels(penguins_clean$species))
x_pos <- centres[as.character(penguins_clean$species)]

raincloud_colors <- c(Adelie = "#3a86ff", Chinstrap = "#f4a261", Gentoo = "#2a9d8f")

ggplot(penguins_clean) +
  geom_polygon(data = violin_data, aes(x = x, y = y, fill = group, group = group),
               alpha = .65, colour = NA) +
  geom_boxplot(aes(x = x_pos + .15, y = body_mass_g, group = species, colour = species),
               width = .09, outlier.shape = NA, fill = "white", linewidth = .45) +
  geom_beeswarm(aes(x = x_pos + .32, y = body_mass_g, colour = species),
                cex = .5, size = .85, alpha = .85) +
  scale_fill_manual(values = raincloud_colors) +
  scale_colour_manual(values = raincloud_colors) +
  scale_x_continuous(breaks = centres, labels = levels(penguins_clean$species)) +
  labs(x = NULL, y = "Body mass (g)") +
  theme_bw() +
  theme(legend.position = "none")
图 1: 基础云雨图:企鹅体重
提示

关键参数:

  • width:半小提琴从组中心伸出的宽度(也是三个组件的视觉平衡点)。
  • + .15 / + .32:把箱线图与蜂群散点放到中心右侧的 x 偏移;与 width 一起调节。
  • cex:蜂群散点的间距系数;越小越紧凑。

2. 带注释的云雨图

再加上均值标记与样本量,图形就是一份完整摘要:菱形标出每个箱体内部的均值,横轴标签携带样本量。

ggplot(penguins_clean) +
  geom_polygon(data = violin_data, aes(x = x, y = y, fill = group, group = group),
               alpha = .65, colour = NA) +
  geom_boxplot(aes(x = x_pos + .15, y = body_mass_g, group = species, colour = species),
               width = .09, outlier.shape = NA, fill = "white", linewidth = .45) +
  geom_beeswarm(aes(x = x_pos + .32, y = body_mass_g, colour = species),
                cex = .5, size = .85, alpha = .85) +
  stat_summary(aes(x = x_pos + .15, y = body_mass_g, group = species),
               fun = mean, geom = "point", shape = 18, size = 3, colour = "grey15") +
  scale_fill_manual(values = raincloud_colors) +
  scale_colour_manual(values = raincloud_colors) +
  scale_x_continuous(
    breaks = centres,
    labels = paste0(levels(penguins_clean$species), "\n(n = ",
                    table(penguins_clean$species)[levels(penguins_clean$species)], ")")) +
  labs(x = NULL, y = "Body mass (g)") +
  theme_bw() +
  theme(legend.position = "none", panel.grid.minor = element_blank())
图 2: 带均值标记与样本量的云雨图

3. 横向排布

当组名较长(临床队列、组学聚类)时,翻转坐标轴把组名放到 y 轴更易阅读;coord_flip() 会让整个云雨图横过来,而无需改动任何图层。

ggplot(penguins_clean) +
  geom_polygon(data = violin_data, aes(x = x, y = y, fill = group, group = group),
               alpha = .65, colour = NA) +
  geom_boxplot(aes(x = x_pos + .15, y = body_mass_g, group = species, colour = species),
               width = .09, outlier.shape = NA, fill = "white", linewidth = .45) +
  geom_beeswarm(aes(x = x_pos + .32, y = body_mass_g, colour = species),
                cex = .5, size = .85, alpha = .85) +
  scale_fill_manual(values = raincloud_colors) +
  scale_colour_manual(values = raincloud_colors) +
  scale_x_continuous(breaks = centres, labels = levels(penguins_clean$species)) +
  labs(x = NULL, y = "Body mass (g)") +
  theme_bw() +
  theme(legend.position = "none", panel.grid.minor = element_blank()) +
  coord_flip()
图 3: 横向云雨图

应用场景

  • 跨组分布比较:当审稿人要求展示原始数据时,这是柱状图加误差棒的标准升级方案。
  • 临床与组学队列:组间样本量不齐、偏态常见;蜂群散点让每一位患者保持可见。
  • 小样本:当 n < 20 时,蜂群散点几乎可以替代小提琴,箱线图仍能概括四分位。
  • 半小提琴配方(density() + geom_polygon())适用于任何密度图层,纵向或横向皆可,且无需额外依赖包。

参考文献

[1] 顶刊图社 | 第017期:箱线图/小提琴图/云雨图. 微信公众号文章. http://mp.weixin.qq.com/s?__biz=MzU5NjE2ODU0OQ==&mid=2247483936&idx=1&sn=cf22ad37b9afc52a0af1f27daf8a4711

[2] Allen M, Poggiali D, Whitaker K, Marshall TR, Kievit RA. Raincloud plots: a multi-platform tool for robust data visualization. Wellcome Open Res. 2019;4:63. doi:10.12688/wellcomeopenres.15191.1