循环生成的ggplot存入列表后数据异常:所有图表y值一致
为什么循环保存的ggplot图表y数据全相同?
这个问题我太熟了!本质是ggplot的延迟求值特性在搞鬼,咱们一步步拆解:
问题根源
ggplot不会在你创建图表对象(P <- ggplot(...))的时候就把数据“固定”下来,它只会记录你指定的数据源和映射规则,等到实际要渲染图表(比如你调用marrangeGrob导出PDF的时候)才会去读取数据。
你在循环里每次给para赋值新的子集,但所有存在PLOT列表里的ggplot对象,都指向同一个外部的para变量。等循环结束后,para已经是最后一次循环(m=4)的子集了,所以所有图表在渲染时都会读取这最后一组数据,自然全变成一样的了。
而你在循环内逐个绘制没问题,是因为那时候para还是当前迭代的子集,渲染时读取的是当时的para数据。
解决办法(两种推荐方案)
方案1:遵循ggplot最佳实践,去掉aes里的para$前缀
ggplot会自动从你在ggplot()里指定的data参数(也就是这里的para)中查找列名,所以直接用列名就行,这样每个图表会绑定自己对应的para子集,不会依赖外部变量:
library(gridExtra) library(ggplot2) # 建议显式加载ggplot2,避免潜在问题 PLOT <- list() for (m in 1:4){ para <- subset(REG, Type == m) # 这里直接用列名,不要加para$前缀 P <- ggplot(para, aes(fill = Factor, y = Slope, x = Year)) P <- P + geom_bar(position = "dodge", stat = "identity") PLOT[[m]] <- P } pdf("variations.pdf") marrangeGrob(PLOT, nrow=2, ncol=2) dev.off()
方案2:用local()创建独立环境(适合不想改aes写法的情况)
如果不想修改aes里的写法,可以用local()把循环内的代码包裹起来,强制每个迭代都创建一个独立的环境,保存当前的para数据:
library(gridExtra) library(ggplot2) PLOT <- list() for (m in 1:4){ PLOT[[m]] <- local({ # 这里的para是当前迭代的局部变量,不会被后续循环覆盖 para <- subset(REG, Type == m) ggplot(para, aes(fill = para$Factor, y = para$Slope, x = para$Year)) + geom_bar(position = "dodge", stat = "identity") }) } pdf("variations.pdf") marrangeGrob(PLOT, nrow=2, ncol=2) dev.off()
两种方案都能解决问题,更推荐方案1,因为它符合ggplot的设计逻辑,代码也更简洁。
内容的提问来源于stack exchange,提问作者Mimosa
相关产品推荐
相关产品推荐

