2010-10-19 16 views
8

Tengo un hoja.de.datos (link to file) con 18 columnas y 11520 filas que transformo como esto:la forma de acelerar el código R

library(plyr) 
df.median<-ddply(data, .(groupname,starttime,fPhase,fCycle), 
       numcolwise(median), na.rm=TRUE) 

según system.time(), que toma alrededor de este long to run:

user system elapsed 
    5.16 0.00 5.17 

Esta llamada es parte de una aplicación web, por lo que el tiempo de ejecución es bastante importante. ¿Hay alguna manera de acelerar esta llamada?

+2

¿Puede cachear los resultados? – Shane

+0

'ddply()' es antes que nada * conveniente *. Si necesita algo rápido, puede que necesite volver a implementar la lógica. –

+0

@Shane: actualmente hay 3 * 400 conjuntos de datos posibles (y aumentados diariamente) que un usuario podría solicitar. Es poco probable que un usuario acierte en el mismo conjunto de datos que otro. Entonces el almacenamiento en caché solo sería útil dentro de una sesión. Como el resultado de la aplicación web es esencialmente un informe enlatado, no creo que el usuario lo solicite más de una vez. ¿Implementarías el almacenamiento en caché para la situación que he descrito? Nunca lo había hecho antes, así que estoy un poco perdido. – dnagirl

Respuesta

9

Simplemente usando aggregate es un poco más rápido ...

> groupVars <- c("groupname","starttime","fPhase","fCycle") 
> dataVars <- colnames(data)[ !(colnames(data) %in% c("location",groupVars)) ] 
> 
> system.time(ag.median <- aggregate(data[,dataVars], data[,groupVars], median)) 
    user system elapsed 
    1.89 0.00 1.89 
> system.time(df.median <- ddply(data, .(groupname,starttime,fPhase,fCycle), numcolwise(median), na.rm=TRUE)) 
    user system elapsed 
    5.06 0.00 5.06 
> 
> ag.median <- ag.median[ do.call(order, ag.median[,groupVars]), colnames(df.median)] 
> rownames(ag.median) <- 1:NROW(ag.median) 
> 
> identical(ag.median, df.median) 
[1] TRUE 
+0

'aggregate' soluciona este problema a la perfección. – dnagirl

7

Sólo para resumir algunos de los puntos de los comentarios:

  1. Antes de empezar a optimizar, usted debe tener algún sentido para un rendimiento "aceptable". Según el rendimiento requerido, puede ser más específico sobre cómo mejorar el código. Por ejemplo, en algún umbral, necesitaría dejar de usar R y pasar a un lenguaje compilado.
  2. Una vez que tenga un tiempo de ejecución esperado, puede crear un perfil de su código existente para encontrar posibles cuellos de botella. R tiene varios mecanismos para esto, incluido Rprof (hay ejemplos en stackoverflow si search for [r] + rprof).
  3. plyr está diseñado principalmente para la facilidad de uso, no para el rendimiento (aunque la versión reciente tuvo algunas mejoras de rendimiento agradables). Algunas de las funciones básicas son más rápidas porque tienen menos sobrecarga. @JDLong señaló a nice thread que cubre algunos de estos problemas, incluidas algunas técnicas especializadas de Hadley.
+0

Gracias por el resumen. Y gracias a todos los que contribuyeron con información tan útil. ¡Tengo muchas lecturas para hacer! – dnagirl

2

Bueno, yo acabo de hacer unas cuantas transformaciones sencillas sobre una trama de datos de gran tamaño (los datos de béisbol establecidos en el paquete plyr) utilizando el estándar funciones de biblioteca (p. ej., 'tabla', 'aplicar', 'agregar', etc.) y la función plyr análoga: en cada instancia, encontré que plyr es significativamente más lenta. Por ejemplo,

> system.time(table(BB$year)) 
    user system elapsed 
    0.007 0.002 0.009 

> system.time(ddply(BB, .(year), 'nrow')) 
    user system elapsed 
    0.183 0.005 0.189 

En segundo lugar, y lo hice no investigar si esto mejoraría el rendimiento en su caso, pero para las tramas de datos del tamaño de los que está trabajando ahora y más grande, yo uso la biblioteca data.table, disponible en CRAN. Es fácil de crear objetos data.table, así como para convertir data.frames existentes a data.tables - sólo llame data.table en el hoja.de.datos desea convertir:

dt1 = data.table(my_dataframe) 
3

Para añadir a Joshua de solución. Si decide utilizar significa lugar de la mediana, se puede acelerar el cálculo de otras 4 veces:

> system.time(ag.median <- aggregate(data[,dataVars], data[,groupVars], median)) 
    user system elapsed 
    3.472 0.020 3.615 
> system.time(ag.mean <- aggregate(data[,dataVars], data[,groupVars], mean)) 
    user system elapsed 
    0.936 0.008 1.006 
+1

muy interesante! Lo tendré en mente. Desafortunadamente, esta información tiene que comparar medianas. – dnagirl

4

El orden de los datos importa cuando se está calculando las medianas: si los datos están en orden de menor a mayor, entonces el cálculo es un poco más rápido.

x <- 1:1e6 
y <- sample(x) 
system.time(for(i in 1:1e2) median(x)) 
    user system elapsed 
    3.47 0.33 3.80 

system.time(for(i in 1:1e2) median(y)) 
    user system elapsed 
    5.03 0.26 5.29 

Para los nuevos conjuntos de datos, ordene los datos por una columna apropiada al importarlos. Para los conjuntos de datos existentes, puede ordenarlos como un trabajo por lotes (fuera de la aplicación web).

2

Trabajar con estos datos es considerablemente más rápido con dplyr:

library(dplyr) 

system.time({ 
    data %>% 
    group_by(groupname, starttime, fPhase, fCycle) %>% 
    summarise_each(funs(median(., na.rm = TRUE)), inadist:larct) 
}) 
#> user system elapsed 
#> 0.391 0.004 0.395 

(que necesitará para conseguir dplyr 0,2 %>% y summarise_each)

Esto se compara favorable para plyr:

library(plyr) 
system.time({ 
    df.median <- ddply(data, .(groupname, starttime, fPhase, fCycle), 
    numcolwise(median), na.rm = TRUE) 
}) 
#> user system elapsed 
#> 0.991 0.004 0.996 

Y a aggregate() (código de @ joshua-ulrich)

groupVars <- c("groupname", "starttime", "fPhase", "fCycle") 
dataVars <- colnames(data)[ !(colnames(data) %in% c("location", groupVars))] 
system.time({ 
    ag.median <- aggregate(data[,dataVars], data[,groupVars], median) 
}) 
#> user system elapsed 
#> 0.532 0.005 0.537 
Cuestiones relacionadas