保存时,空R环境变为大文件

时间:2012-12-17 11:02:18

标签: r

我在保存环境时遇到了我不明白的行为。下面的代码演示了这个问题。我原本期望这两个文件(far-too-big.RDataright-size.RData)大小相同,而且非常小,因为它们包含的环境是空的。

事实上,far-too-big.RData的结尾与bigfile.RData相同。

我在WinXP 5.1 SP3上使用2.14.1和2.15.2得到了相同的结果。任何人都可以解释为什么会这样吗?

加载到新的R会话时,far-too-big.RDataright-size.RData似乎都不包含任何内容。即他们返回character(0)以回应ls()。但是,如果我将保存切换为包含ascii=TRUE,并在文本编辑器中打开结果,我可以看到far-too-big.RData包含bigfile.RData中的数据。

a <- matrix(runif(1000000, 0, 1), ncol=1000)
save(a, file="bigfile.RData")
fn <- function() {
    load("bigfile.RData")
    test <- new.env()
    save(test, file="far-too-big.RData")
    test1 <- new.env(parent=globalenv())
    save(test1, file="right-size.RData")
}
fn()

2 个答案:

答案 0 :(得分:15)

这不是我的专业领域,但我相信环境是这样的。

  • 任何环境都会在其父环境中继承所有内容。
  • 所有函数调用都会创建自己的环境。

上述情况的结果是:

  1. 当您运行fn()时,它会创建自己的本地环境(绿色),其父级默认为globalenv()(灰色)。
  2. test内创建环境fn()(红色)时,其父级默认为fn()的环境(绿色)。因此test将包含对象a
  3. 当您创建环境test1(蓝色)并明确声明其父级为globalenv()时,它与fn()的环境分离,并且不会继承对象{{1} }。
  4. 因此,在保存a时,您还会保存对象test的(有点隐藏)副本。保存a时不会发生这种情况,因为它不包含对象test1

    enter image description here

    更新

    显然这是一个比我过去相信的更复杂的话题。虽然我现在可能只是引用@ joris-mays的答案,但我想最后去做。

    对我来说,最直观的环境可视化将是一个树形结构,见下文,其中每个节点都是一个环境,箭头指向其各自的封闭环境(我希望它与其父级相同,但是这与框架有关,超出了我的角落)。给定的环境通过向下移动树来包含您可以到达的所有对象,它可以访问通过向上移动树可以到达的所有对象。保存环境时,您会看到保存所有对象和环境的所有对象和环境,并且可以从中进行访问(a除外)。

    但是,带回家的消息就像Joris已经说过的那样:将对象保存为列表,您无需担心。

    enter image description here

    如果你想了解更多,我可以推荐Norman Matloff的优秀书the art of R programming。它的目标是R中的软件开发而不是主要数据分析,并假设您有相当多的编程经验。我必须承认我还没有完全消化环境部分,但是由于本书的其余部分写得非常好并且教学方法我认为这个也是。

答案 1 :(得分:8)

实际上,与@Backlin相反,它的另一种方式是:父环境是包围其他环境的环境。因此,在您定义的情况下,test的封闭环境是fn的本地环境,test1的封闭环境是全局环境,如下所示:

enter image description here

环境的行为与R中的其他对象不同,因为它们在传递给函数或在赋值中使用时不会被复制。环境对象本身由指针内部组成:

  • 一个框架(包含值的pairlist)
  • 封闭环境(如上所述)
  • 哈希表(如果没有哈希,则为列表或NULL)

环境包含指针这一事实会产生重大影响。环境并不容易处理,它们实际上非常棘手。看看下面的代码:

> test <- new.env()
> test$a <- 1
> test2 <- test
> test2$a <- 2
> test$a
[1] 2

因此,您在test中从test2复制的唯一内容就是指针。如果您更改test2中的值,则也会在test中更改该值。 (实际上,您只更改了一次该值,但testtest2指向同一帧。)

当您尝试保存环境时,R别无选择,只能获取框架,哈希表和封闭环境的值并保存它们。由于封闭环境本身就是一个环境,因此R还将保存所有封闭环境,直到它到达全局环境。由于全局环境在内部代码中以特殊方式处理,因此(幸运的是)没有保存在文件中。

请注意封闭环境与父框架之间的区别: 假设我们将函数定义有点不同:

a <- matrix(runif(1000000, 0, 1), ncol=1000)
save(a, file="bigfile.RData")
fn <- function() {
    load("bigfile.RData")
    test <- new.env()
    save(test, file="far-too-big.RData")
    test1 <- new.env(parent=globalenv())
    save(test1, file="right-size.RData")
}

fn2 <- function(){
    z <- matrix(runif(1000000,0,1),ncol=1000)
    fn()
}
fn2()

现在我们有以下情况:

enter image description here

有人会认为该文件太远了.RData&#34;包含矩阵a和矩阵z,但情况并非如此。它只包含矩阵a。这是因为fn的封闭环境是全球环境。 fn的父框架fn2的环境,但fn创建的环境对象包含指向全局环境的指针。

另一方面,如果我们执行以下操作:

fn <- function() {
    load("bigfile.RData")
    test <- new.env()
    test$b <- a
    test2 <- new.env(parent=test)
    save(test2, file="far-too-big.RData")
}

test2现在包含在两个环境中(testfun的环境),两个环境也都保存在文件中。所以你得到这种情况:

enter image description here

无论如何,我个人都避免将环境保存为环境,因为有更多的事情可能出错。在我看来,将环境保存为列表在99.9%的情况下是更好的选择:

fn2 <- function(){
    load("bigfile.RData")
    test <- new.env()
    test$x <- "something"
    test$fn <- ls
    testlist <- as.list(test)
    save(testlist, file="right-size.RData")
}
fn2()

如果您需要它作为环境,您可以在加载时将其转换回来。

load("right-size.RData")
test <- as.environment(testlist)