我在保存环境时遇到了我不明白的行为.下面的代码演示了这个问题.我原本期望这两个文件(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.RData
和right-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
这不是我的专业领域,但我相信环境的工作是这样的.
在您的情况下,上述结果是:
fn()
它会创建自己的本地环境(绿色),其父级默认为globalenv()
(灰色).test
在fn()
其父级内部创建环境(红色)时,默认为fn()
环境(绿色).test
因此将包括该对象a
.test1
(蓝色)并明确声明其父级是globalenv()
它与fn()
环境分离并且不继承该对象时a
.因此,在保存test
时还可以保存对象的(有点隐藏)副本a
.保存时不会发生这种情况,test1
因为它不包含对象a
.
显然这是一个比我以前认为的更复杂的话题.虽然我现在可能只是引用@ joris-mays的答案,但我想最后去做.
对我来说,最直观的环境可视化将是一个树结构,见下文,其中每个节点都是一个环境,箭头指向它各自的封闭环境(我想相信它与它的父环境相同,但必须做框架,超出我的角落).给定的环境包围,你可以通过向下移动的树到达所有的对象,它可以访问您可以通过移动了树到达的所有对象.当您保存环境时,它会显示您保存所有对象和环境,这些对象和环境都被它包围并可从中访问(除了globalenv()
).
但是,带回家的消息就像Joris已经说过的那样:将对象保存为列表,您无需担心.
如果你想了解更多,我可以推荐Norman Matloff的优秀书籍R编程艺术.它的目标是R中的软件开发而不是主要数据分析,并假设您有相当多的编程经验.我必须承认我尚未完全消化环境部分,但由于本书的其余部分写得很好而且教学方法我认为这个也是.
实际上,与@Backlin相比,它是另一种方式:父环境是包围其他环境的环境.所以在你定义的情况下,封闭环境test
是本地环境fn
,而封闭环境test1
是全局环境,如下所示:
环境的行为与R中的其他对象不同,因为它们在传递给函数或在赋值中使用时不会被复制.环境对象本身包含指向以下内容的指针:
环境包含指针的事实使得一切都不同.环境并不容易处理,它们实际上非常棘手.看看下面的代码:
> test <- new.env()
> test$a <- 1
> test2 <- test
> test2$a <- 2
> test$a
[1] 2
Run Code Online (Sandbox Code Playgroud)
所以,如果你复制的唯一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()
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)