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

SJC*_*SJC 19 r

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

事实上,far-too-big.RData最终的大小与之相同bigfile.RData.

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

无论far-too-big.RDataright-size.RData,当加载到一个新的R会话,似乎包含什么.即他们character(0)回应ls().但是,如果我将保存切换为include 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()
Run Code Online (Sandbox Code Playgroud)

Bac*_*lin 15

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

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

在您的情况下,上述结果是:

  1. 运行时,fn()它会创建自己的本地环境(绿色),其父级默认为globalenv()(灰色).
  2. 当您testfn()其父级内部创建环境(红色)时,默认为fn()环境(绿色).test因此将包括该对象a.
  3. 当您创建环境test1(蓝色)并明确声明其父级是globalenv()它与fn()环境分离并且不继承该对象时a.

因此,在保存test时还可以保存对象的(有点隐藏)副本a.保存时不会发生这种情况,test1因为它不包含对象a.

在此输入图像描述

更新

显然这是一个比我以前认为的更复杂的话题.虽然我现在可能只是引用@ joris-mays的答案,但我想最后去做.

对我来说,最直观的环境可视化将是一个树结构,见下文,其中每个节点都是一个环境,箭头指向它各自的封闭环境(我想相信它与它的父环境相同,但必须做框架,超出我的角落).给定的环境包围,你可以通过向下移动的树到达所有的对象,它可以访问您可以通过移动了树到达的所有对象.当您保存环境时,它会显示您保存所有对象和环境,这些对象和环境都被它包围并可从中访问(除了globalenv()).

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

在此输入图像描述

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


Jor*_*eys 8

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

在此输入图像描述

环境的行为与R中的其他对象不同,因为它们在传递给函数或在赋值中使用时不会被复制.环境对象本身包含指向以下内容的指针:

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

环境包含指针的事实使得一切都不同.环境并不容易处理,它们实际上非常棘手.看看下面的代码:

> test <- new.env()
> test$a <- 1
> test2 <- test
> test2$a <- 2
> test$a
[1] 2
Run Code Online (Sandbox Code Playgroud)

所以,如果你复制的唯一testtest2,是指针.如果更改了值test2,则test也会更改该值.(实际上,您只更改该值一次,但test同时test2指向同一帧).

当您尝试保存环境时,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()
Run Code Online (Sandbox Code Playgroud)

现在我们有以下情况:

在此输入图像描述

人们会认为文件"far-too-big.RData"包含矩阵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")
}
Run Code Online (Sandbox Code Playgroud)

test2现在包含在两个环境(环境test和环境fun)中,并且两个环境也都保存在文件中.所以你得到这种情况:

在此输入图像描述

无论如何,我个人避免将环境保存为环境,因为有更多的东西可能出错.在我看来,将环境保存为列表是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()
Run Code Online (Sandbox Code Playgroud)

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

load("right-size.RData")
test <- as.environment(testlist)
Run Code Online (Sandbox Code Playgroud)