Ich habe ein kleines Problem damit, die Pass-by-Reference-Eigenschaften von zu verstehen data.table
. Einige Operationen scheinen die Referenz zu "brechen", und ich möchte genau verstehen, was passiert.
Beim Erstellen eines data.table
von einem anderen data.table
(via<-
und anschließendes Aktualisieren der neuen Tabelle durch :=
wird auch die ursprüngliche Tabelle geändert. Dies wird erwartet wie folgt:
?data.table::copy
und Stackoverflow: Referenzübergabe des Operators im Datentabellenpaket
Hier ist ein Beispiel:
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
Wenn ich jedoch eine nicht :=
basierte Änderung zwischen der <-
Zuordnung und den :=
obigen Zeilen einfüge, DT
wird diese jetzt nicht mehr geändert:
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
So scheint es, dass die newDT$b[2] <- 200
Zeile die Referenz irgendwie "bricht". Ich würde vermuten, dass dies irgendwie eine Kopie aufruft, aber ich möchte vollständig verstehen, wie R diese Operationen behandelt, um sicherzustellen, dass ich keine potenziellen Fehler in meinen Code einführe.
Ich würde mich sehr freuen, wenn mir jemand das erklären könnte.
quelle
<-
anstelle der=
grundlegenden Zuweisung in R zu verwenden (z. B. von Google: google.github.io/styleguide/Rguide.xml#assignment ). Dies bedeutet jedoch, dass die Manipulation von data.table nicht wie die Manipulation von Datenrahmen funktioniert und daher weit davon entfernt ist, den Datenrahmen durch einen Drop-In zu ersetzen.Antworten:
Ja, es ist die Unterzuweisung in R mit
<-
(oder=
oder->
), die eine Kopie des gesamten Objekts erstellt. Sie können dies mittracemem(DT)
und.Internal(inspect(DT))
wie unten verfolgen . Diedata.table
Funktionen:=
undset()
Zuweisung unter Bezugnahme auf das Objekt, an das sie übergeben werden. Wenn dieses Objekt zuvor kopiert wurde (durch eine Unterzuweisung<-
oder eine explizite Zuweisungcopy(DT)
), wird die Kopie durch Referenz geändert.Beachten Sie, wie sogar der
a
Vektor kopiert wurde (ein anderer Hex-Wert zeigt eine neue Kopie des Vektors an), obwohl era
nicht geändert wurde. Sogar das Ganzeb
wurde kopiert, anstatt nur die Elemente zu ändern, die geändert werden müssen. Dies ist wichtig, um große Datenmengen zu vermeiden, und warum:=
undset()
wurden eingeführtdata.table
.Jetzt
newDT
können wir mit unserer Kopie es durch Bezugnahme ändern:Beachten Sie, dass alle 3 Hex-Werte (der Vektor der Spaltenpunkte und jede der 2 Spalten) unverändert bleiben. Es wurde also wirklich durch Referenz ohne Kopien modifiziert.
Oder wir können das Original
DT
durch Bezugnahme ändern :Diese Hex-Werte sind die gleichen wie die ursprünglichen Werte, die wir
DT
oben gesehen haben. Geben Sieexample(copy)
für weitere Beispiele eintracemem
und vergleichen Sie mitdata.frame
.Übrigens, wenn Sie
tracemem(DT)
dann sehenDT[2,b:=600]
Sie eine Kopie gemeldet. Dies ist eine Kopie der ersten 10 Zeilen, die dieprint
Methode ausführt. Beim Umschließen mitinvisible()
oder beim Aufrufen innerhalb einer Funktion oder eines Skripts wird dieprint
Methode nicht aufgerufen.All dies gilt auch für Funktionen; dh
:=
undset()
nicht beim Schreiben kopieren, auch nicht innerhalb von Funktionen. Wenn Sie eine lokale Kopie ändern müssen, rufen Siex=copy(x)
zu Beginn der Funktion auf. Denken Sie jedoch daran, dass diesdata.table
für große Datenmengen gilt (sowie für schnellere Programmiervorteile für kleine Datenmengen). Wir wollen absichtlich (nie) keine großen Objekte kopieren. Infolgedessen müssen wir die übliche Faustregel für den 3 * Arbeitsspeicherfaktor nicht berücksichtigen. Wir versuchen, nur Arbeitsspeicher zu benötigen, der so groß wie eine Spalte ist (dh einen Arbeitsspeicherfaktor von 1 / ncol anstelle von 3).quelle
->
Zuweisung geändert wurde, den Speicherort. Die unveränderten Vektoren behalten den Speicherort der Vektoren des ursprünglichen Datenrahmens bei. Dasdata.table
hier beschriebene Verhalten von s ist das aktuelle Verhalten ab 1.12.2.Nur eine kurze Zusammenfassung.
<-
mitdata.table
ist wie Basis; Das heißt, es wird keine Kopie erstellt, bis anschließend eine Unterzuweisung durchgeführt wird<-
(z. B. Ändern der Spaltennamen oder Ändern eines Elements wieDT[i,j]<-v
). Dann wird eine Kopie des gesamten Objekts wie bei base erstellt. Das ist als Copy-on-Write bekannt. Wäre besser bekannt als Copy-on-Subassign, denke ich! Es wird NICHT kopiert, wenn Sie den speziellen:=
Operator oder die vonset*
bereitgestellten Funktionen verwendendata.table
. Wenn Sie große Datenmengen haben, möchten Sie diese wahrscheinlich stattdessen verwenden.:=
undset*
wird diedata.table
nicht kopieren, auch nicht innerhalb von Funktionen.Angesichts dieser Beispieldaten:
Im Folgenden wird nur ein anderer Name
DT2
an dasselbe Datenobjekt "gebunden", das derzeit an den Namen gebunden istDT
:Dies kopiert niemals und auch niemals in der Basis. Es markiert nur das Datenobjekt, sodass R weiß, dass zwei verschiedene Namen (
DT2
undDT
) auf dasselbe Objekt verweisen. Und so muss R das Objekt kopieren, wenn beide später untergeordnet werden.Das ist auch perfekt für
data.table
. Das:=
ist nicht dafür. Das Folgende ist also ein absichtlicher Fehler, da:=
nicht nur Objektnamen gebunden werden::=
dient zur Unterzuweisung als Referenz. Aber Sie verwenden es nicht wie in der Basis:Sie verwenden es wie folgt:
Das hat sich
DT
durch Bezugnahme geändert . Angenommen, Sie fügen eine neue Spaltenew
unter Bezugnahme auf das Datenobjekt hinzu. Dies ist nicht erforderlich:weil sich die RHS bereits
DT
durch Bezugnahme geändert hat . Das ExtraDT <-
ist, was falsch zu verstehen:=
tut. Sie können es dort schreiben, aber es ist überflüssig.DT
wird durch Bezugnahme geändert, durch:=
, AUCH IN FUNKTIONEN:data.table
ist für große Datenmengen gedacht. Wenn Sie über 20 GBdata.table
Speicher verfügen , benötigen Sie eine Möglichkeit, dies zu tun. Es ist eine sehr bewusste Designentscheidung vondata.table
.Kopien können natürlich angefertigt werden. Sie müssen data.table lediglich mitteilen, dass Sie sicher sind, dass Sie Ihren 20-GB-Datensatz kopieren möchten, indem Sie die folgende
copy()
Funktion verwenden:Verwenden Sie keine Zuordnung oder Aktualisierung des Basistyps, um Kopien zu vermeiden:
Wenn Sie sicher sein möchten, dass Sie durch Referenz aktualisieren, verwenden Sie
.Internal(inspect(x))
die Speicheradresswerte der Bestandteile (siehe Antwort von Matthew Dowle).Das Schreiben
:=
inj
so können Sie durch Verweis subassign durch Gruppe . Sie können eine neue Spalte nach Referenz nach Gruppe hinzufügen. Deshalb:=
wird es im Inneren so gemacht[...]
:quelle