ags*_*udy 12 r data.table
这似乎是fread
错误,但我不确定.
这个例子重现了我的问题.我有一个函数,我在其中读取data.table并将其返回到列表中.我使用list将其他结果分组到相同的结构中.这是我的代码:
ff.fread <- function(){
dt = fread("x
1
2
")
list(dt=dt)
}
DT.f <- ff.fread()$dt
Run Code Online (Sandbox Code Playgroud)
现在,当我尝试向DT.f添加新列时,它可以正常工作,但我收到一条警告消息:
DT.f[,y:=1:2]
Warning message:
In `[.data.table`(DT.f, , `:=`(y, 1:2)) :
Invalid .internal.selfref detected and fixed by taking a copy of the whole
table so that := can add this new column by reference. At an earlier point,
this data.table has been copied by R (or been created manually using
structure() or similar). Avoid key<-, names<- and attr<- which in R currently
(and oddly) may copy the whole data.table. Use set* syntax instead to avoid
copying: ?set, ?setnames and ?setattr. Also, in R<v3.1.0, list(DT1,DT2) copied
the entire DT1 and DT2 (R's list() used to copy named objects); please upgrade
to R>=v3.1.0 if that is biting. If this message doesn't help, please report to
datatable-help so the root cause can be fixed.
Run Code Online (Sandbox Code Playgroud)
请注意,如果我手动创建data.table我没有此警告.这工作正常,例如:
ff <- function(){
list(dt=data.table(x=1:2))
}
DT <- ff()$dt
DT[,y:=1:2]
Run Code Online (Sandbox Code Playgroud)
或者如果我不返回fread
列表中的结果,它也可以正常工作
ff.fread <- function(){
dt = fread("x
1
2
")
dt
}
Run Code Online (Sandbox Code Playgroud)
Aru*_*run 25
这与fread
本身无关,但是你正在调用list()
并传递一个命名对象.我们可以通过以下方式重新创建:
require(data.table)
DT <- data.table(x=1:2) # name the object 'DT'
DT.l <- list(DT=DT) # create a list containing one data.table
y <- DT.l$DT # get back the data.table
y[, bla := 1L] # now add by reference
# works fine but warning message will occur
DT.l = list(DT=data.table(x=1:2)) # DT = a call, not a named object
y = DT.l$DT
y[, bla:=1L]
# works fine and no warning message
Run Code Online (Sandbox Code Playgroud)
好消息是,从R版本> = 3.1.0(现在在devel中),传递命名对象list()
将不再创建副本,而是它的引用计数(指向此值的对象数)刚刚被碰撞.因此,问题随着下一版本的R而消失.
要了解如何data.table
使用检测副本.internal.selfref
,我们将深入了解一些历史data.table
.
您应该知道在创建时data.table
过度分配列指针槽(truelength设置为默认值100),以便:=
稍后可以通过引用添加列.这样就有一个问题 - 处理副本.例如,当我们调用list()
并传递一个命名对象时,正在制作一个副本,如下所示.
tracemem(DT)
# [1] "<0x7fe23ac3e6d0>"
DT.list <- list(DT=DT) # `DT` is the named object on the RHS of = here
# tracemem[0x7fe23ac3e6d0 -> 0x7fe23cd72f48]:
Run Code Online (Sandbox Code Playgroud)
与任何副本的问题data.table
是R品牌(没有data.table
的copy()
)条件是,R内部设置的truelength
参数为0,即使truelength(.)
函数仍然会返回正确的结果.这无意中导致了段错误时参照更新:=
,因为在过度分配不存在了(或者至少不再被认可).这发生在<1.7.8版本中.为了克服这个问题,.internal.selfref
引入了一个名为的属性.您可以通过执行来检查此属性attributes(DT)
.
来自NEWS(第1.7.8节):
o'克里斯崩溃'是固定的.根本原因是
key<-
始终复制整个表格.该副本的问题(除了较慢)是R不保持过度分配truelength
,但它看起来好像有.key<-
在内部使用,特别是在内部使用merge()
.因此,使用:=
after 添加列merge()
是内存覆盖,因为过度分配的内存在key<-
复制之后并不存在.
data.tables
现在有一个新的属性.internal.selfref
来捕捉和警告这些副本将来.所有内部使用key<-
已被替换为接受矢量的setkey()
新功能setkeyv()
,并且不会复制.
.internal.selfref
做什么的?它只是指向自己,基本上.它只是附加到的属性DT
,包含RAM中的地址DT
.如果R无意中复制DT
,则地址DT
将在RAM中移动但附加的属性仍将包含旧的内存地址,它们将不再匹配.data.table
在通过引用将新列添加到备用列指针槽之前,检查它们是否匹配(即有效).
.internal.selfref
实施?为了理解这个属性.internal.selfref
,我们要理解外部指针(EXTPTRSXP
)是什么.这个页面很好地解释了.复制/粘贴基本行:
外部指针SEXP旨在处理对诸如句柄之类的 C结构的引用,并且例如在包RODBC中用于此目的.它们的复制语义不同寻常,因为复制R对象时,外部指针对象不会重复.
它们被创建为:
SEXP R_MakeExternalPtr(void *p, SEXP tag, SEXP prot);
Run Code Online (Sandbox Code Playgroud)
其中p是指针(因此它不能作为函数指针),而tag和prot是对普通R对象的引用,它们将在外部指针对象的生命周期内保持存在(受到垃圾收集保护).一个有用的约定是将标记字段用于某种形式的类型标识,使用prot字段来保护外部指针所代表的内存,如果该内存是从R堆分配的话.
在我们的例子中,我们.internal.selfref
为DT 创建了/ 的属性,其值是一个指向NULL的外部指针(你在属性值中看到的地址),这个外部指针的prot
字段是另一个外部指针返回DT
(因此称为selfref))prot
这次设置为NULL.
注意:我们将这个extptr用于NULL,其'prot'是一个extptr策略,因此identical(DT1, DT2)
它是两个不同的副本,但具有相同的内容返回TRUE.(如果你不明白这意味着什么,你可以跳到下一部分.这与理解这个问题的答案无关).
我们知道外部指针在复制期间不会重复.基本上,当我们创建一个data.table时,属性.internal.selfref会创建一个指向NULL的外部指针,并使用它的prot
字段创建一个返回的外部指针DT
.现在,当进行无意的"复制"时,对象的地址被修改,但不受属性保护的地址.它仍然指出它DT
是否存在..因为它不会/不能被修改.因此,通过检查当前对象的地址和受外部指针保护的地址,可以在内部检测到这一点.如果它们不匹配,那么R就会产生一个"副本"(这会丢失过度分配的data.table小心创建).那是:
DT <- data.table(x=1:2) # internal selfref set
DT.list <- list(DT=DT) # copy made, address(DT.list$DT) != address(DT)
# and truelength would be affected.
DT.new <- DT.list$DT # address of DT.new != address of DT
# and it's not equal to the address pointed to by
# the attribute's 'prot' external pointer
# so a re-over-allocation has to be made by data.table at the next update by
# reference, and it warns so you can fix the root cause by not using list(),
# key<-, names<- etc.
Run Code Online (Sandbox Code Playgroud)
这需要很多东西.我想我已经设法尽可能清楚地完成它.如果有任何错误(我需要一段时间将其包裹在我的脑海中)或进一步清晰的可能性,请随时编辑或评论您的建议.
希望这可以解决问题.
Mat*_*wle 10
阿伦的答案是一个很好的解释.list()
R <= 3.0.2 的特定特征是它复制了命名输入(在调用之前已经命名的东西list()
).现在r-devel(R的下一个版本),这个副本list()
不再发生,一切都会很好.这是R中非常受欢迎的变化.
在此期间,您可以通过以不同方式创建输出列表来解决此问题.
> R.version.string
[1] "R version 3.0.2 (2013-09-25)"
Run Code Online (Sandbox Code Playgroud)
首先演示list()复制:
> DT = data.table(a=1:3)
> address(DT)
[1] "0x1d70010"
> address(list(DT)[[1]])
[1] "0x21bc178" # different address => list() copied the data.table named DT
> data.table:::selfrefok(DT)
[1] 1
> data.table:::selfrefok(list(DT)[[1]])
[1] 0 # i.e. this copied DT is not over-allocated
Run Code Online (Sandbox Code Playgroud)
现在以不同的方式创建相同的列表:
> ans = list()
> ans$DT = DT # use $<- instead
> address(DT)
[1] "0x1d70010"
> address(ans$DT)
[1] "0x1d70010" # good, no copy
> identical(ans, list(DT=DT))
[1] TRUE
> data.table:::selfrefok(ans$DT)
[1] 1 # good, the list()-ed DT is still over-allocated ok
Run Code Online (Sandbox Code Playgroud)
我知道,令人困惑和困惑.利用$<-
创建输出列表,甚至只是将调用fread
内部调用list()
,即list(DT=fread(...))
应通过避免拷贝list()
.
归档时间: |
|
查看次数: |
3524 次 |
最近记录: |