本文翻译自:https://www.cedricscherer.com/2021/06/06/visualizing-distributions-with-raincloud-plots-and-how-to-create-them-with-ggplot2/
如何展示你的数据?
对于大多数人来说,小提琴图已经不是展示数据的新鲜方法了:这种图表在 23 年就前开始使用。而在图中加入数据分布的总览,甚至是原始数据,将会是一个非常好的补充。而包含了箱线图、小提琴图以及抖动点图的云雨图,就是这种情况比较合适的实现。
云雨图于 2019 年推出,作为一种克服在绘制带有误差条的条形图(也称为炸药图)或箱线图时隐藏真实数据分布问题的方法。云雨图结合了多种图表类型来同时可视化原始数据、数据密度分布以及关键汇总统计。
| 图一 |
 |
从箱线图开始
箱线图是一种表现数据分布汇总的有效形式。然而,它并不总是能达到我们想要的效果。为了说明这个问题,我们先模拟一组数据:
set.seed(2021)
data <- tibble( group = factor(c(rep("Group 1", 100), rep("Group 2", 250), rep("Group 3", 25))), value = c(seq(0, 20, length.out = 100), c(rep(0, 5), rnorm(30, 2, .1), rnorm(90, 5.4, .1), rnorm(90, 14.6, .1), rnorm(30, 18, .1), rep(20, 5)), rep(seq(0, 20, length.out = 5), 5)) ) %>% rowwise() %>% mutate(value = if_else(group == "Group 2", value + rnorm(1, 0, .4), value))
|
然后绘制箱线图:
ggplot(data, aes(x = group, y = value)) + geom_boxplot(fill = "grey92")
|
| 图二 |
 |
三个组别的箱线图完全一致,但每组的样本量大小,潜在的分布模式却难以识别。 当然,在样本大小上添加注释可能被认为是一种很好的做法,但它仍然不能告诉你有关实际模式的太多信息。
n_fun <- function(x){ return(data.frame(y = median(x) - 1.25, label = paste0("n = ",length(x)))) }
ggplot(data, aes(x = group, y = value)) + geom_boxplot(fill = "grey92") + stat_summary( geom = "text", fun.data = n_fun, size = 5 )
|
| 图三 |
 |
更进一步:添加原始数据
一个显著的改进方法是添加数据点。由于我们已经知道第 2 组由 200 个观测值组成,让我们使用抖动带代替传统的带状图:
ggplot(data, aes(x = group, y = value)) + geom_boxplot(fill = "grey92") + geom_point( size = 2, alpha = .3, position = position_jitter( seed = 1, width = .2 ) )
|
| 图四 |
 |
图案很不一样!第 1 组的值均匀分布。第2组中的值聚集在该组四分位数周围有明显差距!并且第 3 组的少数观察结果都是整数。
我们可以用ggbeeswarm::geom_quasirandom()或ggforce::geom_sina()包来改进散点:
ggplot(data, aes(x = group, y = value)) + geom_boxplot(fill = "grey92") + ggbeeswarm::geom_quasirandom( size = 1.5, alpha = .4, width = .2 )
|
| 图五 |
 |
ggplot(data, aes(x = group, y = value)) + geom_boxplot(fill = "grey92") + ggforce::geom_sina( size = 1.5, alpha = .4, maxwidth = .5 )
|
| 图六 |
 |
小提琴图:审稿人的噩梦?
小提琴图可用于可视化数值变量的分布。它是一条镜像密度曲线,表示沿连续轴的数据点数量。
ggplot(data, aes(x = group, y = value)) + geom_violin(fill = "grey92")
|
| 图七 |
 |
默认情况下,小提琴图可能看起来有点奇怪。默认设置 (scale = "area") 具有误导性。第 1 组看起来与第 3 组几乎相同,但包含的观察值是其四倍。此外,平滑核的默认标准差在该数据的情况下不是最佳的,因为它平滑了没有任何数据的区域,隐藏了数据的真实模式。
我们可以通过将宽度设置为观察次数scale = "count"并调整带宽(bw)来操纵这两个默认值。从美学上讲,可以删除轮廓:
ggplot(data, aes(x = group, y = value)) + geom_violin( fill = "grey72", color = NA, scale = "count", bw = .4 )
|
| 图八 |
 |
小提琴图允许明确表示分布,但不提供汇总统计数据。为了两全其美,它通常与箱线图混合使用——要么是带有”胡须“和异常值的完整箱线图,要么是仅指示中位数和四分位距 (IQR) 的箱线图:
ggplot(data, aes(x = group, y = value)) + geom_violin( fill = "grey72", color = NA, scale = "count", bw = .5 ) + geom_boxplot( fill = NA, width = .1 )
|
| 图九 |
 |
ggplot(data, aes(x = group, y = value)) + geom_violin( fill = "grey72", color = NA, scale = "count", bw = .5 ) + geom_boxplot( fill = NA, width = .1, coef = 0, outlier.color = NA )
|
| 图十 |
 |
你可能想知道:为什么不在箱线图上叠加原始数据,而是要使用小提琴图?好吧,如果你有很多的原始数据,绘制原始数据的所有方法都会变得困难,包括可读性以及运算方面。在这种情况下,小提琴图是一个很好的选择,结合一些汇总统计数据甚至更好。但我们也可以将这三者结合起来……
云雨图:一个绝佳的选择
云雨图可用于同时可视化原始数据、数据分布和关键汇总统计数据。实际上,它是一个混合图:由一半小提琴图、箱线图和作为某种散点的原始数据组成:
ggplot(data, aes(x = group, y = value)) + ggdist::stat_halfeye( adjust = .5, width = .6, justification = -.2, .width = 0, point_colour = NA ) + geom_boxplot( width = .12, outlier.color = NA ) + ggdist::stat_dots( side = "left", justification = 1.1, binwidth = .25 ) + coord_cartesian(xlim = c(1.2, NA))
|
| 图十一 |
 |
在这里,我们使用了{ggdist}包中的两个几何对象的图层,即{stat_dots()}来绘制“雨”,用{stat_halfeye}来绘制云。我们对两个对象的位置都进行了一定的偏移调整,使得他们能彼此相邻并为中间的箱线图腾出空间。我们还通过设置.width为0以及point_colour来将halfeye中的平板间隔去掉。图像需要手动调整一些样式,比如justification和带的数量很大程度上取决于你的数据。我们还可以简单地设置x轴的范围来去除左侧的空白部分。
也可以使用{ggdist}中默认的密度曲线和平板间隔来进行展示:
ggplot(data, aes(x = group, y = value)) + ggdist::stat_halfeye( adjust = .5, width = .6, .width = c(.5, .95) ) + ggdist::stat_dots( side = "left", dotsize = .8, justification = 1.05, binwidth = .3 ) + coord_cartesian(xlim = c(1.2, NA))
|
| 图十二 |
 |
这种方式虽然可以直观展示四分位间距和 95% 区间,但也可能产生误导,让人以为这是置信区间。所以,使用这种图形时,最好再添加一个注释,以确保读者理解平板间隔表示的内容。
当然,可以用抖动点图甚至是条码图来代替点图。我们可以使用{gghalves}包中的geom_half_dotplot()函数。这个函数可以进行ggplot2默认图层geom_point()和geom_jitter()无法做到的调整:
ggplot(data, aes(x = group, y = value)) + ggdist::stat_halfeye( adjust = .5, width = .6, .width = 0, justification = -.2, point_colour = NA ) + geom_boxplot( width = .15, outlier.shape = NA ) + gghalves::geom_half_point( side = "l", range_scale = .4, alpha = .3 ) + coord_cartesian(xlim = c(1.2, NA), clip = "off")
|
| 图十三 |
 |
注意,{gghalves}包也会沿y轴添加一些抖动。这个包也提供了绘制半个箱线图的方法,但这里不推荐。
另一个替代方法是使用geom_jitter()(或geom_point())将抖动点放在箱线图上:
ggplot(data, aes(x = group, y = value)) + ggdist::stat_halfeye( adjust = .5, width = .6, .width = 0, justification = -.3, point_colour = NA) + geom_boxplot( width = .25, outlier.shape = NA ) + geom_point( size = 1.3, alpha = .3, position = position_jitter( seed = 1, width = .1 ) ) + coord_cartesian(xlim = c(1.2, NA), clip = "off")
|
| 图十四 |
 |
最后一种选择是,使用条码图而非抖动点:
ggplot(data, aes(x = group, y = value)) + ggdist::stat_halfeye( adjust = .5, width = .6, .width = 0, justification = -.2, point_colour = NA ) + geom_boxplot( width = .15, outlier.shape = NA ) + geom_point( shape = 95, size = 10, alpha = .2 ) + coord_cartesian(xlim = c(1.2, NA), clip = "off")
|
| 图十五 |
 |
如图,这并不是最优选择,因为第三组的实际值很难倍被观察到,因为它们完全和箱线图重合。我们可以用geom_half_pint(),并取消沿x轴增加的抖动:
ggplot(data, aes(x = group, y = value)) + ggdist::stat_halfeye( adjust = .5, width = .6, .width = 0, justification = -.2, point_colour = NA ) + geom_boxplot( width = .15, outlier.shape = NA ) + gghalves::geom_half_point( side = "l", shape = 95, range_scale = 0, size = 10, alpha = .2 ) + coord_cartesian(xlim = c(1.2, NA), clip = "off")
|
| 图十六 |
 |
点击这里跳转到 RStata 短书平台获取附件:使用 ggplot2 绘制云雨图
评论