ggplot2中facet_wrap结合geom_density_ridges时第二个分面不显示问题
我正在处理一份CSV数据,读取后命名为datplot,其head()输出如下:
> head(datplot) # A tibble: 6 × 3 bta electrode value <chr> <chr> <dbl> 1 b0 Fz 3.03 2 b0 Cz 1.78 3 b0 Pz -1.05 4 b0 Fz 3.78 5 b0 Cz 2.82 6 b0 Pz -0.242
数据里的bta变量有"b0"和"b1"两个取值,对应不同分布的数值。我想用facet_wrap做双分面绘图,但结果只显示"b0"的分面,"b1"分面直接缺失了。
初始代码及问题表现
一开始用的ggplot代码是这样的:
time <- "225" col <- c("#004d8d", "#cc2701", "#e5b400") p1 <- datplot %>% ggplot(mapping = aes(x=value, y=factor(electrode, level = c('Fz','Cz','Pz')), group = electrode, color = electrode)) + scale_y_discrete() + geom_rect(data=data.frame(), inherit.aes = FALSE, mapping = aes( ymin = 0, ymax = Inf, xmin = -0.1 * min(stdev), xmax = 0.1 * max(stdev)), fill = "black", alpha = 0.1) + geom_density_ridges(data= subset(datplot, bta='b0'), scale = -0.5, alpha=0.2, show.legend = FALSE, quantile_lines = TRUE, quantiles = c(0.025, 0.5, 0.975), vline_color = alpha("white", 0.3), aes(fill = electrode)) + #plotb1 geom_density_ridges(data= subset(datplot, bta='b1'),scale = -0.5, alpha=0.5, show.legend = FALSE, quantile_lines = TRUE, quantiles = c(0.025, 0.5, 0.975), vline_color = alpha("white", 0.6), aes(fill = electrode)) + facet_wrap(~bta) + scale_color_manual(values = col, breaks = c("Fz", "Cz", "Pz")) + scale_fill_manual(values = col, breaks = c("Fz", "Cz", "Pz")) + labs(title=sprintf('%s ms.',time)) + ylab("Electrode") + xlab(TeX(r'(Signal (µV) Posteriors $\beta_1\cdot x\; (x=1)$)')) + theme_light() + theme(axis.text = element_text(size = 14)) + theme(axis.title = element_text(size = 16)) + theme(plot.title = element_text(size = 20)) + coord_flip(xlim = c(-8, 8), ylim = c(0.4,3.05), expand = FALSE, clip = "on") p1
运行后只显示"b0"的分面,但单独绘制"b1"的数据是正常的,说明问题出在同时用两个geom_density_ridges加facet_wrap的逻辑上。
更新后的尝试
按照建议取消了手动子集数据,直接用完整的datplot绘图,代码如下:
p1 <- datplot %>% ggplot(mapping = aes(x=value, y=factor(electrode, level = c('Fz','Cz','Pz')), group = electrode, color = electrode)) + scale_y_discrete() + geom_rect(data=data.frame(), inherit.aes = FALSE, mapping = aes( ymin = 0, ymax = Inf, xmin = -0.1 * min(stdev), xmax = 0.1 * max(stdev)), fill = "black", alpha = 0.1) + geom_density_ridges(data= datplot, scale = -0.5, alpha=0.5, show.legend = FALSE, quantile_lines = TRUE, quantiles = c(0.025, 0.5, 0.975), vline_color = alpha("white", 0.3), aes(fill = electrode)) + facet_wrap(~bta) + scale_color_manual(values = col, breaks = c("Fz", "Cz", "Pz")) + scale_fill_manual(values = col, breaks = c("Fz", "Cz", "Pz")) + labs(title=sprintf('%s ms.',time)) + ylab("Electrode") + xlab(TeX(r'(Signal (µV) Posteriors $\beta_1\cdot x\; (x=1)$)')) + theme_light() + theme(axis.text = element_text(size = 14)) + theme(axis.title = element_text(size = 16)) + theme(plot.title = element_text(size = 20)) + coord_flip(xlim = c(-8, 8), ylim = c(0.4,3.05), expand = FALSE, clip = "on") p1
结果还是只显示"b0"分面,求解决办法。
问题根源有两个,逐个解决:
1. coord_flip的ylim限制导致b1分面被裁剪
你设置的ylim = c(0.4,3.05)是基于b0数据的范围,但b1数据的y轴(因为coord_flip,实际对应原x轴的翻转)可能超出这个范围,ggplot会直接隐藏没有有效绘图区域的分面。
解决: 要么移除coord_flip里的ylim参数,要么根据所有数据的实际范围调整,比如改成ylim = c(0.4, 3.5)(具体数值看b1数据的分布)。
2. 初始代码手动子集与分面逻辑冲突
初始代码里给两个geom_density_ridges分别指定b0和b1的子集,再加上facet_wrap(~bta),会导致每个分面里只有对应geom的数据,而ggplot分面是基于全局数据分组的,这种手动子集的操作会让b1分面里没有b0的geom数据,b0分面里没有b1的geom数据,再加上ylim限制,直接触发了分面隐藏。
正确做法: 不要手动子集数据,让facet_wrap自动按bta分组,如果需要区分b0和b1的透明度,把bta映射到alpha美学即可:
time <- "225" col <- c("#004d8d", "#cc2701", "#e5b400") p1 <- datplot %>% ggplot(aes(x = value, y = factor(electrode, level = c('Fz','Cz','Pz')), group = interaction(electrode, bta), # 确保每个电极+bta组合单独计算密度 color = electrode, fill = electrode, alpha = bta)) + # 将bta映射到透明度 scale_y_discrete() + # 注意这里要明确stdev的来源,比如如果stdev是datplot的列,改成min(datplot$stdev) geom_rect(data = data.frame(), inherit.aes = FALSE, aes(ymin = 0, ymax = Inf, xmin = -0.1 * min(stdev), xmax = 0.1 * max(stdev)), fill = "black", alpha = 0.1) + geom_density_ridges(scale = -0.5, show.legend = FALSE, quantile_lines = TRUE, quantiles = c(0.025, 0.5, 0.975), vline_color = alpha("white", 0.3)) + scale_alpha_manual(values = c("b0" = 0.2, "b1" = 0.5)) + # 给不同bta设置透明度 facet_wrap(~bta) + scale_color_manual(values = col, breaks = c("Fz", "Cz", "Pz")) + scale_fill_manual(values = col, breaks = c("Fz", "Cz", "Pz")) + labs(title = sprintf('%s ms.', time)) + ylab("Electrode") + xlab(TeX(r'(Signal (µV) Posteriors $\beta_1\cdot x\; (x=1)$)')) + theme_light() + theme(axis.text = element_text(size = 14), axis.title = element_text(size = 16), plot.title = element_text(size = 20)) + coord_flip(xlim = c(-8, 8), expand = FALSE, clip = "on") # 移除ylim限制 p1
额外注意:stdev变量的来源
代码里geom_rect用到了stdev,如果这个变量不是全局变量或者不在datplot中,会报错,建议明确指定,比如min(datplot$stdev)(假设stdev是datplot的列)。
内容的提问来源于stack exchange,提问作者Unai Vicente

