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.table
recursos :=
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 a
vetor foi copiado (valor hexadecimal diferente indica nova cópia do vetor), mesmo que a
não tenha sido alterado. Até o todo b
foi 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 DT
por 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 DT
acima. Digite example(copy)
para obter mais exemplos usando tracemem
e 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 print
método faz. Quando envolvido com invisible()
ou quando chamado em uma função ou script, o print
mé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.table
se 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.