【问题标题】:Understanding how the device list is read了解如何读取设备列表
【发布时间】:2011-04-14 15:02:24
【问题描述】:

我在打开设备列表时遇到了一个相当烦人的问题,我试图构建一个函数来保存列表中的多个图表。假设我们有以下数据:

Alist <- list(
  X1 = data.frame(X=rnorm(10),Y=1:10),
  X2 = data.frame(X=rnorm(10),Y=1:10),
  X3 = data.frame(X=rnorm(10),Y=1:10)
)

及以下功能:

myPlotFunc <- function(x,save=F){
    fnames <- paste(names(x),"pdf",sep=".")
    for(i in 1:length(x)){
      if(save){
        pdf(fnames[i])
        on.exit(dev.off(),add=T)
      }
      plot(x[[i]])
    }
    fnames
  }

如果我运行 fnames &lt;- myPlotFunc(Alist,save=T),一切都会正常运行,我会得到 3 个 pdf 文件名 X1.pdfX3.pdf。也就是说,如果没有打开图形窗口。如果有,则其中一个 pdf 不会关闭,并且所有后续绘图都将添加到 pdf 中,直到我在控制台中明确调用 dev.off()。像这样:

plot(Alist[[1]])
fnames <- myPlotFunc(Alist,save=T)
myPlotFunc(Alist,save=F)

> dev.list()
pdf 
  4 

如果我添加 on.exit({print(dev.cur());dev.off()},add=T) ,我会得到以下输出:

> fnames <- myPlotFunc(Alist,save=T)
pdf 
  5 
windows 
      2 
pdf 
  3 

显然它需要从下到上的列表再次关闭它遇到的所有内容。因此,如果有一个图形窗口打开,那就是下一个“当前”设备。这意味着 dev.off() 将不再关闭倒数第二个打开的 pdf 连接,因为在 on.exit 调用中会有一个短路。

我通过将函数更改为:

myPlotFunc <- function(x,save=F){
    fnames <- paste(names(x),"pdf",sep=".")
    devs <- NULL
    on.exit(for(i in devs) dev.off(i), add=T)
    for(i in 1:length(x)){
      if(save){
        pdf(fnames[i])
        devs <- c(devs,dev.cur())
      }
      plot(x[[i]])
    }
    fnames
  }

但这感觉相当尴尬。有什么我在这里遗漏的东西,或者有更好的方法来解决这个问题吗?

免责声明:

如果您不知道,请在运行第三个代码块后运行dev.off()。完成后运行unlink(fnames) 即可轻松清理。

【问题讨论】:

    标签: r graphics device


    【解决方案1】:

    让帮助功能做一个情节怎么样:

    myPlotFunc <- function(x, save=FALSE) {
        fnames <- paste(names(x), "pdf", sep=".")
        plot_one <- function(xx, fname, save=save) {
            if (save) {
                pdf(fname)
                on.exit(dev.off())
            }
            plot(xx)
        }
        for (i in 1:length(x)) plot_one(x[[i]], fnames[i], save)
    
        fnames
    }
    

    【讨论】:

    • 好建议,我没想到。似乎比我的解决方案更干净。
    • +1 迄今为止提供的最佳解决方案和最干净的解决方案,对现有设备的副作用最少(无)!
    • 非常狡猾。最好将 1:length(x) 更改为 seq_along(x) 以避免在 x 长度为 0 时出现问题。
    • @Richie 很好。我正在考虑使用mapply并将其更改为if (length(x)) mapply(plot_one, x, fnames, save) else warning("x has 0 length")
    【解决方案2】:

    一个激进的解决方案可能是使用graphics.off(),而不是尝试关闭脚本打开的设备。如果这只是用户代码,那么退出时是否关闭所有图形设备可能无关紧要?

    使用这种残酷的方法似乎有效:

    myPlotFunc <- function(x,save = FALSE) {
        fnames <- paste(names(x),"pdf",sep=".")
        if(save)
            on.exit(graphics.off(),add = TRUE)
        for(i in 1:length(x)) {
          if(save) {
            pdf(fnames[i])
          }
          plot(x[[i]])
        }
        fnames
      }
    

    另一种方法是在调用on.exit() 时列出所有设备,选择pdf 并关闭它们。该函数实现了这一点,并且似乎具有所需的行为。

    myPlotFunc2 <- function(x,save = FALSE) {
        fnames <- paste(names(x), "pdf", sep=".")
        if(save) {
            on.exit(foo <- lapply(dev.list()[grepl("pdf", names(dev.list()))], 
                                  dev.off),
                    add = TRUE)
        }
        for(i in 1:length(x)) {
          if(save) {
            pdf(fnames[i])
          }
          plot(x[[i]])
        }
        fnames
      }
    

    似乎编号最小的设备是 R 在调用 dev.off() 后激活的设备,这将是您描述的设置中的屏幕设备,因此是您报告的行为。

    【讨论】:

    • 感谢您的建议。我真的不明白为什么lapply 的结果应该在on.exit 调用中分配给foo
    • 我不知道如果在on.exit() 通话期间打印了某些内容会发生什么。我基本上这样做是为了阻止在脚本末尾打印关闭的设备名称。这就是它所做的一切,因为一旦on.exit() 完成,foo 就会被丢弃。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-11
    • 2012-04-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多