Sim, é a sub-atribuição em R usando <-(ou =ou ->) que faz uma cópia de todo o objeto. Você pode rastrear isso usando tracemem(DT)e .Internal(inspect(DT)), como abaixo. Os data.tablerecursos :=e set()atribuem por referência a qualquer objeto que eles são transmitidos. Portanto, se esse objeto foi copiado anteriormente (por uma sub-atribuição <-ou explícita copy(DT)), é a cópia que é modificada por referência.
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..
Observe como até o avetor foi copiado (valor hexadecimal diferente indica nova cópia do vetor), mesmo que anão tenha sido alterado. Até o todo bfoi copiado, em vez de apenas alterar os elementos que precisam ser alterados. Isso é importante para evitar dados grandes, e por que :=e set()foram introduzidos data.table.
Agora, com nossa cópia newDT, podemos modificá-la por referência:
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 ..
Observe que todos os três valores hexadecimais (o vetor dos pontos da coluna e cada uma das duas colunas) permanecem inalterados. Por isso, foi verdadeiramente modificado por referência, sem cópias.
Ou podemos modificar o original DTpor referência:
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..
Esses valores hexadecimais são iguais aos valores originais que vimos DTacima. Digite example(copy)para obter mais exemplos usando tracememe comparando com data.frame.
Btw, se você tracemem(DT), em seguida, DT[2,b:=600]você verá uma cópia relatou. Essa é uma cópia das 10 primeiras linhas que o printmétodo faz. Quando envolvido com invisible()ou quando chamado em uma função ou script, o printmétodo não é chamado.
Tudo isso se aplica também a funções internas; ou seja, :=e set()não copie na gravação, mesmo dentro das funções. Se você precisar modificar uma cópia local, ligue x=copy(x)no início da função. Mas lembre- data.tablese de dados grandes (além de vantagens de programação mais rápidas para dados pequenos). Deliberadamente, não queremos copiar objetos grandes (nunca). Como resultado, não precisamos permitir a regra prática usual do fator de memória de 3 *. Tentamos precisar apenas de memória de trabalho do tamanho de uma coluna (ou seja, um fator de memória de 1 / ncol em vez de 3).
<-vez de=atribuições básicas em R (por exemplo: Google: google.github.io/styleguide/Rguide.xml#assignment ). Mas isso significa que a manipulação da tabela de dados não funcionará da mesma maneira que a manipulação do quadro de dados e, portanto, está longe de ser uma substituição imediata do quadro de dados.