R中for循环保存ggplot到列表出现所有图被最后一张覆盖问题排查
问题原因
该问题由ggplot2的惰性求值机制导致:
aes()内的变量映射不会在创建ggplot对象时立刻求值,只会在图表实际渲染(打印/导出)时,才会查找映射用到的变量的当前值。- for循环中的变量
i属于全局环境变量,循环运行结束后i的最终值为3。当你后续查看列表中的所有图表时,所有图表的映射逻辑都会读取当前i=3的取值,因此所有图表都和最后一次循环的生成结果一致。 - 另外两个场景运行正常的原因:
- 普通对象赋值为立刻求值逻辑,不存在延迟查找,每次循环都会直接把当前取值存入列表。
lapply每次迭代都会创建独立的函数运行环境,迭代使用的i是该次函数调用的局部变量,会被绑定到对应ggplot的环境中,不会被后续迭代的i值覆盖。
可行修复方案
方案1:使用.data代词明确绑定数据框变量(官方推荐)
library(ggplot2) mtcars <- mtcars[, c('cyl', 'am', 'gear', 'mpg')] cols <- colnames(mtcars)[1:3] plots <- list() for (i in 1:3) { plots[[i]] <- ggplot( mtcars, aes(x = factor(.data[[cols[i]]]), y = mpg) ) + geom_point() }
方案2:在循环内使用局部环境绑定当前i值
library(ggplot2) mtcars <- mtcars[, c('cyl', 'am', 'gear', 'mpg')] plots <- list() for (i in 1:3) { plots[[i]] <- local({ i <- i ggplot(mtcars, aes(x = factor(mtcars[, i]), y = mtcars[, 'mpg'])) + geom_point() }) }
方案3:使用aes_string()传递字符变量名(兼容旧版本ggplot2)
library(ggplot2) mtcars <- mtcars[, c('cyl', 'am', 'gear', 'mpg')] cols <- colnames(mtcars)[1:3] plots <- list() for (i in 1:3) { plots[[i]] <- ggplot( mtcars, aes_string(x = paste0("factor(", cols[i], ")"), y = "mpg") ) + geom_point() }
内容的提问来源于stack exchange,提问作者Gopala
相关产品推荐
相关产品推荐

