2012-10-07 8 views
24

Supponiamo che io sono creare un elenco in R e accodare ad esso come segue:L'aggiunta a un elenco in R comporta la copia?

x = list(10) 
x[[2]] = 20 

È questo equivale a

x = list(10) 
x = list(10, 20) 

? Non sono così esperto con i dettagli particolari di come R gestisce gli elenchi in memoria, ma la mia comprensione limitata è che tende ad essere felice per la copia; quello che sarebbe l'ideale per me sarebbe che la prima opzione non implicasse essenzialmente la creazione di un altro elenco in memoria, ma si traducesse in un nuovo posto in memoria per il valore aggiunto. Essenzialmente, se ho una grande lista, non voglio che R facciane un'altra copia se voglio solo aggiungervi qualcosa.

Se il comportamento che voglio non è quello che viene dato qui, c'è un altro modo in cui posso ottenere l'effetto desiderato?

+4

forse '? Tracemem' sarebbe utile? – Chase

+1

E '.Internal (inspect (x))' prima e dopo. –

risposta

15

Sono abbastanza fiducioso che la risposta è "no". Ho usato il seguente codice di doppio controllo:

Rprof(tmp <- tempfile(), memory.profiling = TRUE) 

x <- list() 
for (i in 1:100) x[[i]] <- runif(10000) 

Rprof() 
summaryRprof(tmp, memory = "stats") 
unlink(tmp) 

L'output:

# index: runif 
#  vsize.small max.vsize.small  vsize.large max.vsize.large 
#   76411   381781   424523   1504387 
#   nodes  max.nodes  duplications tot.duplications 
#   2725878   13583136    0    0 
#   samples 
#    5 

La parte rilevante essendo duplications = 0.

+3

Non penso che il tuo ragionamento sia necessariamente corretto: le duplicazioni hanno un significato speciale in R, e tecnicamente, mentre estendendo la lunghezza di un vettore crea una copia, non è una duplicazione. Vedi questo thread su R-help: http://r.789695.n4.nabble.com/Understanding-tracemem-td4636321.html – hadley

4

La risposta di flodel accettata, ma il suggerimento di Chase era buono, quindi ho confermato di avere il comportamento desiderato utilizzando il suo suggerimento di utilizzare tracemem(). Ecco il primo esempio, dove ci basta aggiungere alla lista:

x = list(10) 
tracemem(x[[1]]) 
# [1] "<0x2d03fa8>" #(likely different on each machine) 
x[[2]] = 20 
tracemem(x[[1]]) 
# [1] "<0x2d03fa8>" 

E qui è il risultato dal secondo esempio, dove creiamo due liste:

x = list(10) 
tracemem(x[[1]]) 
# [1] "<0x2d03c78>" 
x = list(10, 20) 
tracemem(x[[1]]) 
# [1] "<0x2d07ff8>" 

Quindi il primo metodo sembra dare il comportamento desiderato risposta

10

Matteo Dowle here e la logica alla base molto l'efficienza della memoria è quello di fermare i numerosi dietro le quinte di copia da <-, [<-, [[<- e altre R operazioni di base (names ecc)

[[<- copierà l'intera x . Vedere l'esempio qui sotto

x <- list(20) 
tracemem(x) 
#[1] "<0x2b0e2790>" 
x[[2]] <- 20 
# tracemem[0x2b0e2790 -> 0x2adb7798]: 

Il secondo caso

x <- list(10,20) 

non è realmente aggiungendo dell'originale x ma sostituendo x con un oggetto che sembra essere l'originale x con un valore aggiunto.

+0

(+1), Il secondo caso non si aggiunge, o un esempio di qualcosa che ero proponendo, ma piuttosto un esempio di qualcosa che non voglio che R faccia dietro le quinte. – guy

+0

Ah, ho letto male la tua domanda, prima mi leggeva mentre stavi chiedendo se 'x <- list (10,20)', era l'equivalente (in termini di memoria) a 'x <- list (10); x [[2]] <- 20'. Rileggendo vedo che era più sfumato di così. – mnel

+0

Sì, ma in quella risposta collegata 'x' era un' data.frame'. In questa domanda 'x' è una' lista'. La copia del comportamento di 'list' può essere diversa. Nota che non esiste il metodo '[<-. List' ma esiste un' [<-. Data.frame'. Usa '.Internal (inspect (x))' per controllare. –

8

Per aiutarmi a capire se la modifica di un elenco è o meno eseguita in una copia profonda o in una copia poco profonda, ho impostato un piccolo esperimento.Se la modifica di un elenco fa una copia completa, allora dovrebbe essere più lento quando si sta modificando un elenco che contiene un oggetto di grandi dimensioni rispetto a un elenco che contiene un piccolo oggetto:

z1 <- list(runif(1e7)) 
z2 <- list(1:10) 

system.time({ 
    for(i in 1:1e4) z1[1 + i] <- 1L 
}) 
# user system elapsed 
# 0.283 0.034 0.317 
system.time({ 
    for(i in 1:1e4) z2[1 + i] <- 1L 
}) 
# user system elapsed 
# 0.284 0.034 0.319 

I tempi sul mio computer sono stati sostanzialmente identico, suggerendo che copiare una lista fa una copia superficiale, copiando i puntatori a strutture di dati esistenti.

+7

'.Internal (inspect (x))' è un modo più concreto per dirlo. Cercando di vedere se l'indirizzo esadecimale del vettore lungo è cambiato. –

+0

@MatthewDowle Bello, grazie. – hadley