R 通过引用分配到加载的包数据集中
我正在创建一个包,该包使用R 通过引用分配到加载的包数据集中,r,data.table,R,Data.table,我正在创建一个包,该包使用data.table作为数据集,并具有两个函数,这些函数使用:=通过引用进行分配 我构建了一个简单的包来演示我的问题 library(devtools) install_github('foo','mnel') 它包含两个函数 foo <- function(x){ x[, a := 1] } fooCall <- function(x){ eval(substitute(x[, a :=1]),parent.frame(1)) } 当我安
data.table
作为数据集,并具有两个函数,这些函数使用:=
通过引用进行分配
我构建了一个简单的包来演示我的问题
library(devtools)
install_github('foo','mnel')
它包含两个函数
foo <- function(x){
x[, a := 1]
}
fooCall <- function(x){
eval(substitute(x[, a :=1]),parent.frame(1))
}
当我安装这个软件包时,我的理解是foo(DT)
应该通过DT
中的引用进行分配
library(foo)
data(DT)
foo(DT)
b a
1: 1 1
2: 2 1
3: 3 1
4: 4 1
5: 5 1
# However this has not assigned by reference within `DT`
DT
b
1: 1
2: 2
3: 3
4: 4
5: 5
如果我使用更多正确的
tracmem(DT)
DT <- foo(DT)
# This works without copying
DT
b a
1: 1 1
2: 2 1
3: 3 1
4: 4 1
5: 5 1
untracemem(DT)
我应该坚持吗
DT这与数据集或锁定无关——只需使用
DT<-unserialize(serialize(data.table(b = 1:5),NULL))
foo(DT)
DT
另一种解决方案是使用inst/extdata
保存rda
文件(该文件将包含任意数量的data.table对象),并在data
子目录中有一个文件DT.r
# get the environment from the call to `data()`
env <- get('envir', parent.frame(1))
# load the data
load(system.file('extdata','DT.rda', package= 'foo'), envir = env)
# overallocate (evaluating in correct environment)
if(require(data.table)){
# the contents of `DT.rda` are known, so write out in full
evalq(alloc.col(DT), envir = env)
}
# clean up so `env` object not present in env environment after calling `data(DT)`
rm(list = c('env'), envir = env)
}
#从对`data()的调用中获取环境`
env从未尝试通过引用更新包中的数据!但是,如果数据包是密封的,那么数据包中的数据不应该是只读的吗?在这里末尾键入DT
并不意味着它是通过引用分配的,是吗?DT可能被复制到了.GlobalEnv
,也可能是它被更新的地方。顺便说一句,TraceM
报告了R本身的复制。不太可能捕捉到data.table所做的复制,例如第一次过度分配时,因为从技术上讲,这不是一个完美的复制,而是一个过度分配(虽然是浅拷贝而不是深拷贝)。也许可以尝试对包中的数据对象执行alloc.col
,看看会发生什么。@MatthewDowle我认为数据(DT)在全局环境中创建副本时,延迟加载可能意味着数据集被锁定。我不是在试图更新包中的副本,而是在示例/渐晕图中使用数据集。我不熟悉data()
,但这听起来不错。但是R正在创建它(不是data.table),也就是说,这是R的data()
命令,它不知道过度分配。与加载()data.table时类似,在第一个:=
添加新列之前,不会过度分配数据表。Doeslibrary(foo);数据(DT);alloc.col(DT);foo(DT)
工作?Matthew:但是请注意,alloc.col()
在函数内部同样不起作用(出于上述相同的原因)-您确实需要一些不试图伪造引用的东西-例如,如果需要通过引用将列添加到未序列化的data.table,那么有效的是DT,我认为这只是一个问题,在函数中,并且该表名事先未知(即需要通过函数参数传入)。我想不出一个例子,在非序列化之后直接调用alloc.col(DT)
是不可能的,但在实践中也需要这样做。我倾向于像使用数据库一样使用data.table;i、 e.GlobalEnv中的几个大型固定名称表。请参阅“新建编辑”。@MatthewDowle(和Simon)--感谢您的指点,我可以看出您的第二个示例与我的想法有些相似,即构造适当的调用,并在正确的父环境中对其进行评估。(fooCall
在我的问题中)+1有趣。我想知道是否应该增强alloc.col
以同时接受字符向量?然后它可以包装load()
调用。我认为您不需要数据。表:
前缀为alloc。col
已导出并供用户使用。@MatthewDowle,关于数据的要点很好。表:
已修复,并修改为提前知道加载结果的特定情况alloc.col
可能还需要一个环境参数。好主意。增强alloc.col
现在已存档。
DT<-unserialize(serialize(data.table(b = 1:5),NULL))
foo(DT)
DT
DT<-unserialize(serialize(data.table(b = 1:3),NULL))
DT
b
1: 1
2: 2
3: 3
DT[,newcol:=42]
DT # Ok. DT rebound to new shallow copy (when direct)
b newcol
1: 1 42
2: 2 42
3: 3 42
DT<-unserialize(serialize(data.table(b = 1:3),NULL))
foo(DT)
b a
1: 1 1
2: 2 1
3: 3 1
DT # but not ok when via function foo()
b
1: 1
2: 2
3: 3
DT<-unserialize(serialize(data.table(b = 1:3),NULL))
alloc.col(DT) # alloc.col needed first
b
1: 1
2: 2
3: 3
foo(DT)
b a
1: 1 1
2: 2 1
3: 3 1
DT # now it's ok
b a
1: 1 1
2: 2 1
3: 3 1
DT <- unserialize(serialize(data.table(b = 1:5),NULL))
foo <- function() {
DT[, newcol := 7]
}
foo()
b newcol
1: 1 7
2: 2 7
3: 3 7
4: 4 7
5: 5 7
DT # Unserialized data.table now over-allocated and updated ok.
b newcol
1: 1 7
2: 2 7
3: 3 7
4: 4 7
5: 5 7
# get the environment from the call to `data()`
env <- get('envir', parent.frame(1))
# load the data
load(system.file('extdata','DT.rda', package= 'foo'), envir = env)
# overallocate (evaluating in correct environment)
if(require(data.table)){
# the contents of `DT.rda` are known, so write out in full
evalq(alloc.col(DT), envir = env)
}
# clean up so `env` object not present in env environment after calling `data(DT)`
rm(list = c('env'), envir = env)
}