从循环中生成 ggplots(并生成文件)而不在 RMarkdown 中打印任何可见输出
Producing ggplots from a loop (and generating the files) without printing any visible output in RMarkdown
我正在构建一个混合了数字、文本和绘图的 table。我用 ggplot 构建了我的绘图,然后将它们添加到 table(请参见下面的代码)。因为我(最终)会有很多情节,所以我需要使用循环来高效地创建它们。但是,因为 ggplot 似乎需要打印才能为每个图生成图像链接,所以我无法使用 invisible()
,随后得到讨厌的 '[1] [[2]] [[3]] ' 输出在下图的顶部。
如何在不打印 ggplot 的任何可见输出的情况下编译文档?
```{r score_table, fig.show = "hide", echo = FALSE, fig.height=.75, fig.width=2.5}
#Load libraries
library(knitr)
library(ggplot2)
#Item data
items <- data.frame(text = sapply(1:3, FUN = function(x){
paste0(sample(x = LETTERS, size = 60, replace = T), collapse = "")}))
#Score data
score_set = replicate(n = 3, expr = {data.frame(other = rep("other", 4),
score=sample(1:7,4,TRUE))}, simplify = F)
#Plot function
plotgen<-function(score_set,other,score){
p <- ggplot(score_set, aes(factor(other), score))
p + geom_violin(fill = "#99CCFF") + coord_flip() + scale_x_discrete(name=NULL) +
scale_y_continuous(breaks = round(seq(1, 7, by = 1),1), limits = c(1,7), name=NULL) +
theme(axis.text.y=element_blank(),axis.title.y=element_blank(),axis.ticks.y=elemen t_blank(),
panel.grid.major.y = element_line(colour = "black"),
panel.grid.minor = element_blank(),
panel.background = element_rect(fill = "white"),
panel.border = element_rect(colour = "black", fill=NA, size=1)) +
geom_hline(yintercept=sample(1:7,1,TRUE), size = 1.5, colour = "#334466")
}
#Generate plots
print(lapply(seq_along(score_set), FUN = function(x){plotgen(score_set[[x]],other,score)}))
out <- cbind(row.names(items), as.character(items$text), sprintf("",
opts_current$get("fig.path"), opts_current$get("label"), 1:nrow(items)))
#Build table
kable(out, col.names = c("ID", "Text", "Scores"))
```
lapply
returns 一个列表。当你 print
一个列表时,不管它的内容如何,它也会打印列表索引,[[1]]
,[[2]]
,[[3]]
,......如果你保存列表,
plot_list <- lapply(seq_along(score_set), FUN = function(x){plotgen(score_set[[x]],other,score)})
然后在列表中打印 每个图 而不是打印 整个列表 (我们可以将其包装在 invisible()
所以返回的列表不打印)
invisible(lapply(plot_list, print))
它不会打印列表的索引。因为您将单独打印每个图,而不是打印恰好包含图的列表。
在简单列表上进行演示:
x = list(1, 2, 3)
print(x)
# [[1]]
# [1] 1
#
# [[2]]
# [1] 2
#
# [[3]]
# [1] 3
invisible(lapply(x, print))
# [1] 1
# [1] 2
# [1] 3
另一种解决方案不需要 invisible
因为它 return
什么都没有,只是一个 for 循环:
for (i in seq_along(plot_list)) print(plot_list[[i]])
我会留给你看你喜欢哪个。
解决 for
循环会变慢的担忧:
p = ggplot(mtcars, aes(x = hp, y = mpg)) + geom_point()
plist = list(p, p)
library(microbenchmark)
microbenchmark(
forloop = {for (i in seq_along(plist)) print(plist[[i]])},
lapply = invisible(lapply(plist, print)),
times = 10L
)
# Unit: milliseconds
# expr min lq mean median uq max neval cld
# forloop 260.4532 271.2784 295.8415 276.1587 289.7507 402.1792 10 a
# lapply 258.8032 269.5915 296.2268 287.9524 294.8860 398.6803 10 a
相差几毫秒。
我正在构建一个混合了数字、文本和绘图的 table。我用 ggplot 构建了我的绘图,然后将它们添加到 table(请参见下面的代码)。因为我(最终)会有很多情节,所以我需要使用循环来高效地创建它们。但是,因为 ggplot 似乎需要打印才能为每个图生成图像链接,所以我无法使用 invisible()
,随后得到讨厌的 '[1] [[2]] [[3]] ' 输出在下图的顶部。
如何在不打印 ggplot 的任何可见输出的情况下编译文档?
```{r score_table, fig.show = "hide", echo = FALSE, fig.height=.75, fig.width=2.5}
#Load libraries
library(knitr)
library(ggplot2)
#Item data
items <- data.frame(text = sapply(1:3, FUN = function(x){
paste0(sample(x = LETTERS, size = 60, replace = T), collapse = "")}))
#Score data
score_set = replicate(n = 3, expr = {data.frame(other = rep("other", 4),
score=sample(1:7,4,TRUE))}, simplify = F)
#Plot function
plotgen<-function(score_set,other,score){
p <- ggplot(score_set, aes(factor(other), score))
p + geom_violin(fill = "#99CCFF") + coord_flip() + scale_x_discrete(name=NULL) +
scale_y_continuous(breaks = round(seq(1, 7, by = 1),1), limits = c(1,7), name=NULL) +
theme(axis.text.y=element_blank(),axis.title.y=element_blank(),axis.ticks.y=elemen t_blank(),
panel.grid.major.y = element_line(colour = "black"),
panel.grid.minor = element_blank(),
panel.background = element_rect(fill = "white"),
panel.border = element_rect(colour = "black", fill=NA, size=1)) +
geom_hline(yintercept=sample(1:7,1,TRUE), size = 1.5, colour = "#334466")
}
#Generate plots
print(lapply(seq_along(score_set), FUN = function(x){plotgen(score_set[[x]],other,score)}))
out <- cbind(row.names(items), as.character(items$text), sprintf("",
opts_current$get("fig.path"), opts_current$get("label"), 1:nrow(items)))
#Build table
kable(out, col.names = c("ID", "Text", "Scores"))
```
lapply
returns 一个列表。当你 print
一个列表时,不管它的内容如何,它也会打印列表索引,[[1]]
,[[2]]
,[[3]]
,......如果你保存列表,
plot_list <- lapply(seq_along(score_set), FUN = function(x){plotgen(score_set[[x]],other,score)})
然后在列表中打印 每个图 而不是打印 整个列表 (我们可以将其包装在 invisible()
所以返回的列表不打印)
invisible(lapply(plot_list, print))
它不会打印列表的索引。因为您将单独打印每个图,而不是打印恰好包含图的列表。
在简单列表上进行演示:
x = list(1, 2, 3)
print(x)
# [[1]]
# [1] 1
#
# [[2]]
# [1] 2
#
# [[3]]
# [1] 3
invisible(lapply(x, print))
# [1] 1
# [1] 2
# [1] 3
另一种解决方案不需要 invisible
因为它 return
什么都没有,只是一个 for 循环:
for (i in seq_along(plot_list)) print(plot_list[[i]])
我会留给你看你喜欢哪个。
解决 for
循环会变慢的担忧:
p = ggplot(mtcars, aes(x = hp, y = mpg)) + geom_point()
plist = list(p, p)
library(microbenchmark)
microbenchmark(
forloop = {for (i in seq_along(plist)) print(plist[[i]])},
lapply = invisible(lapply(plist, print)),
times = 10L
)
# Unit: milliseconds
# expr min lq mean median uq max neval cld
# forloop 260.4532 271.2784 295.8415 276.1587 289.7507 402.1792 10 a
# lapply 258.8032 269.5915 296.2268 287.9524 294.8860 398.6803 10 a
相差几毫秒。