【问题标题】:Producing ggplots from a loop (and generating the files) without printing any visible output in RMarkdown从循环中生成 ggplots(并生成文件)而不在 RMarkdown 中打印任何可见的输出
【发布时间】:2016-09-08 23:52:12
【问题描述】:

我正在构建一个混合了数字、文本和图表的表格。我用 ggplot 构建了我的图,然后将它们添加到表中(请参见下面的代码)。因为我(最终)会有很多图,所以我需要使用循环来有效地创建它们。但是,因为 ggplot 似乎需要打印才能为每个图生成图像链接,所以我无法使用invisible(),随后在下图的顶部。

如何在不从 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("![](%s%s-%s.png)", 
       opts_current$get("fig.path"), opts_current$get("label"), 1:nrow(items)))

#Build table
kable(out, col.names = c("ID", "Text", "Scores"))
```

【问题讨论】:

  • 将您的代码分成两块,第一块包含除kableinclude=FALSE 选项之外的所有内容,第二块仅包含kable。或使用purrr::walk

标签: r ggplot2 knitr r-markdown


【解决方案1】:

lapply 返回一个列表。当您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

相差几毫秒。

【讨论】:

  • 出色地使用了 for 循环。它有点慢,但会产生所需的输出。有时我会陷入 (l/s)apply 心态。这是一个很好的提醒。谢谢!
  • 如果 for 循环慢了几微秒以上,我会感到震惊。 lapplyfor 循环更快是一个常见的误解。但是,它通常更容易编写。
  • 好吧,我很惊讶。中值差异高达 12 毫秒。
  • 非常聪明的解决方案。关于性能:在我的系统上基本上没有速度差异,可能您的差异是测量工件。重复 100 次后,我得到的差异只有 2.4 毫秒。
猜你喜欢
  • 1970-01-01
  • 2018-06-15
  • 2023-03-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多