我目前在使用R保存列表和"子列表"时遇到了一个奇怪的问题.标题可能不明确,但这是令我不安的问题:
给出一些数据(这里的数据完全是人为的,但问题不在于模型的相关性):
set.seed(1)
a0 = rnorm(10000,10,2)
b1 = rnorm(10000,10,2)
b2 = rnorm(10000,10,2)
b3 = rnorm(10000,10,2)
data = data.frame(a0,b1,b2,b3)
Run Code Online (Sandbox Code Playgroud)
并且函数返回复杂对象列表(比如说lm()对象):
test = function(k){
tt = vector('list',k)
for(i in 1:k) tt[[i]] = lm(a0~b1+b2+b3,data = data)
tt
}
Run Code Online (Sandbox Code Playgroud)
我们的测试函数返回一个lm()对象列表.让我们看看这个对象的大小:
ok = test(2)
object.size(ok)
> object.size(ok)
4019336 bytes
Run Code Online (Sandbox Code Playgroud)
让我们创建ok2一个完全相似的对象但不在函数内:
ok2 = vector('list',2)
ok2[[1]] = lm(a0~b1+b2+b3,data = data)
ok2[[2]] = lm(a0~b1+b2+b3,data = data)
Run Code Online (Sandbox Code Playgroud)
...并检查他的大小:
> object.size(ok2)
4019336 bytes
Run Code Online (Sandbox Code Playgroud)
这里我们是,ok并且ok2完全相同,所以告诉我们R.问题,如果我们将这些对象作为R对象保存在硬盘上(带save()或saveRDS()):
save(ok,file='ok.RData')
save(ok2,file='ok2.RData')
Run Code Online (Sandbox Code Playgroud)
它们在硬盘上的尺寸分别为:3 366 005 bytes和1 678 851 bytes.
ok比ok2它们完全相似时大2倍!
更奇怪的是,如果您保存对象的"子列表",可以说ok[[1]]和ok2[[1]](对象再次完全相同):
a = ok[[1]]
a2 = ok2[[1]]
save(a,file='console/a.RData')
save(a2,file='console/a2.RData')
Run Code Online (Sandbox Code Playgroud)
它们在硬盘上的尺寸分别为:2 523 284 bytes和838 977 bytes.
两件事:为什么尺寸a与a2硬盘上的尺寸不同?为什么尺寸ok与ok2硬盘驱动器的尺寸不同?为什么a它只是ok尺寸的一半2 523 284 bytes而HD ok尺寸 3 366 005 bytes呢?
我错过了什么吗?
ps:我在Windows 7 32位下使用R 2.15.1,2.15.2,2.15.3,3.0.0以及debian和R 2.15.1,R 2.15.2运行此测试.我每次都遇到这个问题.
编辑
thx到@ user1609452,这是一个似乎有效的小技巧:
test2 = function(k){
tt = vector('list',k)
for(i in 1:k){
tt[[i]] = lm(a0~b1+b2+b3,data = data)
attr(tt[[i]]$terms,".Environment") = .GlobalEnv
attr(attr(tt[[i]]$model,"terms"),".Environment") = .GlobalEnv
}
tt
}
Run Code Online (Sandbox Code Playgroud)
公式对象有自己的环境和很多东西.把它放到NULL.GlobalEnv或它似乎工作.像predict.lm()这样的函数仍然有效,我们保存的对象在HD上的大小合适.不知道为什么.
看着
> attr(ok[[1]]$terms,".Environment")
<environment: 0x9bcf3f8>
> attr(ok2[[1]]$terms,".Environment")
<environment: R_GlobalEnv>
Run Code Online (Sandbox Code Playgroud)
也
> ls(envir = attr(ok[[1]]$terms,".Environment"))
[1] "i" "k" "tt"
Run Code Online (Sandbox Code Playgroud)
所以用ok它拖动函数的环境.
还看了 ?object.size
The calculation is of the size of the object, and excludes the
space needed to store its name in the symbol table.
Associated space (e.g. the environment of a function and what the
pointer in a ‘EXTPTRSXP’ points to) is not included in the
calculation.
Run Code Online (Sandbox Code Playgroud)
例如,定义a test2和aok3
test2 = function(k){
tt = vector('list',k)
for(i in 1:k) tt[[i]] = lm(a0~b1+b2+b3,data = data)
rr = tt
tt
}
ok3 <- test2(2)
save(ok3, 'ok3.RdData')
> file.info('ok3.RData')$size
[1] 5043933
> file.info('ok.RData')$size
[1] 3366005
> file.info('ok2.RData')$size
[1] 1678851
> ls(envir = attr(ok3[[1]]$terms,".Environment"))
[1] "i" "k" "rr" "tt"
Run Code Online (Sandbox Code Playgroud)
所以ok大约是两倍大,ok2因为它有额外的tt和ok3的三倍大,因为它具有tt和rr
> c(object.size(ok),object.size(ok2),object.size(ok3))
[1] 4019336 4019336 4019336
Run Code Online (Sandbox Code Playgroud)
有相关讨论在这里