Sí, es la subasignación en R usando <-
(o =
o ->
) lo que hace una copia de todo el objeto. Puede rastrear eso usando tracemem(DT)
y .Internal(inspect(DT))
, como a continuación. Las data.table
características :=
y set()
asignar por referencia a cualquier objeto que se pasan. Entonces, si ese objeto fue copiado previamente (mediante una subasignación <-
o un explícito copy(DT)
), entonces es la copia la que se modifica por referencia.
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 cómo a
se copió incluso el vector (un valor hexadecimal diferente indica una nueva copia del vector), aunque a
no se modificó. Incluso b
se copió todo, en lugar de simplemente cambiar los elementos que deben cambiarse. Eso es importante para evitar grandes datos, y por qué :=
y se set()
les presentó data.table
.
Ahora, con nuestra copia newDT
podemos modificarla por referencia:
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 los 3 valores hexadecimales (el vector de los puntos de columna y cada una de las 2 columnas) permanecen sin cambios. Por lo tanto, fue realmente modificado por referencia sin copias en absoluto.
O podemos modificar el original DT
por referencia:
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..
Esos valores hexadecimales son los mismos que los valores originales que vimos DT
anteriormente. Escriba example(copy)
para obtener más ejemplos utilizando tracemem
y comparación con data.frame
.
Por cierto, si tracemem(DT)
luego DT[2,b:=600]
verá una copia informada. Esa es una copia de las primeras 10 filas que hace el print
método. Cuando se envuelve con invisible()
o cuando se llama dentro de una función o script, el print
método no se llama.
Todo esto se aplica también a las funciones internas; es decir, :=
y set()
no copie en escritura, incluso dentro de las funciones. Si necesita modificar una copia local, llame x=copy(x)
al inicio de la función. Pero recuerde que data.table
es para datos grandes (así como ventajas de programación más rápidas para datos pequeños). Deliberadamente no queremos copiar objetos grandes (nunca). Como resultado, no necesitamos permitir la regla general del factor de memoria de trabajo 3 * habitual. Intentamos necesitar solo una memoria de trabajo tan grande como una columna (es decir, un factor de memoria de trabajo de 1 / ncol en lugar de 3).
<-
lugar de=
la asignación básica en R (por ejemplo, Google: google.github.io/styleguide/Rguide.xml#assignment ). Pero esto significa que la manipulación de data.table no funcionará de la misma manera que la manipulación del marco de datos y, por lo tanto, está lejos de ser un reemplazo directo al marco de datos.