我在保存环境时遇到了我不明白的行为。下面的代码演示了这个问题。我原本期望这两个文件(far-too-big.RData
和right-size.RData
)大小相同,而且非常小,因为它们包含的环境是空的。
事实上,far-too-big.RData
的结尾与bigfile.RData
相同。
我在WinXP 5.1 SP3上使用2.14.1和2.15.2得到了相同的结果。任何人都可以解释为什么会这样吗?
加载到新的R会话时,far-too-big.RData
和right-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()
答案 0 :(得分:15)
这不是我的专业领域,但我相信环境是这样的。
上述情况的结果是:
fn()
时,它会创建自己的本地环境(绿色),其父级默认为globalenv()
(灰色)。test
内创建环境fn()
(红色)时,其父级默认为fn()
的环境(绿色)。因此test
将包含对象a
。test1
(蓝色)并明确声明其父级为globalenv()
时,它与fn()
的环境分离,并且不会继承对象{{1} }。因此,在保存a
时,您还会保存对象test
的(有点隐藏)副本。保存a
时不会发生这种情况,因为它不包含对象test1
。
显然这是一个比我过去相信的更复杂的话题。虽然我现在可能只是引用@ joris-mays的答案,但我想最后去做。
对我来说,最直观的环境可视化将是一个树形结构,见下文,其中每个节点都是一个环境,箭头指向其各自的封闭环境(我希望它与其父级相同,但是这与框架有关,超出了我的角落)。给定的环境通过向下移动树来包含您可以到达的所有对象,它可以访问通过向上移动树可以到达的所有对象。保存环境时,您会看到保存所有对象和环境的所有对象和环境,并且可以从中进行访问(a
除外)。
但是,带回家的消息就像Joris已经说过的那样:将对象保存为列表,您无需担心。
如果你想了解更多,我可以推荐Norman Matloff的优秀书the art of R programming。它的目标是R中的软件开发而不是主要数据分析,并假设您有相当多的编程经验。我必须承认我还没有完全消化环境部分,但是由于本书的其余部分写得非常好并且教学方法我认为这个也是。
答案 1 :(得分:8)
实际上,与@Backlin相反,它的另一种方式是:父环境是包围其他环境的环境。因此,在您定义的情况下,test
的封闭环境是fn
的本地环境,test1
的封闭环境是全局环境,如下所示:
环境的行为与R中的其他对象不同,因为它们在传递给函数或在赋值中使用时不会被复制。环境对象本身由指针内部组成:
环境包含指针这一事实会产生重大影响。环境并不容易处理,它们实际上非常棘手。看看下面的代码:
> test <- new.env()
> test$a <- 1
> test2 <- test
> test2$a <- 2
> test$a
[1] 2
因此,您在test
中从test2
复制的唯一内容就是指针。如果您更改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()
现在我们有以下情况:
有人会认为该文件太远了.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
现在包含在两个环境中(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()
如果您需要它作为环境,您可以在加载时将其转换回来。
load("right-size.RData")
test <- as.environment(testlist)