Perché data.table aggiorna i nomi (DT) per riferimento, anche se assegno ad un’altra variabile?

Ho memorizzato i nomi di un data.table come vector :

 library(data.table) set.seed(42) DT <- data.table(x = runif(100), y = runif(100)) names1 <- names(DT) 

Per quanto ne so, è un semplice vettore di caratteri di vaniglia:

 str(names1) # chr [1:2] "x" "y" class(names1) # [1] "character" dput(names1) # c("x", "y") 

Tuttavia, questo non è un vettore di carattere ordinario. È un vettore di carattere magico! Quando aggiungo una nuova colonna al mio data.table , questo vettore viene aggiornato!

 DT[ , z := runif(100)] names1 # [1] "x" "y" "z" 

So che questo ha qualcosa a che fare con come := aggiornamenti per incarico, ma questo mi sembra ancora magico, come mi aspetto <- per fare una copia dei nomi di data.table .

Posso risolvere il problema avvolgendo i nomi in c() :

 library(data.table) set.seed(42) DT <- data.table(x = runif(100), y = runif(100)) names1 <- names(DT) names2 <- c(names(DT)) all.equal(names1, names2) # [1] TRUE DT[ , z := runif(100)] names1 # [1] "x" "y" "z" names2 # [1] "x" "y" 

La mia domanda è duplice:

  1. Perché names1 <- names(DT) crea una copia dei nomi di data.table ? In altri casi, siamo esplicitamente avvisati che <- crea copie, sia di data.table che di data.frame s.
  2. Qual è la differenza tra names1 <- names(DT) e names2 <- c(names(DT)) ?

Aggiornamento: Questo è ora aggiunto nella documentazione per ?copy nella versione 1.9.3. Dalle NOTIZIE :

  1. Sposta ?copy nella propria pagina di help e documenta che dt_names <- copy(names(DT)) è necessario per dt_names per non essere modificato come riferimento come risultato dell'aggiornamento di DT per riferimento (es: aggiungere una nuova colonna per riferimento) . Chiude # 512 . Grazie a Zach per questa domanda SO e user1971988 per questa domanda SO .

Parte della tua prima domanda mi rende un po 'oscuro su cosa intendi per operatore <- (almeno nel contesto di data.table ), specialmente la parte: in altri casi, siamo esplicitamente avvisati che <- crea copie, sia di data.tables che di dataframes.

Quindi, prima di rispondere alla tua domanda, la touch brevemente qui. Nel caso di un data.table un <- (assegnazione) semplicemente non è sufficiente per copiare un data.table . Per esempio:

 DT <- data.table(x = 1:5, y= 6:10) # assign DT2 to DT DT2 <- DT # assign by reference, no copy taken. DT2[, z := 11:15] # DT will also have the z column 

Se vuoi creare una copy , devi menzionarla esplicitamente usando il comando copy .

 DT2 <- copy(DT) # copied content to DT2 DT2[, z := 11:15] # only DT2 is affected 

Da CauchyDistributedRV, capisco cosa intendi con i names(dt) <- . compiti names(dt) <- . ciò comporterà l'avvertimento. Lo lascerò come tale.


Ora, per rispondere alla tua prima domanda: sembra che names1 <- names(DT) si comportino allo stesso modo. Non ci avevo pensato / saputo fino ad ora. Il .Internal(inspect(.)) È molto utile qui:

 .Internal(inspect(names1)) # @7fc86a851480 16 STRSXP g0c7 [MARK,NAM(2)] (len=2, tl=100) # @7fc86a069f68 09 CHARSXP g1c1 [MARK,gp=0x61] [ASCII] [cached] "x" # @7fc86a0f96d8 09 CHARSXP g1c1 [MARK,gp=0x61] [ASCII] [cached] "y" .Internal(inspect(names(DT))) # @7fc86a851480 16 STRSXP g0c7 [MARK,NAM(2)] (len=2, tl=100) # @7fc86a069f68 09 CHARSXP g1c1 [MARK,gp=0x61] [ASCII] [cached] "x" # @7fc86a0f96d8 09 CHARSXP g1c1 [MARK,gp=0x61] [ASCII] [cached] "y" 

Qui, si vede che stanno puntando alla stessa posizione di memoria @7fc86a851480 . Anche la truelength dei names1 è 100 (che per default è allocata in data.table , controlla ?alloc.col per questo).

 truelength(names1) # [1] 100 

Quindi, in pratica, i names1 <- names(dt) sembrano accadere per riferimento. Cioè, names1 sta puntando alla stessa posizione del puntatore dei nomi delle colonne di dt.

Per rispondere alla seconda domanda: Il comando c(.) Sembra creare una copia poiché non vi è alcun controllo sul fatto che il risultato del contenuto a causa dell'operazione di concatenazione sia diverso . Cioè, dato che l'operazione c(.) Può cambiare il contenuto del vettore, ne risulta immediatamente una "copia" che viene eseguita senza verificare se i contenuti non sono modificati.