2016-09-30 124 views
1

我使用for loop将ggplots分配给list,然后传递给plot_grid()(包cowplot)。 plot_grid将多个ggplots并排放置在一个图中。这可以手动正常工作,但是当我使用for loop时,生成的最后一个图在图的每个子帧中重复(如下所示)。换句话说,所有子帧都显示相同的ggplot。R分配ggplot对象到循环列表中

这里是一个玩具例如:

require(cowplot) 

dfrm <- data.frame(A=1:10, B=10:1) 

v <- c("A","B") 
dfmsize <- nrow(dfrm) 
myplots <- vector("list",2) 

count = 1 
for(i in v){ 
    myplots[[count]] <- ggplot(dfrm, aes(x=1:dfmsize, y=dfrm[,i])) + geom_point() + labs(y=i) 
    count = count +1 
} 
plot_grid(plotlist=myplots) 

预期图:

enter image description here

图从for loop

enter image description here

我试图转换列表中的元素到grobs,如本question描述是这样的:

mygrobs <- lapply(myplots, ggplotGrob) 
plot_grid(plotlist=mygrobs) 

,但我得到了同样的结果。

我认为问题在于循环分配,而不是plot_grid(),但我看不出我做错了什么。

+0

[这个答案](http://stackoverflow.com/a/26246791/2461552)详细介绍了ggplot2懒惰评估的一些细节。 – aosmith

回答

2

答案至今都非常接近,但在我看来不令人满意。问题是下面的 - 你for后循环:

myplots[[1]]$mapping 
#* x -> 1:dfmsize 
#* y -> dfrm[, i] 
myplots[[1]]$plot_env 
#<environment: R_GlobalEnv> 

myplots[[2]]$mapping 
#* x -> 1:dfmsize 
#* y -> dfrm[, i] 
myplots[[2]]$plot_env 
#<environment: R_GlobalEnv> 

i 
#[1] "B" 

至于其他的答案提到,ggplot实际上并没有评估这些表情,直到阴谋,并因为这些都是在全球环境,以及i值是"B",你会得到不希望的结果。

有避免这种问题的几种方法,其中最简单的事实上的简化你的表情:

myplots = lapply(v, function(col) 
      ggplot(dfrm, aes(x=1:dfmsize, y=dfrm[,col])) + geom_point() + labs(y=col)) 

这部作品的原因,是因为环境是每个在值的不同lapply循环:

myplots[[1]]$mapping 
#* x -> 1:dfmsize 
#* y -> dfrm[, col] 
myplots[[1]]$plot_env 
#<environment: 0x000000000bc27b58> 

myplots[[2]]$mapping 
#* x -> 1:dfmsize 
#* y -> dfrm[, col] 
myplots[[2]]$plot_env 
#<environment: 0x000000000af2ef40> 

eval(quote(dfrm[, col]), env = myplots[[1]]$plot_env) 
#[1] 1 2 3 4 5 6 7 8 9 10 
eval(quote(dfrm[, col]), env = myplots[[2]]$plot_env) 
#[1] 10 9 8 7 6 5 4 3 2 1 

因此,即使表达式是相同的,其结果是不同的。

而如果你想知道究竟是存储/复制到的lapply环境 - 勿庸置疑,它只是列名:

ls(myplots[[1]]$plot_env) 
#[1] "col" 
+0

我将eddi标记为最佳答案,因为通过映射,然后在循环的每次迭代中显示'plot_env',它都很清楚发生了什么。 @jrandall:我没有意识到aes会执行非标准的评估,正如你所提到的,这就是为什么使用'aes_q更好。荣誉给其他人提及'lapply'作为循环的替代品。 – prefectionist

1

我觉得ggplot是越来越通过寻找内dfrmxy变量,即使你实际上是定义它们的飞行混淆。如果稍微更改for循环以构建新的子作为第一行,那么它工作得很好。

myplots <- list() 
count = 1 

for(i in v){ 
    df <- data.frame(x = 1:dfmsize, y = dfrm[,i]) 
    myplots[[count]] <- ggplot(df, aes(x=x, y=y)) + geom_point() + labs(y=i) 
    count = count + 1 
} 
plot_grid(plotlist=myplots) 

enter image description here

2

有什么用GGPLOT2的懒惰评估情况,并在[这个答案](https://stackoverflow.com/a/26246791/2461552。循环一个很好的解释

我通常切换到aes_stringaes_对于这样的情况所以我可以在ggplot2中使用变量作为字符串。

我发现lapply循环比y中的for循环更容易我们可以避免初始化列表和使用计数器的情况。

首先,我将x变量添加到数据集中。

dfrm$index = 1:nrow(dfrm) 

现在,lapply循环,通过循环列在v

myplots = lapply(v, function(x) { 
    ggplot(dfrm, aes_string(x = "index", y = x)) + 
     geom_point() + 
     labs(y = x) 
}) 

plot_grid(plotlist = myplots) 
3

我相信这里的问题是,aes方法拖延评估i的非标准评价,直到该地块实际绘制。在绘制时,i是最后一个值(在玩具示例“B”中),因此所有绘图的美学贴图都指最后一个值。同时,labs呼叫使用标准评估,因此标签正确地指代循环中的每个迭代i

这个问题可以通过简单地使用映射函数的标准评估版本,aes_q

require(cowplot) 

dfrm <- data.frame(A=1:10, B=10:1) 

v <- c("A","B") 
dfmsize <- nrow(dfrm) 
myplots <- vector("list",2) 

count = 1 
for(i in v){ 
    myplots[[count]] <- ggplot(dfrm, aes_q(x=1:dfmsize, y=dfrm[,i])) + geom_point() + labs(y=i) 
    count = count +1 
} 
plot_grid(plotlist=myplots) 
+0

我喜欢你明确提到NSE。它也可以通过在分配给列表之前实际打印循环图来验证,这实际上给出了正确的输出(不像循环运行后打印)。 – jakub