2010-10-25 5 views
6

Como SO está un poco lento últimamente, estoy publicando una pregunta fácil. Agradecería que peces grandes se quedaran en el banquillo para esta oportunidad y les dieran la oportunidad a los novatos de responder.desafío: optimizar la desinstalación [fácil]

A veces tenemos objetos que tienen una cantidad ridícula de elementos grandes de la lista (vectores). ¿Cómo "deslistaría" este objeto en un solo vector? Demuestre que su método es más rápido que unlist().

+8

Todo el mundo es un "pez gordo" aquí;). Usted corre el riesgo de no obtener ninguna respuesta. – VitoshKa

+0

¿Qué tan grande es grande? ¿Estamos hablando de salmón, marlin o tiburón ballena? – James

+0

¿Qué significa "una cantidad ridícula de elementos de la lista grande" - vectores de longitud de 1,000,000 o más? ¿Cuántos elementos de la lista es una cantidad "ridícula"? –

Respuesta

6

Si no necesita nombres y su lista es un nivel de profundidad, entonces si puedes vencer

.Internal(unlist(your_list, FALSE, FALSE)) 

votaré a todo lo que haces en SO para el próximo 1 año !!!

[Actualización: si se necesitan nombres no únicos y la lista no es recursivo, aquí es una versión que mejora el no listados 100 veces

myunlist <- function(l){ 
    names <- names(l) 
    vec <- unlist(l, F, F) 
    reps <- unlist(lapply(l, length), F, F) 
    names(vec) <- rep(names, reps) 
    vec 
    } 

myunlist(list(a=1:3, b=2)) 
a a a b 
1 2 3 2 

> tl <- list(a = 1:20000, b = 1:5000, c = 2:30) 
> system.time(for(i in 1:200) unlist(tl)) 
user system elapsed 
22.97 0.00 23.00 

> system.time(for(i in 1:200) myunlist(tl)) 
user system elapsed 
0.2  0.0  0.2 

> system.time(for(i in 1:200) unlist(tl, F, F)) 
user system elapsed 
0.02 0.00 0.02 

]

[Actualización 2: Responce para desafiar Nr3 de Richie Cotton.

bigList3 <- replicate(500, rnorm(1e3), simplify = F) 

unlist_vit <- function(l){ 
    names(l) <- NULL 
    do.call(c, l) 
    } 

library(rbenchmark) 

benchmark(unlist = unlist(bigList3, FALSE, FALSE), 
      rjc = unlist_rjc(bigList3), 
      vit = unlist_vit(bigList3), 
      order = "elapsed", 
      replications = 100, 
      columns = c("test", "relative", "elapsed") 
     ) 

    test relative elapsed 
1 unlist 1.0000 2.06 
3 vit 1.4369 2.96 
2 rjc 3.5146 7.24 

]

PD: suponer una "gran pez" es el que tiene más prestigio que tú. Así que soy bastante pequeño aquí :).

+0

+1 Probablemente no sea significativo, pero en mi prueba tu versión fue un poco más rápida que @ucfagls '. Sin embargo, la mayor aceleración se obtiene de use.names = F. – mbq

+0

Comprendí por la pregunta de Roman que su deseo era reemplazar el "unlist" incorporado por algo más inteligente. En lo que a mí respecta, esto no es posible cuando los nombres no son necesarios. – VitoshKa

+0

¡Esa es una oferta que una persona no puede rechazar! Si escribo 6 artículos al día, puedo tener posibilidades de atrapar a Dirk. FWIW, considero peces grandes personas con varios 1000 puntos. –

2

Una solución no unlist() tendría que ser bastante rápida para superar unlist() ¿no es así? Aquí lleva menos de dos segundos deslistar una lista con 2000 vectores numéricos de longitud 100.000 cada uno.

> bigList2 <- as.list(data.frame(matrix(rep(rnorm(1000000), times = 200), 
+          ncol = 2000))) 
> print(object.size(bigList2), units = "Gb") 
1.5 Gb 
> system.time(foo <- unlist(bigList2, use.names = FALSE)) 
    user system elapsed 
    1.897 0.000 2.019 

Con bigList2 y foo en mi espacio de trabajo, R es el uso de ~ 9Gb de mi memoria disponible. La clave es use.names = FALSE. Sin él, unlist() es dolorosamente lento. Exactamente lo lento Todavía estoy esperando para averiguar ...

Nos puede acelerar este proceso un poco más mediante el establecimiento de recursive = FALSE y entonces tenemos efectivamente el mismo que la respuesta de Vitoshka (dos tiempos) representativos:

> system.time(foo <- unlist(bigList2, recursive = FALSE, use.names = FALSE)) 
    user system elapsed 
    1.379 0.001 1.416 
> system.time(foo <- .Internal(unlist(bigList2, FALSE, FALSE))) 
    user system elapsed 
    1.335 0.000 1.344 

... finalmente la versión use.names = TRUE terminado ...:

> system.time(foo <- unlist(bigList2, use = TRUE)) 
    user system elapsed 
2307.839 10.978 2335.815 

y se consume todos mis sistemas de 16 GB de RAM por lo que me di por vencido en ese punto ...

0

Como un pez de tamaño medio, estoy saltando con una solución de primer intento que da un punto de referencia para los peces pequeños para vencer. Es aproximadamente 3 veces más lento que unist.

Estoy usando una versión más pequeña de la lista de prueba de ucfagls. (Ya que cabe en la memoria mejor.)

bigList3 <- as.list(data.frame(matrix(rep(rnorm(1e5), times = 200), ncol = 2000))) 

La idea básica es crear un vector largo para almacenar la respuesta, entonces bucle sobre elementos de la lista de copiar los valores de la lista.

unlist_rjc <- function(l) 
{ 
    lengths <- vapply(l, length, FUN.VALUE = numeric(1), USE.NAMES = FALSE) 
    total_len <- sum(lengths) 
    end_index <- cumsum(lengths) 
    start_index <- 1 + c(0, end_index) 
    v <- numeric(total_len) 
    for(i in seq_along(l)) 
    { 
    v[start_index[i]:end_index[i]] <- l[[i]] 
    } 
    v 
} 

t1 <- system.time(for(i in 1:10) unlist(bigList2, FALSE, FALSE)) 
t2 <- system.time(for(i in 1:10) unlist_rjc(bigList2)) 
t2["user.self"]/t1["user.self"] # 3.08 

retos para los pequeños peces:
1. ¿Puede extenderlo a tratar con otros tipos que numérico?
2. ¿Puede hacer que funcione con recursión (listas anidadas)?
3. ¿Puedes hacerlo más rápido?

Rechazaré a alguien con menos puntos que yo, cuya respuesta cumpla con uno o más de estos mini-desafíos.

+0

De tamaño mediano? De acuerdo con [clasificación de usuarios principales en la etiqueta R] (http://stackoverflow.com/tags/r/topusers), usted es el décimo respondedor en los últimos 30 días, y el 7º todo el tiempo;) – Marek

+0

Wow. No se había dado cuenta de eso. Me yay. De todos modos, mi oferta sigue en pie. Mejorar/vencer mi respuesta -> obtener un voto positivo. –

+0

Además, el desafío 1 es fácil. –

Cuestiones relacionadas