2012-10-07 27 views
24

Supongamos que tengo a crear una lista de R y anexar a la misma de la siguiente manera:¿La adición a una lista en R resulta en copia?

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

¿Es esta equivalente a

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

? No tengo tanta experiencia con los detalles particulares de cómo maneja R las listas en la memoria, pero mi entendimiento limitado es que tiende a ser feliz; lo que sería ideal para mí sería que la primera opción no implique esencialmente crear otra lista en la memoria, sino que simplemente resulte en reservar un nuevo lugar en la memoria para el valor agregado. Básicamente, si tengo una lista grande, no quiero que R haga otra copia si solo quiero añadirle algo.

Si el comportamiento que deseo no es el que se da aquí, ¿hay alguna otra forma en que pueda obtener el efecto deseado?

+4

tal vez '? Tracemem' sería útil? – Chase

+1

Y '.Interno (inspeccionar (x))' antes y después. –

Respuesta

15

Estoy bastante seguro de que la respuesta es "no". He utilizado el código siguiente a doble verificación:

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

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

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

La salida:

# 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 pertinente de ser duplications = 0.

+3

No creo que su razonamiento sea necesariamente correcto: las duplicaciones tienen un significado especial en R, y técnicamente, al extender la longitud de un vector crea una copia, no es una duplicación. Ver este hilo en R-help: http://r.789695.n4.nabble.com/Understanding-tracemem-td4636321.html – hadley

4

Se aceptó la respuesta de flodel, pero la sugerencia de Chase era buena, así que confirmé que tenía el comportamiento deseado usando su sugerencia de usar tracemem(). Aquí está el primer ejemplo, en el que sólo añadimos a la lista:

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

Y aquí está el resultado del segundo ejemplo, donde creamos dos listas:

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

para que aparezca el primer método a dar el comportamiento deseado. respuesta

10

Mateo de Dowle here y la razón de ser de mucha eficiencia de la memoria es detener la numerosa detrás de la copia escenas por <-, [<-, [[<- y otras bases R operaciones (names etc)

[[<- copiará el conjunto de x . Véase el siguiente ejemplo

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

Su segundo caso

x <- list(10,20) 

no es realmente anexando el original x pero sustituyendo x con un objeto que pasa a ser el original x con un valor adjunto.

+0

(+1), El segundo caso no se agrega, o un ejemplo de algo que yo era proponiendo, sino más bien un ejemplo de algo que no quiero que R haga entre bastidores. – guy

+0

Ahh, leí mal su pregunta, primero me leyó cuando estaba preguntando si 'x <- list (10,20)', era el equivalente (en términos de memoria) a 'x <- list (10); x [[2]] <- 20'. Al releer veo que era más matizado que eso. – mnel

+0

Sí, pero en esa respuesta vinculada 'x' era un' data.frame'. En esta pregunta, 'x' es una' lista'. El comportamiento de copia de 'list' puede ser diferente. Tenga en cuenta que no hay un método '[<-. List' pero hay un' [<-. Data.frame'. Use '.Internal (inspeccionar (x))' para verificar. –

8

Para ayudarme a determinar si la modificación de una lista hace o no una copia profunda o una copia superficial, configuré un pequeño experimento.Si la modificación de una lista hace una copia profunda, entonces debería ser más lenta cuando se está modificando una lista que contiene un objeto grande en comparación con una lista que contiene un pequeño objeto:

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 

Los tiempos de mi equipo eran básicamente idéntico, lo que sugiere que la copia de una lista hace una copia superficial, copiar punteros a las estructuras de datos existentes.

+7

'.Interno (inspeccionar (x))' es una forma más concreta de decir. Mirando para ver si la dirección hexadecimal del vector largo ha cambiado. –

+0

@MatthewDowle Nice, gracias. – hadley

Cuestiones relacionadas