Oui, c'est la sous-affectation dans R en utilisant <-
(ou =
ou ->
) qui fait une copie de l' objet entier . Vous pouvez tracer cela en utilisant tracemem(DT)
et .Internal(inspect(DT))
, comme ci-dessous. ledata.table
fonctionnalités :=
et les set()
attribuer par référence à tout objet auquel elles sont transmises. Donc, si cet objet a été précédemment copié (par une sous-attribution <-
ou un explicite copy(DT)
), c'est la copie qui est modifiée par référence.
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..
Remarquez comment même le a
vecteur a été copié (une valeur hexadécimale différente indique une nouvelle copie du vecteur), même s'il a
n'a pas été modifié. Même le tout a b
été copié, plutôt que de simplement changer les éléments qui doivent être modifiés. C'est important à éviter pour les données volumineuses, et pourquoi :=
et set()
ont été introduits data.table
.
Maintenant, avec notre copié, newDT
nous pouvons le modifier par référence:
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 ..
Notez que les 3 valeurs hexadécimales (le vecteur des points de colonne et chacune des 2 colonnes) restent inchangées. Il a donc été vraiment modifié par référence sans aucune copie.
Ou, nous pouvons modifier l'original DT
par référence:
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..
Ces valeurs hexadécimales sont les mêmes que les valeurs d'origine que nous avons vues DT
ci-dessus. Tapez example(copy)
pour plus d'exemples d'utilisation tracemem
et de comparaison avec data.frame
.
Btw, si vous voyez tracemem(DT)
alors DT[2,b:=600]
une copie rapportée. C'est une copie des 10 premières lignes que fait la print
méthode. Lorsqu'elle est encapsulée avec invisible()
ou lorsqu'elle est appelée dans une fonction ou un script, la print
méthode n'est pas appelée.
Tout cela s'applique également à l'intérieur des fonctions; c'est-à-dire, :=
et set()
ne copiez pas en écriture, même dans les fonctions. Si vous devez modifier une copie locale, appelez x=copy(x)
au début de la fonction. Mais rappelez data.table
- vous que c'est pour les données volumineuses (ainsi que des avantages de programmation plus rapide pour les petites données). Nous ne voulons délibérément pas copier de gros objets (jamais). Par conséquent, nous n'avons pas besoin de tenir compte de la règle empirique habituelle du facteur de mémoire de travail 3 *. Nous essayons de n'avoir besoin que d'une mémoire de travail aussi grande qu'une colonne (c'est-à-dire un facteur de mémoire de travail de 1 / ncol au lieu de 3).
<-
place de=
l'affectation de base dans R (par exemple par Google: google.github.io/styleguide/Rguide.xml#assignment ). Mais cela signifie que la manipulation de data.table ne fonctionnera pas de la même manière que la manipulation de trame de données et est donc loin d'être un remplacement instantané de la trame de données.