是的,它是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)。
<-
代替=
R 中的基本分配(例如Google:google.github.io/styleguide/Rguide.xml#assignment)。但这意味着data.table操作将无法与数据帧操作以相同的方式工作,因此与直接替换数据帧相去甚远。