2016-09-30 299 views
1

我使用for loop將ggplots分配給list,然後傳遞給plot_grid()(包cowplot)。 plot_grid將多個ggplots並排放置在一個圖中。這可以手動正常工作,但是當我使用for loop時,生成的最後一個圖在圖的每個子幀中重複(如下所示)。換句話說,所有子幀都顯示相同的ggplot。R分配ggplot對象到循環列表中

這裏是一個玩具例如:

require(cowplot) 

dfrm <- data.frame(A=1:10, B=10:1) 

v <- c("A","B") 
dfmsize <- nrow(dfrm) 
myplots <- vector("list",2) 

count = 1 
for(i in v){ 
    myplots[[count]] <- ggplot(dfrm, aes(x=1:dfmsize, y=dfrm[,i])) + geom_point() + labs(y=i) 
    count = count +1 
} 
plot_grid(plotlist=myplots) 

預期圖:

enter image description here

圖從for loop

enter image description here

我試圖轉換列表中的元素到grobs,如本question描述是這樣的:

mygrobs <- lapply(myplots, ggplotGrob) 
plot_grid(plotlist=mygrobs) 

,但我得到了同樣的結果。

我認爲問題在於循環分配,而不是plot_grid(),但我看不出我做錯了什麼。

+0

[這個答案](http://stackoverflow.com/a/26246791/2461552)詳細介紹了ggplot2懶惰評估的一些細節。 – aosmith

回答

2

答案至今都非常接近,但在我看來不令人滿意。問題是下面的 - 你for後循環:

myplots[[1]]$mapping 
#* x -> 1:dfmsize 
#* y -> dfrm[, i] 
myplots[[1]]$plot_env 
#<environment: R_GlobalEnv> 

myplots[[2]]$mapping 
#* x -> 1:dfmsize 
#* y -> dfrm[, i] 
myplots[[2]]$plot_env 
#<environment: R_GlobalEnv> 

i 
#[1] "B" 

至於其他的答案提到,ggplot實際上並沒有評估這些表情,直到陰謀,並因爲這些都是在全球環境,以及i值是"B",你會得到不希望的結果。

有避免這種問題的幾種方法,其中最簡單的事實上的簡化你的表情:

myplots = lapply(v, function(col) 
      ggplot(dfrm, aes(x=1:dfmsize, y=dfrm[,col])) + geom_point() + labs(y=col)) 

這部作品的原因,是因爲環境是每個在值的不同lapply循環:

myplots[[1]]$mapping 
#* x -> 1:dfmsize 
#* y -> dfrm[, col] 
myplots[[1]]$plot_env 
#<environment: 0x000000000bc27b58> 

myplots[[2]]$mapping 
#* x -> 1:dfmsize 
#* y -> dfrm[, col] 
myplots[[2]]$plot_env 
#<environment: 0x000000000af2ef40> 

eval(quote(dfrm[, col]), env = myplots[[1]]$plot_env) 
#[1] 1 2 3 4 5 6 7 8 9 10 
eval(quote(dfrm[, col]), env = myplots[[2]]$plot_env) 
#[1] 10 9 8 7 6 5 4 3 2 1 

因此,即使表達式是相同的,其結果是不同的。

而如果你想知道究竟是存儲/複製到的lapply環境 - 勿庸置疑,它只是列名:

ls(myplots[[1]]$plot_env) 
#[1] "col" 
+0

我將eddi標記爲最佳答案,因爲通過映射,然後在循環的每次迭代中顯示'plot_env',它都很清楚發生了什麼。 @jrandall:我沒有意識到aes會執行非標準的評估,正如你所提到的,這就是爲什麼使用'aes_q更好。榮譽給其他人提及'lapply'作爲循環的替代品。 – prefectionist

1

我覺得ggplot是越來越通過尋找內dfrmxy變量,即使你實際上是定義它們的飛行混淆。如果稍微更改for循環以構建新的子作爲第一行,那麼它工作得很好。

myplots <- list() 
count = 1 

for(i in v){ 
    df <- data.frame(x = 1:dfmsize, y = dfrm[,i]) 
    myplots[[count]] <- ggplot(df, aes(x=x, y=y)) + geom_point() + labs(y=i) 
    count = count + 1 
} 
plot_grid(plotlist=myplots) 

enter image description here

2

有什麼用GGPLOT2的懶惰評估情況,並在[這個答案](https://stackoverflow.com/a/26246791/2461552。循環一個很好的解釋

我通常切換到aes_stringaes_對於這樣的情況所以我可以在ggplot2中使用變量作爲字符串。

我發現lapply循環比y中的for循環更容易我們可以避免初始化列表和使用計數器的情況。

首先,我將x變量添加到數據集中。

dfrm$index = 1:nrow(dfrm) 

現在,lapply循環,通過循環列在v

myplots = lapply(v, function(x) { 
    ggplot(dfrm, aes_string(x = "index", y = x)) + 
     geom_point() + 
     labs(y = x) 
}) 

plot_grid(plotlist = myplots) 
3

我相信這裏的問題是,aes方法拖延評估i的非標準評價,直到該地塊實際繪製。在繪製時,i是最後一個值(在玩具示例「B」中),因此所有繪圖的美學貼圖都指最後一個值。同時,labs呼叫使用標準評估,因此標籤正確地指代循環中的每個迭代i

這個問題可以通過簡單地使用映射函數的標準評估版本,aes_q

require(cowplot) 

dfrm <- data.frame(A=1:10, B=10:1) 

v <- c("A","B") 
dfmsize <- nrow(dfrm) 
myplots <- vector("list",2) 

count = 1 
for(i in v){ 
    myplots[[count]] <- ggplot(dfrm, aes_q(x=1:dfmsize, y=dfrm[,i])) + geom_point() + labs(y=i) 
    count = count +1 
} 
plot_grid(plotlist=myplots) 
+0

我喜歡你明確提到NSE。它也可以通過在分配給列表之前實際打印循環圖來驗證,這實際上給出了正確的輸出(不像循環運行後打印)。 – jakub