R将ggplot对象分配给循环列表
R assigning ggplot objects to list in loop
我正在使用 for loop
将 ggplots 分配给 list
,然后将其传递给 plot_grid()
(包 cowplot
)。 plot_grid
将多个 ggplots 并排放置在一个图中。这手动工作正常,但是当我使用 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
:
我尝试将列表元素转换为 grobs,如 中所述,如下所示:
mygrobs <- lapply(myplots, ggplotGrob)
plot_grid(plotlist=mygrobs)
但我得到了相同的结果。
我认为问题出在循环赋值上,而不是plot_grid()
,但我看不出我做错了什么。
我认为 ggplot
在 dfrm
中寻找 x
和 y
变量时感到困惑,即使您实际上是在动态定义它们。如果您稍微更改 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)
在 [this answer](.
中对 ggplot2 的惰性求值和 for 循环发生了什么有很好的解释
我通常会在这种情况下切换到 aes_string
或 aes_
,这样我就可以在 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)
我认为这里的问题是 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)
到目前为止的答案非常接近,但在我看来并不令人满意。问题如下 - 在 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
所以即使表达相同,结果也不同。
如果您想知道 stored/copied 对 lapply
的环境到底是什么 - 不出所料,它只是列名称:
ls(myplots[[1]]$plot_env)
#[1] "col"
我正在使用 for loop
将 ggplots 分配给 list
,然后将其传递给 plot_grid()
(包 cowplot
)。 plot_grid
将多个 ggplots 并排放置在一个图中。这手动工作正常,但是当我使用 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
:
我尝试将列表元素转换为 grobs,如
mygrobs <- lapply(myplots, ggplotGrob)
plot_grid(plotlist=mygrobs)
但我得到了相同的结果。
我认为问题出在循环赋值上,而不是plot_grid()
,但我看不出我做错了什么。
我认为 ggplot
在 dfrm
中寻找 x
和 y
变量时感到困惑,即使您实际上是在动态定义它们。如果您稍微更改 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)
在 [this answer](.
中对 ggplot2 的惰性求值和 for 循环发生了什么有很好的解释我通常会在这种情况下切换到 aes_string
或 aes_
,这样我就可以在 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)
我认为这里的问题是 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)
到目前为止的答案非常接近,但在我看来并不令人满意。问题如下 - 在 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
所以即使表达相同,结果也不同。
如果您想知道 stored/copied 对 lapply
的环境到底是什么 - 不出所料,它只是列名称:
ls(myplots[[1]]$plot_env)
#[1] "col"