Estou com problemas para entender as propriedades de passagem por referência de data.table
. Algumas operações parecem 'quebrar' a referência e eu gostaria de entender exatamente o que está acontecendo.
Ao criar um a data.table
partir de outro data.table
(via <-
, e atualizando a nova tabela por :=
, a tabela original também é alterada. Isso é esperado, conforme:
?data.table::copy
e stackoverflow: passa-por-referência-o-operador-na-tabela-de-dados-pacote
Aqui está um exemplo:
library(data.table)
DT <- data.table(a=c(1,2), b=c(11,12))
print(DT)
# a b
# [1,] 1 11
# [2,] 2 12
newDT <- DT # reference, not copy
newDT[1, a := 100] # modify new DT
print(DT) # DT is modified too.
# a b
# [1,] 100 11
# [2,] 2 12
No entanto, se eu inserir uma :=
modificação não baseada entre a <-
atribuição e as :=
linhas acima, DT
agora não será mais modificada:
DT = data.table(a=c(1,2), b=c(11,12))
newDT <- DT
newDT$b[2] <- 200 # new operation
newDT[1, a := 100]
print(DT)
# a b
# [1,] 1 11
# [2,] 2 12
Então parece que a newDT$b[2] <- 200
linha de alguma forma 'quebra' a referência. Eu acho que isso chama uma cópia de alguma forma, mas eu gostaria de entender completamente como o R está tratando essas operações, para garantir que eu não introduza possíveis erros no meu código.
Eu apreciaria muito se alguém pudesse me explicar isso.
fonte
<-
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.Respostas:
Sim, é a sub-atribuição em R usando
<-
(ou=
ou->
) que faz uma cópia de todo o objeto. Você pode rastrear isso usandotracemem(DT)
e.Internal(inspect(DT))
, como abaixo. Osdata.table
recursos:=
eset()
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ícitacopy(DT)
), é a cópia que é modificada por referência.Observe como até o
a
vetor foi copiado (valor hexadecimal diferente indica nova cópia do vetor), mesmo quea
não tenha sido alterado. Até o todob
foi copiado, em vez de apenas alterar os elementos que precisam ser alterados. Isso é importante para evitar dados grandes, e por que:=
eset()
foram introduzidosdata.table
.Agora, com nossa cópia
newDT
, podemos modificá-la por referência: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:Esses valores hexadecimais são iguais aos valores originais que vimos
DT
acima. Digiteexample(copy)
para obter mais exemplos usandotracemem
e comparando comdata.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 oprint
método faz. Quando envolvido cominvisible()
ou quando chamado em uma função ou script, oprint
método não é chamado.Tudo isso se aplica também a funções internas; ou seja,
:=
eset()
não copie na gravação, mesmo dentro das funções. Se você precisar modificar uma cópia local, liguex=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).fonte
->
atribuição altera o local da memória. Os vetores inalterados mantêm a localização da memória dos vetores do data.frame original. O comportamento dedata.table
s descrito aqui é o comportamento atual a partir de 1.12.2.Apenas uma rápida soma.
<-
comdata.table
é como base; ou seja, nenhuma cópia é feita até que uma sub-atribuição seja feita posteriormente com<-
(como alterar os nomes das colunas ou alterar um elemento comoDT[i,j]<-v
). Em seguida, é necessária uma cópia de todo o objeto como a base. Isso é conhecido como cópia na gravação. Seria mais conhecido como copiar na subassignação, eu acho! NÃO copia quando você usa o:=
operador especial ou asset*
funções fornecidas pordata.table
. Se você possui dados grandes, provavelmente deseja usá-los.:=
eset*
NÃO COPIARÁdata.table
, MESMO DENTRO DAS FUNÇÕES.Dados esses dados de exemplo:
O seguinte apenas "vincula" outro nome
DT2
ao mesmo objeto de dados vinculado atualmente vinculado ao nomeDT
:Isso nunca copia e também nunca copia na base. Apenas marca o objeto de dados para que R saiba que dois nomes diferentes (
DT2
eDT
) apontam para o mesmo objeto. E, portanto, R precisará copiar o objeto se um deles for subassociado posteriormente.Isso é perfeito
data.table
também. O:=
não é para fazer isso. Portanto, o seguinte é um erro deliberado, pois:=
não é apenas para vincular nomes de objetos::=
é para subassociação por referência. Mas você não o usa como faria na base:você usa assim:
Isso mudou
DT
por referência. Digamos que você adicione uma nova colunanew
por referência ao objeto de dados, não há necessidade de fazer isso:porque o RHS já mudou
DT
por referência. O extraDT <-
é entender mal o que:=
faz. Você pode escrever lá, mas é supérfluo.DT
é alterado por referência, por:=
, MESMO DENTRO DAS FUNÇÕES:data.table
é para grandes conjuntos de dados, lembre-se. Se você tem 20 GBdata.table
de memória, precisa de uma maneira de fazer isso. É uma decisão de design muito deliberadadata.table
.Cópias podem ser feitas, é claro. Você só precisa informar ao data.table que tem certeza de que deseja copiar seu conjunto de dados de 20 GB usando a
copy()
função:Para evitar cópias, não use a atribuição ou atualização do tipo base:
Se quiser ter certeza de que está atualizando por referência, use
.Internal(inspect(x))
os valores de endereço de memória dos constituintes (consulte a resposta de Matthew Dowle).Escrevendo
:=
emj
como que permite subassign por referência pelo grupo . Você pode adicionar uma nova coluna por referência por grupo. É por isso que:=
é feito dessa maneira por dentro[...]
:fonte