Sì, è il sottoassegnazione in R che utilizza <-
(o =
o ->
) a creare una copia dell'intero oggetto. Puoi rintracciarlo usando tracemem(DT)
e .Internal(inspect(DT))
, come sotto. Le data.table
caratteristiche :=
e set()
assegnate in riferimento a qualunque oggetto vengano passate. Quindi se quell'oggetto è stato precedentemente copiato (da una sottoassegnazione <-
o da un esplicito copy(DT)
), allora è la copia che viene modificata per riferimento.
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..
Notare come anche il a
vettore è stato copiato (valore esadecimale diverso indica una nuova copia del vettore), anche se a
non è stato modificato. Anche l'intero è b
stato copiato, piuttosto che cambiare semplicemente gli elementi che devono essere cambiati. È importante evitare dati di grandi dimensioni e perché :=
e perché set()
sono stati introdotti data.table
.
Ora, con il nostro copiato newDT
, possiamo modificarlo come riferimento:
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 ..
Si noti che tutti e 3 i valori esadecimali (il vettore dei punti di colonna e ciascuna delle 2 colonne) rimangono invariati. Quindi è stato veramente modificato per riferimento senza alcuna copia.
Oppure, possiamo modificare l'originale DT
per riferimento:
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..
Quei valori esadecimali sono gli stessi dei valori originali che abbiamo visto DT
sopra. Digitare example(copy)
per ulteriori esempi utilizzando tracemem
e confronto a data.frame
.
A proposito, se tracemem(DT)
poi DT[2,b:=600]
vedrai una copia segnalata. Questa è una copia delle prime 10 righe eseguite dal print
metodo. Se racchiuso in invisible()
o quando viene chiamato all'interno di una funzione o di uno script, il print
metodo non viene chiamato.
Tutto ciò vale anche per le funzioni interne; cioè, :=
e set()
non copiare su scrittura, anche all'interno delle funzioni. Se è necessario modificare una copia locale, chiamare x=copy(x)
all'inizio della funzione. Tuttavia, ricorda che data.table
è per dati di grandi dimensioni (oltre a vantaggi di programmazione più rapidi per dati di piccole dimensioni). Non vogliamo deliberatamente copiare oggetti di grandi dimensioni (mai). Di conseguenza non è necessario consentire la consueta regola pratica del fattore di memoria di lavoro 3 *. Cerchiamo di avere bisogno solo di una memoria di lavoro grande quanto una colonna (cioè un fattore di memoria di lavoro di 1 / ncol anziché 3).
<-
anziché=
per un compito di base in R (ad es. Da Google: google.github.io/styleguide/Rguide.xml#assignment ). Ciò significa che la manipolazione di data.table non funzionerà allo stesso modo della manipolazione del frame di dati ed è quindi tutt'altro che una sostituzione drop-in del frame di dati.