是的,它是 R 中使用<-
(或=
或->
)的子赋值,它制作了整个对象的副本。您可以使用tracemem(DT)
和进行跟踪.Internal(inspect(DT))
,如下所示。data.table
功能:=
并set()
通过引用分配给它们传递的任何对象。因此,如果该对象先前被复制(通过子分配<-
或显式copy(DT)
),那么它就是通过引用修改的副本。
DT <- data.table(a = c(1, 2), b = c(11, 12))
newDT <- DT
.Internal(inspect(DT))
# @0000000003B7E2A0 19 VECSXP g0c7 [OBJ,NAM(2),ATT] (len=2, tl=100)
# @00000000040C2288 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 1,2
# @00000000040C2250 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 11,12
# ATTRIB: # ..snip..
.Internal(inspect(newDT)) # precisely the same object at this point
# @0000000003B7E2A0 19 VECSXP g0c7 [OBJ,NAM(2),ATT] (len=2, tl=100)
# @00000000040C2288 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 1,2
# @00000000040C2250 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 11,12
# ATTRIB: # ..snip..
tracemem(newDT)
# [1] "<0x0000000003b7e2a0"
newDT$b[2] <- 200
# tracemem[0000000003B7E2A0 -> 00000000040ED948]:
# tracemem[00000000040ED948 -> 00000000040ED830]: .Call copy $<-.data.table $<-
.Internal(inspect(DT))
# @0000000003B7E2A0 19 VECSXP g0c7 [OBJ,NAM(2),TR,ATT] (len=2, tl=100)
# @00000000040C2288 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 1,2
# @00000000040C2250 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 11,12
# ATTRIB: # ..snip..
.Internal(inspect(newDT))
# @0000000003D97A58 19 VECSXP g0c7 [OBJ,NAM(2),ATT] (len=2, tl=100)
# @00000000040ED7F8 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 1,2
# @00000000040ED8D8 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 11,200
# ATTRIB: # ..snip..
请注意a
向量是如何被复制的(不同的十六进制值表示向量的新副本),即使a
没有更改。甚至整个都b
被复制了,而不是仅仅改变需要改变的元素。对于大数据,避免这一点很重要,以及:=
为什么set()
引入data.table
.
现在,使用我们的副本,newDT
我们可以通过引用对其进行修改:
newDT
# a b
# [1,] 1 11
# [2,] 2 200
newDT[2, b := 400]
# a b # See FAQ 2.21 for why this prints newDT
# [1,] 1 11
# [2,] 2 400
.Internal(inspect(newDT))
# @0000000003D97A58 19 VECSXP g0c7 [OBJ,NAM(2),ATT] (len=2, tl=100)
# @00000000040ED7F8 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 1,2
# @00000000040ED8D8 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 11,400
# ATTRIB: # ..snip ..
请注意,所有 3 个十六进制值(列点向量和 2 列中的每一列)保持不变。所以它是真正通过引用修改的,根本没有副本。
或者,我们可以参考修改原文DT
:
DT[2, b := 600]
# a b
# [1,] 1 11
# [2,] 2 600
.Internal(inspect(DT))
# @0000000003B7E2A0 19 VECSXP g0c7 [OBJ,NAM(2),ATT] (len=2, tl=100)
# @00000000040C2288 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 1,2
# @00000000040C2250 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 11,600
# ATTRIB: # ..snip..
这些十六进制值与我们在上面看到的原始值相同DT
。键入以获取更多使用和比较的example(copy)
示例。tracemem
data.frame
顺便说一句,如果你tracemem(DT)
那么DT[2,b:=600]
你会看到一份报告。这是该print
方法执行的前 10 行的副本。当使用函数或脚本包装invisible()
或在函数或脚本中调用时,print
不会调用该方法。
所有这些也适用于函数内部;即,:=
不要set()
在写入时复制,即使在函数内也是如此。如果您需要修改本地副本,请x=copy(x)
在函数开头调用。但是,记住data.table
是针对大数据的(以及针对小数据的更快编程优势)。我们故意不想复制大对象(永远)。因此,我们不需要考虑通常的 3* 工作记忆因子经验法则。我们尝试只需要一列大小的工作记忆(即工作记忆系数为 1/ncol 而不是 3)。