2011-03-09 10 views
7

Tengo un marco de datos con 2 millones de filas y 15 columnas. Quiero agrupar por 3 de estas columnas con ddply (los 3 son factores, y hay 780,000 combinaciones únicas de estos factores), y obtener la media ponderada de 3 columnas (con pesos definidos por mi conjunto de datos). El siguiente es razonablemente rápido:¿Cómo acelerar el resumen y ddply?

system.time(a2 <- aggregate(cbind(col1,col2,col3) ~ fac1 + fac2 + fac3, data=aggdf, FUN=mean)) 
    user system elapsed 
91.358 4.747 115.727 

El problema es que quiero utilizar weighted.mean en lugar de la media para calcular mis columnas agregadas.

Si trato de la siguiente ddply en la misma trama de datos (Nota, yo echo a inmutable), el siguiente no termina después de 20 minutos:

x <- ddply(idata.frame(aggdf), 
     c("fac1","fac2","fac3"), 
     summarise, 
     w=sum(w), 
     col1=weighted.mean(col1, w), 
     col2=weighted.mean(col2, w), 
     col3=weighted.mean(col3, w)) 

Esta operación parece ser la CPU hambre, pero no muy intensivo en RAM

EDIT: Así que terminé escribiendo esta pequeña función, que "engaña" un poco al aprovechar algunas propiedades de la media ponderada y hace una multiplicación y una división en todo el objeto, en lugar de en las rebanadas.

weighted_mean_cols <- function(df, bycols, aggcols, weightcol) { 
    df[,aggcols] <- df[,aggcols]*df[,weightcol] 
    df <- aggregate(df[,c(weightcol, aggcols)], by=as.list(df[,bycols]), sum) 
    df[,aggcols] <- df[,aggcols]/df[,weightcol] 
    df 
} 

Cuando corro como:

a2 <- weighted_mean_cols(aggdf, c("fac1","fac2","fac3"), c("col1","col2","col3"),"w") 

puedo obtener un buen rendimiento, y algo reutilizable, código elegante.

+3

Hay montones y montones de consejos de optimización plyr en [esta cuestión] (http://stackoverflow.com/questions/3685492/r-speeding-up-group-by-operations). Además, no olvide que puede ejecutar 'ddply' en paralelo vinculándolo al paquete' foreach'. –

+0

He visto eso - probé los trucos que me gustaron, no los que no. En su lugar, fui con la edición anterior que usa base R, sigue siendo bastante flexible y se ejecuta rápidamente (aún menos de 2 minutos). Todavía me encantaría una explicación de por qué esto es lento en ddply. Me encantan las características de sintaxis y paralelismo. – evanrsparks

+4

'ddply' es muy lento porque funciona con marcos de datos, que lamentablemente son bastante lentos. Los enfoques más rápidos funcionan directamente con los vectores, que son mucho más rápidos – hadley

Respuesta

2

Si va a utilizar su edición, ¿por qué no utilizar rowsum y ahorrarse unos minutos de tiempo de ejecución?

nr <- 2e6 
nc <- 3 
aggdf <- data.frame(matrix(rnorm(nr*nc),nr,nc), 
        matrix(sample(100,nr*nc,TRUE),nr,nc), rnorm(nr)) 
colnames(aggdf) <- c("col1","col2","col3","fac1","fac2","fac3","w") 

system.time({ 
aggsums <- rowsum(data.frame(aggdf[,c("col1","col2","col3")]*aggdf$w,w=aggdf$w), 
    interaction(aggdf[,c("fac1","fac2","fac3")])) 
agg_wtd_mean <- aggsums[,1:3]/aggsums[,4] 
}) 
# user system elapsed 
# 16.21 0.77 16.99 
+0

Si votas esta respuesta, asegúrate de votar también [Respuesta de Marek] (http://stackoverflow.com/questions/3685492/r-speeding-up-group-by-operations/3686241#3686241). .. –

+0

Esto es agradable y eficiente, ¡gracias! Mi verdadero objetivo aquí es tener una idea de lo que hace que la operación sea tan lenta en ddply, pero creo que podría hacer un poco de perfil y averiguar por qué. – evanrsparks

5

Aunque ddply es difícil de vencer por elegancia y facilidad de código, me parece que para datos grandes, tapply es mucho más rápido. En su caso, usaría un

do.call("cbind", list((w <- tapply(..)), tapply(..))) 

Disculpe los puntos y posiblemente la comprensión errónea de la pregunta; pero estoy un poco apurado y debo tomar un autobús en menos de cinco minutos.