2

我正在使用 afor loop将 ggplots 分配给 a list,然后将其传递给plot_grid()(package cowplot)。plot_grid将多个 ggplot 并排放置在一个图中。这手动工作正常,但是当我使用 a 时for loop,生成的最后一个图会在图中的每个子帧中重复(如下所示)。换句话说,所有子帧都显示相同的 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)

预期数字:

在此处输入图像描述

图来自for loop

在此处输入图像描述

如this question中所述,我尝试将列表元素转换为grobs,如下所示:

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

但我得到了同样的结果。

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

4

4 回答 4

5

到目前为止的答案非常接近,但在我看来并不令人满意。问题如下 - 在您的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在绘图之前实际上并不评估这些表达式,并且由于这些都在全局环境中,并且 is 的值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"
于 2016-09-30T22:18:23.017 回答
3

我认为这里的问题是该方法的非标准评估会aes延迟评估i,直到实际绘制该图。到绘图时,i是最后一个值(在玩具示例中为“B”),因此y所有绘图的美学映射都是指最后一个值。同时,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)
于 2016-09-30T21:22:22.787 回答
3

在 [this answer]( https://stackoverflow.com/a/26246791/2461552

我通常切换到aes_stringaes_用于这样的情况,因此我可以在 ggplot2 中将变量用作字符串。

在您的情况下,我发现lapply循环比循环更容易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)
于 2016-09-30T21:14:25.693 回答
2

我认为即使您实际上是在动态定义它们,在内部ggplot寻找您的xy变量也会感到困惑。dfrm如果您for稍微更改循环以构建一个新的子data.frame作为第一行,它就可以正常工作。

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)

在此处输入图像描述

于 2016-09-30T21:07:29.153 回答