【问题标题】:How can I create a 1000-page pdf faster?如何更快地创建 1000 页的 pdf?
【发布时间】:2015-05-23 08:45:59
【问题描述】:

我需要在 R 中使用 ggplot2 将 1000 多页绘制到 PDF 文件中。除了以下代码之外,还有更快的方法:

library(ggplot2)
data(diamonds)
pdf("name.pdf", width = 6, height = 6)
for(i in 1:1000) {
  p1 <- ggplot(diamonds, aes(x = carat,  y = price)) +
        geom_point()
  print(p1)
}
dev.off()

我的实际情况是这样的;

(1)需要读取一个文件,根据文件每一行的值创建一个data.frame

(2) 将该文件的每一行绘制为 pdf。

fa <- read.table(file)
pdf(name.pdf, width = 6, height = 4)
for(i in 1:nrow(fa)) {
  new.data <- function(i)
  p1 <- ggplot(new.data,...) + ...
  print(p1)
}
dev.off()

【问题讨论】:

  • 您只能在循环外调用一次 ggplot,然后重复调用 print,但不知何故,这似乎不太可能改善实际用例。 ggplot2 中的绘图函数与基础图形和晶格图形相比非常慢,因此您可能会考虑使用更快的绘图范例。
  • 同意,并且:根据我的经验,它是渲染图形,而不是构建它,这需要时间(所以即使在这种情况下,将 ggplot 移到循环之外也无济于事 - - 但这可以通过基准测试来测试)
  • move ggplot out 在我的情况下不起作用,不同的data frame 将在每个循环中传递给ggplot()。对我的问题还有其他建议吗?
  • 正如@BondedDust 所说:您对ggplot 的依恋程度如何?对于这个特定的例子plot(price~carat,data=diamonds)(来自base R)或lattice::xyplot(price~carat,data=diamonds)都可以正常工作。
  • @Ben Bolker,我很抱歉遗漏了我案件的细节。在我的帖子中只是一个例子。我的实际是:对于每个循环,我需要创建一个新的data frame,然后创建一个情节。

标签: r pdf ggplot2


【解决方案1】:

如上所述,速度是ggplot2 的弱点之一。这需要一些工作,但您通常可以在其他标准绘图包(base 或 lattice)之一中复制 ggplot 的外观;例如this series of blog posts 则相反(从 lattice 到 ggplot),但这些示例应该会有所帮助。 (@G.Grothendieck cmets 低于 library(latticeExtra); xyplot(y ~ x, diamonds, par.settings = ggplot2like(), lattice.options = ggplot2like.opts()) 将生成类似 ggplot 的图。)

如果您真的很绝望,我想您可以使用 parallel::parApply 生成合理数量的单独 PDF,然后使用 pdftk 等外部工具将它们拼接在一起...

设置机器以在所有三个系统中生成(大约)相同的图

 library("ggplot2")
 library("lattice")
 data(diamonds)
 gg_plot <- function() {
    cat(".")
    print(ggplot(diamonds, aes(x = carat,  y = price)) +
    geom_point())
 }
 base_plot <- function() {
    cat("+")
    plot(y~x,data=diamonds)
 }
 lattice_plot <- function() {
    cat("/")
    print(xyplot(y~x,data=diamonds))
 }
 wrap <- function(f,npages=20,fn="name.pdf") {
    pdf(fn, width = 6, height = 6) 
    for(i in 1:npages) {
           f()
    }
    dev.off()
    unlink(fn)
 }

 library("rbenchmark")
 benchmark(wrap(gg_plot),wrap(base_plot),wrap(lattice_plot),
           replications=10)

好的,这比我预期的要慢得多(我将其缩减为每个 PDF 20 页和 10 次复制)。 (我一开始以为lattice赢了很多,但那是因为我忘了print()结果...)

lattice 和 base 的速度都是 ggplot 的两倍...

                test replications elapsed relative user.self sys.self
2    wrap(base_plot)           10  75.693    1.249    74.053    1.596
1      wrap(gg_plot)           10 120.397    1.987   117.507    2.832
3 wrap(lattice_plot)           10  60.590    1.000    58.580    1.976

【讨论】:

  • 请注意,如果library(latticeExtra); xyplot(y ~ x, diamonds, par.settings = ggplot2like(), lattice.options = ggplot2like.opts()) 具有 ggplot2 的外观很重要,则可以使用它。
  • 示例中的变量不一致...克拉价格(ggplot)与x-y(基数和晶格)。
【解决方案2】:

感谢@Carl Witthoft 的建议,我将使用parallel + foreach 来完成我的任务。以下是示例,我正在尝试制作更简单的图。

以下是我的观点:将数据计算扔到parallel 并将plots 存储到list(可能非常大),最后将print 所有数字存储到PDF 文件中。

library("ggplot2")
library("lattice")
data(diamonds)
gg_plot <- function() {
  cat(".")
  for(i in 1:5) {
    fig <- ggplot(diamonds, aes(x = carat, y = price)) + geom_point()
    print(fig)
  }
}

para_plot <- function() {
  cat("+")
  library(foreach)
  library(doParallel)
  library(ggplot2)    
  cl <- makeCluster(2)
  registerDoParallel(cl, cores = 2)
  AllFigs <- list()
  cTime <- system.time(
    AllFigs <- foreach(i = 1:5, .packages = c("ggplot2")) %dopar% {
      fig <- ggplot(mtcars, aes(x = mpg, y = disp)) + geom_point()
      #fig <- ggplot(diamonds, aes(x = carat, y = price)) + geom_point()
      fig
    }
  )
  stopCluster(cl)    
  print(AllFigs)    
}

wrap <- function(f,npages=20,fn="name.pdf") {
  pdf(fn, width = 6, height = 6) 
  for(i in 1:npages) {
    f()
  }
  dev.off()
  unlink(fn)
}

library("rbenchmark")
benchmark(wrap(gg_plot), wrap(para_plot), replications=10)

是的,我认为parallel 比正常速度快两倍。但是,我认为它仍然需要改进。

test replications elapsed relative user.self sys.self user.child sys.child
1   wrap(gg_plot)           10 620.109    1.937   611.018    5.125      0.000     0.000
2 wrap(para_plot)           10 320.081    1.000   138.696    5.475      0.349     1.931

【讨论】:

  • 当您在 n 内核或机器上进行并行处理时,您能做的最好的事情就是对 n-fold 进行改进……也许这就是您所说的,我误解了。跨度>
  • 谢谢@BenBolker,是的。我只想在使用多核时使用parallel 进行更多改进。 (请原谅我没有更具体地描述我的问题,这是我在这里的第一篇文章)
猜你喜欢
  • 2013-04-17
  • 2011-10-12
  • 1970-01-01
  • 2023-01-09
  • 1970-01-01
  • 2015-12-29
  • 2021-04-25
  • 1970-01-01
  • 2012-07-19
相关资源
最近更新 更多