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

R语言绘图列表重复最后一张图问题排查求助

问题:循环生成ggplot图存入列表后,ggarrange显示重复的最后一张图

我用R分析列数可变的数据集,包含线性回归步骤,需要把每列数据单独可视化。已经用循环实现分析,用ggsave能生成72张独立PDF图,但创建绘图列表用ggarrange查看时,得到的是72张最后一张图的重复副本。

原代码

library(ggplot2)
library(dplyr)
library(ggpubr)
rm(list = ls())
#Select data (csv format, first column cycle numbers)
filename <- file.choose()
Data<-read.csv(filename)
#subtract first row from all data (requires dplyr)
Results<-data.frame(matrix(ncol=2,nrow=0, dimnames=list(NULL, c("Well", "Slope"))))
Data %>% mutate_if(is.numeric, funs(.-first(.)))
#subset based on cycles 6 to 25
Data<-Data[c(6:25),]
yy <-ncol(Data)
plotList=list()
for (i in seq(from = 2, to = yy, by = 1)){
well<-Data[,i]
lm_fit <- lm(well ~ Cycle, data=Data)
cf <- coef(lm_fit)
Slope <- cf[2]
WellData<-merge(i,Slope)
Results<-rbind(Results, WellData)
pltName <- paste( 'Plot', i, sep = '' )
P1 <-eval(substitute(ggplot(Data, aes(Cycle, well)) + 
geom_point() + geom_smooth(method="lm")),list(i=i))
plotList[[i]]<-P1
filename<-(paste0(pltName,".pdf"))
ggsave(filename = filename)
}
colnames(Results) <- c("Well", "Slope")
Results[1]<-Results[1]-1
print(Results)
ggarrange(plotlist = plotList)

示例数据

Cycle   A1  A2  A3  A4
1   5451.07 5986.14 5922.59 7092.01
2   5953.04 6527.92 6505.93 7989.85
3   6227.3  6752.74 6783.56 8237.67
4   6477.02 6952.62 6959.91 8302.8
5   6660.14 7133.86 7149.98 8286.72
6   6804.14 7288.28 7301.48 8271.56
7   6919.78 7413.27 7424.77 8264.33
8   7002.38 7511.45 7519.84 8245.55
9   7075.04 7597.66 7599.81 8229.94
10  7127.82 7645.51 7649.2  8212.87
11  7171.94 7695.83 7701.47 8211.97
12  7193.82 7727.9  7730.65 8199.28
13  7229.76 7756.09 7762.58 8185.75
14  7252.7  7783.35 7780.36 8193.05
15  7259.97 7793    7789.77 8179.77
16  7278.49 7803.85 7799.26 8177.83
17  7288.07 7814.82 7799.02 8169.31
18  7294.38 7812.15 7810.12 8169.02
19  7295.29 7821.13 7816.68 8177.07
20  7296.58 7826.55 7816.37 8165.72
21  7304.65 7829.22 7823.11 8169.64
22  7305.38 7821.46 7819.89 8167.32
23  7306.26 7828.97 7820.36 8162.02
24  7306.38 7830.81 7823.79 8162.48
25  7300.14 7824.72 7827.94 8165.42
26  7296.08 7821.81 7816.32 8156.62
27  7301.82 7826.25 7816.6  8160.69
28  7295.56 7829.91 7822.93 8153.53
29  7295.81 7827.21 7809.87 8156.48
30  7299.35 7833.84 7821.41 8162.64
31  7300.85 7834.91 7819.91 8156.94

问题原因

  1. 延迟求值问题:循环中用eval(substitute())生成ggplot对象时,ggplot不会立即绑定well对应的列数据,而是在调用列表时才读取well的当前值——循环结束后well指向最后一列,导致所有图显示重复内容。
  2. 数据处理遗漏:Data %>% mutate_if(is.numeric, funs(.-first(.)))未赋值回Data,等于没有执行数据中心化操作。
  3. 列表索引错误:plotList[[i]]会导致列表前1个元素为空,影响后续展示。

修正代码

推荐方案:用列名明确绑定数据(彻底解决延迟求值)

library(ggplot2)
library(dplyr)
library(ggpubr)
rm(list = ls())

# 选择数据(csv格式,第一列为循环数)
filename <- file.choose()
Data <- read.csv(filename)

# 修正:执行数据中心化并赋值回Data
Data <- Data %>% mutate_if(is.numeric, ~ . - first(.))

# 筛选第6到25个循环的数据
Data <- Data[6:25, ]
yy <- ncol(Data)
plotList <- list()

for (i in seq(from = 2, to = yy, by = 1)) {
  well_col <- colnames(Data)[i]
  # 动态构建回归公式
  lm_fit <- lm(reformulate("Cycle", response = well_col), data = Data)
  Slope <- coef(lm_fit)[2]
  # 保存回归结果
  WellData <- data.frame(Well = i - 1, Slope = Slope)
  Results <- if (exists("Results")) rbind(Results, WellData) else WellData
  # 生成绘图对象:用.data[[well_col]]明确指定列,避免延迟求值
  P1 <- ggplot(Data, aes(x = Cycle, y = .data[[well_col]])) + 
    geom_point() + 
    geom_smooth(method = "lm") +
    ggtitle(paste("Plot", i)) # 增加标题区分不同图
  plotList[[i - 1]] <- P1 # 列表从索引1开始,避免空元素
  # 保存单图
  filename <- paste0("Plot", i, ".pdf")
  ggsave(filename = filename, plot = P1)
}

print(Results)
# 用ggarrange展示所有图,可指定行列数
ggarrange(plotlist = plotList, ncol = 4, nrow = 6)

备选方案:用本地环境捕获循环变量

如果坚持使用eval(substitute()),可以在循环内创建本地环境捕获当前变量:

# 仅修改循环内绘图部分,其余代码同推荐方案
for (i in seq(from = 2, to = yy, by = 1)) {
  well <- Data[, i]
  lm_fit <- lm(well ~ Cycle, data = Data)
  Slope <- coef(lm_fit)[2]
  WellData <- data.frame(Well = i - 1, Slope = Slope)
  Results <- if (exists("Results")) rbind(Results, WellData) else WellData
  pltName <- paste('Plot', i, sep = '')
  
  # 创建本地环境捕获当前well值
  local_env <- environment()
  P1 <- eval(substitute(ggplot(Data, aes(Cycle, well)) + 
                          geom_point() + geom_smooth(method="lm")), local_env)
  plotList[[i - 1]] <- P1
  filename <- paste0(pltName, ".pdf")
  ggsave(filename = filename, plot = P1)
}

关键修正点

  1. 修复数据中心化的赋值问题,确保数据被正确处理。
  2. 调整列表索引为从1开始,避免空元素干扰。
  3. 用.data[[well_col]]明确引用列名,彻底解决ggplot延迟求值导致的重复图问题。
  4. 用reformulate动态构建回归公式,代码更简洁安全。

内容的提问来源于stack exchange,提问作者Lee Smith

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 03:52:01