J'ai un peu de mal à comprendre les propriétés de passage par référence de data.table
. Certaines opérations semblent «casser» la référence, et j'aimerais comprendre exactement ce qui se passe.
Lors de la création d'un à data.table
partir d'un autre data.table
(via <-
, puis de la mise à jour de la nouvelle table par :=
, la table d'origine est également modifiée. Ceci est attendu, selon:
?data.table::copy
et stackoverflow: pass-by-reference-the-operator-in-the-data-table-package
Voici un exemple:
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
Cependant, si j'insère une :=
modification non basée entre l' <-
affectation et les :=
lignes ci-dessus, DT
elle n'est plus modifiée:
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
Il semble donc que le newDT$b[2] <- 200
ligne «casse» en quelque sorte la référence. Je suppose que cela invoque une copie d'une manière ou d'une autre, mais j'aimerais bien comprendre comment R traite ces opérations, pour m'assurer de ne pas introduire de bogues potentiels dans mon code.
J'apprécierais beaucoup si quelqu'un pouvait m'expliquer cela.
la source
<-
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.Réponses:
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 utilisanttracemem(DT)
et.Internal(inspect(DT))
, comme ci-dessous. ledata.table
fonctionnalités:=
et lesset()
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 explicitecopy(DT)
), c'est la copie qui est modifiée par référence.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'ila
n'a pas été modifié. Même le tout ab
é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:=
etset()
ont été introduitsdata.table
.Maintenant, avec notre copié,
newDT
nous pouvons le modifier par référence: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:Ces valeurs hexadécimales sont les mêmes que les valeurs d'origine que nous avons vues
DT
ci-dessus. Tapezexample(copy)
pour plus d'exemples d'utilisationtracemem
et de comparaison avecdata.frame
.Btw, si vous voyez
tracemem(DT)
alorsDT[2,b:=600]
une copie rapportée. C'est une copie des 10 premières lignes que fait laprint
méthode. Lorsqu'elle est encapsulée avecinvisible()
ou lorsqu'elle est appelée dans une fonction ou un script, laprint
méthode n'est pas appelée.Tout cela s'applique également à l'intérieur des fonctions; c'est-à-dire,
:=
etset()
ne copiez pas en écriture, même dans les fonctions. Si vous devez modifier une copie locale, appelezx=copy(x)
au début de la fonction. Mais rappelezdata.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).la source
->
affectation change l'emplacement de la mémoire. Les vecteurs inchangés conservent l'emplacement mémoire des vecteurs du data.frame d'origine. Le comportement dedata.table
s décrit ici est le comportement actuel à partir du 1.12.2.Juste un bref résumé.
<-
avecdata.table
est juste comme la base; c'est-à-dire qu'aucune copie n'est effectuée jusqu'à ce qu'une sous-attribution soit effectuée par la suite avec<-
(comme changer les noms de colonne ou changer un élément tel queDT[i,j]<-v
). Ensuite, il prend une copie de l'objet entier tout comme la base. C'est ce qu'on appelle la copie sur écriture. Serait mieux connu sous le nom de copie sur sous-attribution, je pense! Il NE copie PAS lorsque vous utilisez l':=
opérateur spécial ou lesset*
fonctions fournies pardata.table
. Si vous disposez de données volumineuses, vous souhaiterez probablement les utiliser à la place.:=
etset*
NE COPIE PASdata.table
, MÊME DANS LES FONCTIONS.Compte tenu de cet exemple de données:
Ce qui suit "lie" simplement un autre nom
DT2
au même objet de données lié actuellement au nomDT
:Cela ne copie jamais, et jamais non plus dans la base. Il marque simplement l'objet de données afin que R sache que deux noms différents (
DT2
etDT
) pointent vers le même objet. Et donc R devra copier l'objet si l'un ou l'autre est sous- affecté par la suite.C'est parfait pour
data.table
aussi. Le:=
n'est pas pour faire ça. Donc, ce qui suit est une erreur délibérée,:=
pas seulement pour la liaison des noms d'objets::=
est pour la sous- attribution par référence. Mais vous ne l'utilisez pas comme vous le feriez dans la base:vous l'utilisez comme ceci:
Cela a changé
DT
par référence. Supposons que vous ajoutiez une nouvelle colonnenew
par référence à l'objet de données, il n'est pas nécessaire de le faire:parce que le RHS a déjà changé
DT
par référence. Le plusDT <-
est de mal comprendre ce que:=
fait. Vous pouvez l'écrire là-bas, mais c'est superflu.DT
est modifié par référence:=
, MÊME DANS LES FONCTIONS:data.table
est pour les grands ensembles de données, rappelez-vous. Si vous avez 20 Godata.table
de mémoire, vous avez besoin d'un moyen de le faire. C'est une décision de conception très délibérée dedata.table
.Des copies peuvent être faites, bien sûr. Il vous suffit de dire à data.table que vous êtes sûr de vouloir copier votre ensemble de données de 20 Go, en utilisant la
copy()
fonction:Pour éviter les copies, n'utilisez pas l'attribution ou la mise à jour du type de base:
Si vous voulez être sûr que vous mettez à jour par référence, utilisez
.Internal(inspect(x))
et regardez les valeurs d'adresse mémoire des constituants (voir la réponse de Matthew Dowle).L' écriture
:=
dans cej
genre vous permet subassign par référence par groupe . Vous pouvez ajouter une nouvelle colonne par référence par groupe. C'est pourquoi:=
on fait de cette façon à l'intérieur[...]
:la source