You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 16:12:52