如何用ggarrange正确排列甲虫物种箱线图,解决图形重复问题
问题描述
我有一份包含28种甲虫在不同地点标准化计数的数据集,想为每个物种(对应数据列)生成A、K两个条件的箱线图,再用ggarrange把所有图合并到一张图里。但生成的28个图除了y轴标签正确,其余内容全重复了,附上代码和输出图,求排查错误。
数据集说明:包含28种甲虫的标准化计数,sp列用于区分A/K组别。
代码
library(viridis) library("ggpubr") library(stringr) beetles<- read.csv("beetles.csv", header=TRUE) beetles$sp2<- str_replace_all(beetles$sp, "[:digit:]", "") beetles$sp3<- gsub("^K.+","K", gsub( "^A.+", "A",beetles$sp) ) specie<- colnames(beetles[2:29]) plot_list = list() for (i in seq_along(specie)) { tmpS<- specie[i] p = ggplot(beetles, aes(x = sp3, y = beetles[,tmpS], fill = factor(sp3))) + geom_boxplot(outlier.colour = NA) + scale_fill_viridis(discrete = TRUE, alpha = 0.6) + geom_point(aes(color = sp, group = factor(sp2)), size = 1, position = position_jitterdodge(0.25, 0, 0.75)) + stat_compare_means(method = "t.test") + #hrbrthemes::theme_ipsum() + theme(plot.title = element_text(size = 11))+ labs(x = NULL, y=tmpS, fill = 'sp3', title = "A boxplot with jitter") print(p) plot_list[[tmpS]] = p } k<- ggarrange( plotlist = plot_list, ncol = 7, nrow = 4, common.legend = TRUE ) ggsave("boxes.pdf", plot=k, width=20, height=20)
输出图

问题原因与解决方法
错误原因
问题出在ggplot的延迟求值机制:在循环里用y = beetles[,tmpS]指定y轴时,ggplot不会立即绑定当前迭代的列,而是等到最终渲染图形时才读取tmpS的值。循环结束后tmpS已经变成最后一个物种的列名,导致所有图都复用了最后一列的数据,仅y轴标签因提前赋值保持正确。
解决方法
方法1:修正循环内的列引用
使用ggplot提供的.data工具强制绑定当前迭代的列,避免延迟求值问题,只需修改aes中的y轴映射:
plot_list = list() for (i in seq_along(specie)) { tmpS<- specie[i] p = ggplot(beetles, aes(x = sp3, y = .data[[tmpS]], fill = factor(sp3))) + geom_boxplot(outlier.colour = NA) + scale_fill_viridis(discrete = TRUE, alpha = 0.6) + geom_point(aes(color = sp, group = factor(sp2)), size = 1, position = position_jitterdodge(0.25, 0, 0.75)) + stat_compare_means(method = "t.test") + theme(plot.title = element_text(size = 11))+ labs(x = NULL, y=tmpS, fill = '组别', title = "带散点抖动的箱线图") plot_list[[tmpS]] = p }
方法2:转成长格式数据(更推荐)
ggplot对长格式数据支持更友好,先将宽表转成“物种-计数”的长表,再按物种分组绘图,彻底避免循环求值问题:
library(tidyverse) library(viridis) library(ggpubr) library(stringr) # 读取并预处理数据 beetles<- read.csv("beetles.csv", header=TRUE) beetles$sp2<- str_replace_all(beetles$sp, "[:digit:]", "") beetles$sp3<- gsub("^K.+","K", gsub( "^A.+", "A",beetles$sp) ) # 宽表转长表 beetles_long <- beetles %>% pivot_longer(cols = 2:29, names_to = "species", values_to = "count") # 按物种分组生成所有图 plot_list <- beetles_long %>% group_by(species) %>% group_map(~ggplot(.x, aes(x = sp3, y = count, fill = factor(sp3))) + geom_boxplot(outlier.colour = NA) + scale_fill_viridis(discrete = TRUE, alpha = 0.6) + geom_point(aes(color = sp, group = factor(sp2)), size = 1, position = position_jitterdodge(0.25, 0, 0.75)) + stat_compare_means(method = "t.test") + theme(plot.title = element_text(size = 11))+ labs(x = NULL, y = .y$species, fill = '组别', title = "带散点抖动的箱线图")) # 合并并保存图形 k<- ggarrange( plotlist = plot_list, ncol = 7, nrow = 4, common.legend = TRUE ) ggsave("boxes.pdf", plot=k, width=20, height=20)
这种方法更符合tidy数据规范,代码简洁易维护,也不会出现求值延迟的问题。
内容的提问来源于stack exchange,提问作者Jan
相关产品推荐
相关产品推荐

