是的,这是 R 中的子分配,使用<-
(or =
or ->
)复制一份whole目的。你可以使用追踪tracemem(DT)
and .Internal(inspect(DT))
, 如下。这data.table
特征:=
and 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
被复制,而不仅仅是更改需要更改的元素。对于大数据来说,避免这一点很重要,为什么:=
and 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)
then DT[2,b:=600]
您将看到报告的一份副本。这是前 10 行的副本print
方法确实如此。当包裹着invisible()
或者在函数或脚本中调用时,print
方法未被调用。
所有这些也适用于函数内部; IE。,:=
and set()
即使在函数内,也不要在写入时复制。如果需要修改本地副本,请调用x=copy(x)
在函数的开始处。但要记住data.table
适用于大数据(以及小数据的更快编程优势)。我们故意不想复制大对象(永远)。因此,我们不需要考虑通常的 3* 工作记忆因子经验法则。我们尝试只需要一列那么大的工作记忆(即工作记忆系数为 1/ncol 而不是 3)。