2011-04-14 27 views
3

我遇到了一個相當惱人的問題,打開設備列表,試圖構造一個函數,爲列表保存大量圖形。假設我們有以下數據:瞭解如何讀取設備列表

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 <- 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 

因此很明顯,它從下往上列表中再次關閉它符合一切。所以如果有一個圖形窗口打開,那就是下一個「當前」設備。這意味着第二個最後打開的pdf連接將不會被dev.off()函數關閉,因爲在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)輕鬆清理。

回答

6

如何使幫助功能做一個情節:

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 
} 
+0

好的建議,我沒有想到。看起來比我的解決方案更清潔。 – 2011-04-14 15:47:09

+0

+1迄今爲止提供的最好的解決方案和最乾淨的解決方案,現有設備的最小副作用(無)! – 2011-04-14 15:50:54

+3

非常狡猾。最好將'1:length(x)'改爲'seq_along(x)',以避免'x'長度爲0的問題。 – 2011-04-14 16:03:59

1

一個極端的解決方案可能是使用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()一個電話後激活一個,這將是屏幕上的設備在你所描述的設置,因此行爲報告。

+0

THX的建議。我不明白爲什麼'lapply'的結果應該分配給'on.exit'調用中的'foo' – 2011-04-14 15:46:44

+1

我不知道在'on.exit()'調用期間打印什麼內容時會發生什麼。我基本上這樣做是爲了停止在腳本結尾打印設備名稱。這就是它所做的一切,因爲一旦'on.exit()'完成,'foo'就會被丟棄。 – 2011-04-14 15:50:08