In data.table
, :=
e tuttiset*
oggetti funzioni di aggiornamento di riferimento. Questo è stato introdotto intorno al 2012 IIRC. E in questo momento, la base R non ha la copia superficiale, ma profonda copiata. Shallow copia è stata introdotta dalla 3.1.0.
Si tratta di una risposta prolisso/lungo, ma penso che questo risponde alle vostre prime due domande:
Come è questo metodo R base diversa dal equivalente data.table? La differenza riguarda solo la velocità o anche l'utilizzo della memoria?
In base di R + v3.1.0 quando facciamo:
DF1 = data.frame(x=1:5, y=6:10, z=11:15)
DF2 = DF1[, c("x", "y")]
DF3 = transform(DF2, y = ifelse(y>=8L, 1L, y))
DF4 = transform(DF2, y = 2L)
- Da
DF1
a DF2
, entrambe le colonne sono solo superficiale copiati.
- Da
DF2
al DF3
colonna y
solo doveva essere copiato/riassegnazione, ma x
ottiene superficiale copiato nuovamente.
- Da
DF2
a DF4
, uguale a (2).
Ovvero, le colonne vengono copiate in modo superficiale finché la colonna rimane invariata - in un modo, la copia viene ritardata a meno che non sia assolutamente necessario.
In data.table
, si modifica sul posto. Il significato anche durante la colonna DF3
e DF4
non viene copiato nella colonna .
DT2[y >= 8L, y := 1L] ## (a)
DT2[, y := 2L]
Qui, dal y
è già una colonna integer, e ci sono modificandola da intero, per riferimento, non c'è nuovo allocazione di memoria fatta qui a tutti.
Questo è anche particolarmente utile quando si desidera assegnare sotto il numero di telefono per riferimento (contrassegnato come (a) sopra). Questa è una funzionalità utile che ci piace molto in data.table
.
Un altro vantaggio che viene fornito gratuitamente (che sono venuto a sapere dai nostri interazioni) è, quando abbiamo, per esempio, convertire tutte le colonne di una data.table a un tipo numeric
, da dire, character
Tipo:
DT[, (cols) := lapply(.SD, as.numeric), .SDcols = cols]
Qui, dal momento che stiamo aggiornando per riferimento, ogni colonna personaggio ottiene sostituito per riferimento con il suo omologo numerico. E dopo quella sostituzione, la precedente colonna di caratteri non è più richiesta ed è in palio per la garbage collection. Ma se si dovesse farlo usando di base R:
DF[] = lapply(DF, as.numeric)
Tutte le colonne dovranno essere convertite a numerico, e che dovrete essere tenuto in un temporanea variabile, e poi finalmente verranno assegnati di nuovo a DF
. Ciò significa che, se hai 10 colonne con 100 milioni di righe, ciascuna di tipo di carattere, allora il vostro DF
prende uno spazio di:
10 * 100e6 * 4/1024^3 = ~ 3.7GB
E poiché numeric
tipo è due volte tanto in termini di dimensioni, avremo bisogno un totale di 7.4GB + 3.7GB
di spazio per noi per fare la conversione con base di R.
ma nota che data.table
copie durante DF1
-DF2
. Cioè:
DT2 = DT1[, c("x", "y"), with=FALSE]
risultati in una copia, perché non possiamo sub-assegnare da riferimento un superficiale copia . Aggiornerà tutti i cloni.
Ciò che sarebbe bello se potessimo integrare senza problemi la funzione di copia superficiale, ma tenere traccia di se le colonne di un particolare oggetto ha più riferimenti e aggiornare per riferimento ove possibile. La funzione di conteggio dei riferimenti aggiornata di R potrebbe essere molto utile a questo riguardo. In ogni caso, ci stiamo adoperando.
Per la vostra ultima domanda: "Quando è la differenza più consistente"
Non ci sono ancora persone che devono utilizzare le vecchie versioni di R, in cui le copie profonde non possono essere evitati.
Dipende da quante colonne vengono copiate perché le operazioni eseguite su di esso. Lo scenario peggiore sarebbe che hai copiato tutte le colonne, ovviamente.
Ci sono casi come this in cui la copia superficiale non ne trarrà beneficio.
Quando si desidera aggiornare le colonne di un data.frame per ogni gruppo e ci sono troppi gruppi.
Quando si desidera aggiornare una colonna di dire, data.table DT1
sulla base di un join con un altro data.table DT2
- questo può essere fatto come:
DT1[DT2, col := i.val]
dove i.
si riferisce al valore dalla colonna val
di DT2
(argomento i
) per le righe corrispondenti. Questa sintassi consente di eseguire questa operazione in modo molto efficiente, invece di dover prima aggiungere l'intero risultato e quindi aggiornare la colonna richiesta.
Tutto sommato, ci sono forti argomenti in cui aggiornamento con riferimento permetterebbe di risparmiare un sacco di tempo, e di essere veloce. Ma a volte le persone non amano aggiornare gli oggetti sul posto e sono disposti a sacrificare la velocità/la memoria per questo. Stiamo cercando di capire come offrire al meglio questa funzionalità, oltre all'aggiornamento già esistente per riferimento.
Spero che questo aiuti. Questa è già una risposta abbastanza lunga. Lascerò tutte le domande che potresti aver lasciato agli altri o per te a capire (a parte qualsiasi idea sbagliata in questa risposta).