Meine Gruppe schreibt eine Menge Code mit data.table und wir gelegentlich von der 'Invalid .internal.selfref erkannt und durch eine Kopie von der ganze Tisch ... warnte. Dieses Verhalten kann unseren Code beschädigen, wenn eine Datentabelle durch Verweis auf eine Funktion übergeben wird, und ich versuche herauszufinden, wie ich sie umgehen kann.Umgang mit ungültigen Eigenreferenz in R data.table bei der Übergabe durch Verweis auf eine Funktion
Angenommen, ich habe eine Funktion, die als Nebeneffekt eine Spalte zu einer data.table hinzufügt - beachten Sie, dass die ursprüngliche data.table nicht zurückgegeben wird.
foo <- function(mydt){
mydt[, c := c("a", "b")]
return(123)
)
> x<- data.table(a=c(1,2), b=c(3,4))
> foo(x)
[1] 123
> x
a b c
1: 1 3 a
2: 2 4 b
x wurde mit der neuen Spalte aktualisiert. Dies ist das gewünschte Verhalten.
Nehmen wir nun an etwas passiert, dass die interne Selbst ref in x bricht:
> x<- data.table(a=c(1,2), b=c(3,4))
> x[["a"]] <- c(7,8)
> foo(x)
[1] 123
Warning message:
In `[.data.table`(mydt, , `:=`(c, c("a", "b"))) :
Invalid .internal.selfref detected and fixed by taking a copy ...
> x
a b
1: 7 3
2: 8 4
Ich verstehe, was (fast) passiert ist. Die Konstruktion [["a"]] ist nicht data.table friendly; x wurde in einen Datenrahmen und dann zurück in eine Datentabelle konvertiert, was die internen Abläufe irgendwie durcheinander brachte. Dann innerhalb von foo(), während der Referenzoperation des Hinzufügens einer Spalte wurde dieses Problem erkannt, und eine Kopie von mydt wurde erstellt; die neue Spalte 'c' wurde zu mydt hinzugefügt. Durch diese Kopieroperation wurde jedoch die Beziehung zwischen übergebenem Verweis zwischen x und mydt getrennt, sodass die zusätzlichen Spalten nicht zu x gehören.
Die Funktion foo() wird von verschiedenen Personen verwendet und es wird schwierig sein, sich vor ungültigen internen Selfref-Situationen zu schützen. Jemand da draußen könnte leicht etwas wie x [["a"]] machen, was zu einer ungültigen Eingabe führen würde. Ich versuche herauszufinden, wie ich damit umgehen kann.
Bisher habe ich diese Idee, am Anfang von foo():
if(!data.table:::selfrefok(mydt)) stop("mydt is corrupt.")
Das gibt uns zumindest eine Chance, das Problem Spek, aber es ist nicht sehr freundlich für die Nutzer von foo() , weil die Art und Weise, in der diese Eingänge beschädigt werden können, ziemlich undurchsichtig sein kann. Idealerweise möchte ich in der Lage sein, beschädigte Eingaben zu korrigieren und die gewünschte Funktionalität von foo() beizubehalten. Aber ich kann nicht sehen, wie, wenn ich meinen Code nicht so umstrukturiere, dass foo mydt zurückgibt und es x im aufrufenden Bereich zuweist, was möglich aber nicht ideal ist. Irgendwelche Ideen?
Ja, ich weiß, es wird eine Kopie erstellen. Die Herausforderung besteht darin, dass ich nicht garantieren kann, dass Benutzer meines Codes nicht [[<-] verwenden und somit fehlerhafte Datentabellen übergeben. Also muss ich diese Tabellen mit fehlerhaften Datentypen irgendwie so reparieren, dass das Verhalten der einzelnen Funktionen in Bezug auf die Referenz beibehalten wird, oder das Nebenreferenzmuster in foo() vermeiden. Ich fange an zu denken, dass letzteres die einzige Option ist. – pteehan
@pteehan - siehe meine Bearbeitung. – mnel