我正在尝试使用R(最好使用ggplot2
)生成一种分组小提琴图的变体,类似于下面的图:
以下是可重复的示例代码生成的:
# Load libraries #
library(tidyverse)
# Create dummy data #
set.seed(321)
df <- data.frame(X = rep(c("X1", "X2"), each = 100),
Y = rgamma(n = 200, shape = 2, rate = 2),
Z = rep(c("Za", "Zb"), rep = 100),
stringsAsFactors = FALSE)
# Grouped violin plot #
df %>%
ggplot(., aes(x = X, y = Y, fill = Z)) +
geom_violin(draw_quantiles = 0.5) +
scale_fill_manual(values = c("Za" = "red", "Zb" = "blue"))
我希望实现的效果是,中位数以上的密度和中位数以下的密度颜色不同,就像下面的图表一样:
我使用以下代码为数据中的组合 X = X1
和 Z = Za
生成了上述(单个)小提琴图:
## Shaded violin plot ##
# Calculate limits and median #
df.lim <- df %>%
filter(X == "X1", Z == "Za") %>%
summarise(Y_min = min(Y),
Y_qnt = quantile(Y, 0.5),
Y_max = max(Y))
# Calculate density, truncate at limits and assign shade category #
df.dens <- df %>%
filter(X == "X1", Z == "Za") %>%
do(data.frame(LOC = density(.$Y)$x,
DENS = density(.$Y)$y)) %>%
filter(LOC >= df.lim$Y_min, LOC <= df.lim$Y_max) %>%
mutate(COL = ifelse(LOC > df.lim$Y_qnt, "Empty", "Filled"))
# Find density values at limits #
df.lim.2 <- df.dens %>%
filter(LOC == min(LOC) | LOC == max(LOC))
# Produce shaded single violin plot #
df.dens %>%
ggplot(aes(x = LOC)) +
geom_area(aes(y = DENS, alpha = COL), fill = "red") +
geom_area(aes(y = -DENS, alpha = COL), fill = "red") +
geom_path(aes(y = DENS)) +
geom_path(aes(y = -DENS)) +
geom_segment(data = df.lim.2, aes(x = LOC, y = DENS, xend = LOC, yend = -DENS)) +
coord_flip() +
scale_alpha_manual(values = c("Empty" = 0.1, "Filled" = 1))
如您在代码中所见,我正在使用
density
函数水平地构建小提琴图,然后翻转坐标轴。问题出现在当我尝试生成分组小提琴图时,因为用于显示组X
和Z
的轴已经用于密度的“高度”,所以会出现问题。我尝试通过按组重复所有计算来达到相同的结果,但我卡在了最后一步:## Shaded grouped violin plot ##
# Calculate limits and median by group #
df.lim <- df %>%
group_by(X, Z) %>%
summarise(Y_min = min(Y),
Y_qnt = quantile(Y, 0.5),
Y_max = max(Y))
# Calculate density, truncate at limits and assign shade category by group #
df.dens <- df %>%
group_by(X, Z) %>%
do(data.frame(LOC = density(.$Y)$x,
DENS = density(.$Y)$y)) %>%
left_join(., df.lim, by = c("X", "Z")) %>%
filter(LOC >= Y_min, LOC <= Y_max) %>%
mutate(COL = ifelse(LOC > Y_qnt, "Empty", "Filled"))
# Find density values at limits by group #
df.lim.2 <- df.dens %>%
group_by(X, Z) %>%
filter(LOC == min(LOC) | LOC == max(LOC))
# Produce shaded grouped violin plot #
df.dens %>%
ggplot(aes(x = LOC, group = interaction(X, Z))) +
# The following two lines don't work when included #
#geom_area(aes(y = DENS, alpha = COL), fill = "red") +
#geom_area(aes(y = -DENS, alpha = COL), fill = "red") +
geom_path(aes(y = DENS)) +
geom_path(aes(y = -DENS)) +
geom_segment(data = df.lim.2, aes(x = LOC, y = DENS, xend = LOC, yend = -DENS)) +
coord_flip() +
scale_alpha_manual(values = c("Empty" = 0.1, "Filled" = 1))
运行上面的代码将为每个组绘制小提琴图的轮廓,每个小提琴图叠在其他小提琴图之上。但一旦我尝试包含
geom_area
线条,代码就会失败。我的直觉告诉我,我需要以某种方式生成“阴影”小提琴图作为新的
geom
,然后可以在ggplot2
图形的通用结构下使用,但我不知道如何做到这一点,因为我的编码技能还没有达到那个水平。任何帮助或指针,无论是沿着我的思路还是朝着不同的方向,都将不胜感激。谢谢您的时间。
geom_area()
无法解决您的问题。最好用geom_polygon()
替换它。我发现创建自己的几何对象和其他内容的最佳指南在这里:https://cran.r-project.org/web/packages/ggplot2/vignettes/extending-ggplot2.html。 - teunbrand