2014-04-01 2 views
6

Sto aggiornando una serie di funzioni che in precedenza accettavano solo gli oggetti data.frame per lavorare con gli argomenti data.table.Viene eseguita una copia quando la funzione restituisce un data.table?

Ho deciso di implementare la funzione utilizzando l'invio del metodo di R in modo che il vecchio codice che utilizza data.frame s continui a funzionare con le funzioni aggiornate. In una delle mie funzioni, prendo un data.frame come input, lo modifico e restituisco lo data.frame modificato. Ho creato anche un'implementazione data.table. Per esempio:

# The functions 
foo <- function(d) { 
    UseMethod("foo") 
} 

foo.data.frame <- function(d) { 
    <Do Something> 
    return(d) 
} 

foo.data.table <- function(d) { 
    <Do Something> 
    return(d) 
} 

So che data.table opere apportare modifiche senza copiare, e ho implementato foo.data.table tenendo questo in mente. Tuttavia, restituisco l'oggetto data.table alla fine della funzione perché voglio che i miei vecchi script funzionino con i nuovi oggetti data.table. Questo farà una copia dello data.table? Come posso controllare? Secondo la documentazione, uno deve essere molto esplicito per creare una copia di uno data.table, ma non sono sicuro in questo caso.

Il motivo che voglio restituire qualcosa quando io non devo con data.tables:

miei vecchi script simile a questa

someData <- read.table(...) 
... 
someData <- foo(someData) 

voglio gli script per essere in grado di correre con data.table s da basta cambiare le righe di ingest dei dati. In altre parole, voglio che lo script funzioni semplicemente cambiando someData <- read.table(...) a someData <- fread(...).

+1

Solo le funzioni ': =' e 'set *' modificano 'data.table' per riferimento. Quindi, se stai usando ': =' o una qualsiasi delle funzioni di 'set *' all'interno della tua funzione, potresti dover lavorare su 'copy (d)' se non vuoi che 'd' sia modificato come riferimento . Per esempio: se lo fai: 'ans <- d [, lapply (.SD, sum), by = cols]', allora non c'è nulla di cambiato in riferimento qui. – Arun

+0

@Arun Scusa se la mia domanda non è chiara. Voglio che 'd' sia modificato per riferimento, quindi ho fatto uso delle funzioni': = 'e' set * 'per modificare' data.table' nella mia funzione 'foo.data.table'. Se modifico 'd' all'interno della funzione per riferimento, allora' return (d) 'restituisce una copia di' d'? – ialm

+1

no, non è così .. puoi usare 'tracemem' per controllare queste cose in generale. Es: 'foo <- function (x) {x [, bar: = 1L]; return (x)}; x = data.table (a = 1: 5, b = 6: 10); tracemem (x); foo (x) '. Risulterà in alcuni verbose dopo 'foo (x)' se è stata fatta una copia. In alternativa puoi usare la funzione 'address()' per verificare se le colonne di 'x' hanno lo stesso indirizzo prima e dopo la funzione .. e se è così, nessuna copia è stata fatta. – Arun

risposta

5

Grazie ad Arun per la sua risposta nei commenti. Userò il suo esempio nei suoi commenti per rispondere alla domanda.

Si può controllare se le copie sono stati fatti utilizzando la funzione tracemem di tracciare un oggetto in R. Dal file aiuto della funzione, ?tracemem, la descrizione dice:

Questa funzione segna un oggetto in modo che un messaggio viene stampato ogni volta che il codice interno copia l'oggetto. È una delle principali cause di difficili da prevedere l'utilizzo della memoria in R.

Ad esempio:

# Using a data.frame 
df <- data.frame(x=1:5, y=6:10) 
tracemem(df) 
## [1] "<0x32618220>" 
df$y[2L] <- 11L 
## tracemem[0x32618220 -> 0x32661a98]: 
## tracemem[0x32661a98 -> 0x32661b08]: $<-.data.frame $<- 
## tracemem[0x32661b08 -> 0x32661268]: $<-.data.frame $<- 
df 
## x y 
## 1 1 6 
## 2 2 11 
## 3 3 8 
## 4 4 9 
## 5 5 10 

# Using a data.table 
dt <- data.table(x=1:5, y=6:10) 
tracemem(dt) 
## [1] "<0x5fdab40>" 
set(dt, i=2L, j=2L, value=11L) # No memory output! 
address(dt) # Verify the address in memory is the same 
## [1] "0x5fdab40" 
dt 
## x y 
## 1: 1 6 
## 2: 2 11 
## 3: 3 8 
## 4: 4 9 
## 5: 5 10 

Sembra che l'oggetto data.frame viene copiato due volte quando si cambia uno degli elementi della data.frame, mentre lo data.table viene modificato sul posto senza fare copie!

Dalla mia domanda, posso solo rintracciare l'oggetto data.table o data.frame, d, prima di passarlo alla funzione, foo, per verificare se sono state fatte tutte le copie.

+2

Eccellente! Puoi anche controllare l'indirizzo '(dt $ x)'. – Arun

+0

potrebbe anche voler controllare quando il valore di ritorno è memorizzato, quindi someData = foo (someData) –

3

Non sono sicuro questo aggiunge nulla, ma come un ammonimento nota il seguente comportamento:

library(data.table) 
foo.data.table <- function(d) { 
    d[,A:=4] 
    d$B <- 1 
    d[,C:=1] 
    return(d) 
} 
set.seed(1) 
dt  <- data.table(A=rnorm(5),B=runif(5),C=rnorm(5)) 
dt 
#    A   B   C 
# 1: -0.6264538 0.2059746 -0.005767173 
# 2: 0.1836433 0.1765568 2.404653389 
# 3: -0.8356286 0.6870228 0.763593461 
# 4: 1.5952808 0.3841037 -0.799009249 
# 5: 0.3295078 0.7698414 -1.147657009 
result <- foo.data.table(dt) 
dt 
# A   B   C 
# 1: 4 0.2059746 -0.005767173 
# 2: 4 0.1765568 2.404653389 
# 3: 4 0.6870228 0.763593461 
# 4: 4 0.3841037 -0.799009249 
# 5: 4 0.7698414 -1.147657009 
result 
# A B C 
# 1: 4 1 1 
# 2: 4 1 1 
# 3: 4 1 1 
# 4: 4 1 1 
# 5: 4 1 1 

Quindi, evidentemente, dt è passato con riferimento al foo.data.table(...) e la prima affermazione, d[,A:=4], lo modifica per riferimento, cambio colonna A in dt.

La seconda affermazione, d$B <- 1, costringe la creazione di una copia di d (anche chiamato d) ambito interno alla funzione. Quindi la terza istruzione, d[,C:=1], modifica per riferimento (ma non influenza dt) e return(d) quindi restituisce la copia.

Se si modifica l'ordine delle seconde e terze istruzioni, l'effetto della chiamata di funzione su dt è diverso.

+2

Il modo in cui ho capito la domanda è, supponendo che la funzione - fino al punto di 'return (.)' - non abbia un 'copia', fa solo l'istruzione' return (.) 'risulta ancora in una copia ... – Arun

+1

Penso che tu sollevi un punto importante. Il codice risultante dalla mia funzione è un po 'ambiguo, dal momento che sto codificando con effetti collaterali e non richiede l'operatore '<-' perché sto modificando il' d' sul posto (cioè 'foo (d)' è sufficiente, ma ho usato 'd <-foo (d)'). Ho appena avuto un caso speciale di voler conformarmi al mio vecchio codice base. Ho fatto attenzione a usare le funzioni di 'data.table' per modificare i valori e creare colonne nella mia funzione in modo che nessuna copia sia composta fino al punto di' return'. Ero solo incerto sul comportamento di 'return'ing su un oggetto' data.table'. – ialm