2015-08-13 88 views
10

我的问题类似于this one;当我在循环中生成绘图对象(在这种情况下是直方图)时,似乎它们都被最近的绘图覆盖。将ggplot对象存储在循环中的列表中R

要在循环内进行调试,我打印索引和生成的图,两者都显示正确。但是当我查看存储在列表中的图表时,它们都是相同的,但标签的除外。

(我使用的multiplot使合成图像,但你如果你print (myplots[[1]]) 通过print(myplots[[4]])一次一个相同的结果。)

因为我已经有一个附加的数据框(不像的海报类似的问题),我不知道如何解决这个问题。

(顺便说一句,列类是在原有的数据集,我在这里近似的因素,但如果是整数发生同样的问题)

这里是一个重复的例子:

library(ggplot2) 
source("http://peterhaschke.com/Code/multiplot.R") #load multiplot function 

#make sample data 
col1 <- c(2, 4, 1, 2, 5, 1, 2, 0, 1, 4, 4, 3, 5, 2, 4, 3, 3, 6, 5, 3, 6, 4, 3, 4, 4, 3, 4, 
      2, 4, 3, 3, 5, 3, 5, 5, 0, 0, 3, 3, 6, 5, 4, 4, 1, 3, 3, 2, 0, 5, 3, 6, 6, 2, 3, 
      3, 1, 5, 3, 4, 6) 
col2 <- c(2, 4, 4, 0, 4, 4, 4, 4, 1, 4, 4, 3, 5, 0, 4, 5, 3, 6, 5, 3, 6, 4, 4, 2, 4, 4, 4, 
      1, 1, 2, 2, 3, 3, 5, 0, 3, 4, 2, 4, 5, 5, 4, 4, 2, 3, 5, 2, 6, 5, 2, 4, 6, 3, 3, 
      3, 1, 4, 3, 5, 4) 
col3 <- c(2, 5, 4, 1, 4, 2, 3, 0, 1, 3, 4, 2, 5, 1, 4, 3, 4, 6, 3, 4, 6, 4, 1, 3, 5, 4, 3, 
      2, 1, 3, 2, 2, 2, 4, 0, 1, 4, 4, 3, 5, 3, 2, 5, 2, 3, 3, 4, 2, 4, 2, 4, 5, 1, 3, 
      3, 3, 4, 3, 5, 4) 
col4 <- c(2, 5, 2, 1, 4, 1, 3, 4, 1, 3, 5, 2, 4, 3, 5, 3, 4, 6, 3, 4, 6, 4, 3, 2, 5, 5, 4, 
      2, 3, 2, 2, 3, 3, 4, 0, 1, 4, 3, 3, 5, 4, 4, 4, 3, 3, 5, 4, 3, 5, 3, 6, 6, 4, 2, 
      3, 3, 4, 4, 4, 6) 
data2 <- data.frame(col1,col2,col3,col4) 
data2[,1:4] <- lapply(data2[,1:4], as.factor) 
colnames(data2)<- c("A","B","C", "D") 

#generate plots 
myplots <- list() # new empty list 
for (i in 1:4) { 
    p1 <- ggplot(data=data.frame(data2),aes(x=data2[ ,i]))+ 
    geom_histogram(fill="lightgreen") + 
    xlab(colnames(data2)[ i]) 
    print(i) 
    print(p1) 
    myplots[[i]] <- p1 # add each plot into plot list 
} 
multiplot(plotlist = myplots, cols = 4) 

当我看在情节列表中的剧情对象的总结,这是我看到

> summary(myplots[[1]]) 
data: A, B, C, D [60x4] 
mapping: x = data2[, i] 
faceting: facet_null() 
----------------------------------- 
geom_histogram: fill = lightgreen 
stat_bin: 
position_stack: (width = NULL, height = NULL) 

我认为mapping: x = data2[, i]的问题,但我难倒!我无法发布图片,因此如果我对问题的解释令人困惑,则需要运行我的示例并查看图表。

谢谢!

回答

22

除了其他优秀的答案,这里是一个解决方案,使用“正常”的评价而不是eval。由于for循环没有单独的变量范围(即它们在当前环境中执行),我们需要使用local来包装for块;此外,我们需要i一个局部变量 - 我们可以通过简单地重新分配它:

myplots <- list() # new empty list 
for (i in 1:4) 
    local({ 
     i <- i 
     p1 <- ggplot(data=data.frame(data2),aes(x=data2[ ,i]))+ 
      geom_histogram(fill="lightgreen") + 
      xlab(colnames(data2)[ i]) 
     print(i) 
     print(p1) 
     myplots[[i]] <<- p1 # add each plot into plot list 
    }) 

然而,一个完全清洁的方式是完全放弃了for回路,并使用列表功能来构建结果。这有几种可能的方式。以下是我认为最简单的:

plot_data_column = function (data, column) 
    ggplot(data = data2, aes_string(x = column)) + 
     geom_histogram(fill = "lightgreen") + 
     xlab(column) 

myplots <- lapply(colnames(data2), plot_data_column, data = data2) 

这有许多优点:它的简单,不会弄乱环境(与循环变量i)。

+0

功能不错的想法 – jenesaisquoi

+0

非常感谢你,特别是对于lapply版本;我想将其功能化,但无法弄清楚,并决定为循环做(表面上更容易,实际上是可怕的)。我认为这是一个可变范围问题,我经常在R中与他们战斗! – LizPS

2

由于所有传入的表达式的引用,在循环结束时评估的i是无论i碰巧在那个时候,这是它的最终值。在每次迭代期间,您都可以通过正确的值来解决这个问题。

myplots <- list() # new empty list 
for (i in 1:4) { 
    p1 <- eval(substitute(
     ggplot(data=data.frame(data2),aes(x=data2[ ,i]))+ 
      geom_histogram(fill="lightgreen") + 
      xlab(colnames(data2)[ i]) 
    ,list(i = i))) 
    print(i) 
    print(p1) 
    myplots[[i]] <- p1 # add each plot into plot list 
} 
multiplot(plotlist = myplots, cols = 4) 
+0

诊断正确,但解决方案有些复杂。在本地环境中捕捉'i'更容易。问题是R中的'for'循环没有作用域,所以你需要使用'local'来代替:'for(in in 1:4)local({i = i; ... rest of the loop ...})''。自我分配'我=我'不是偶然 - 这实际上是需要的。还可以使用不同的变量名称。无论如何,通过使用“适当的”列表函数而不是'for',这一切都是不必要的,这在坦克里是一个糟糕的语言结构。 –

+0

@KonradRudolph'local'很好 – jenesaisquoi

+0

啊,我忘记了一些东西:如果'local'是使用时,对'myplots [[i]]的赋值需要使用'<< - '运算符而不是本地赋值。 –