2011-04-26 16 views
10

Es bastante fácil dividir un data.frame por filas dependiendo de un factor de agrupamiento. ¿Pero cómo puedo dividir por columnas y posiblemente aplicar una función?dividir un data.frame por columnas usando una variable de agrupamiento

my.df <- data.frame(a = runif(10), 
     b = runif(10), 
     c = runif(10), 
     d = runif(10)) 
grp <- as.factor(c(1,1, 2,2)) 

Lo que me gustaría tener es una media de columnas por grupos.

Lo que tengo hasta ahora es deficiente.

lapply(as.list(as.numeric(levels(grp))), FUN = function(x, cn, data) { 
      rowMeans(data[grp %in% x]) 
     }, cn = grp, data = my.df) 

EDITAR Gracias a todos por participar. Ejecuté 10 réplicas * y mi data.frame de trabajo tiene aproximadamente 22000 filas. Estos son los resultados en segundos.

Roman: 2.19 
Joris: 4.60 
Joris #2: 3.79 #changed sapply to lapply as suggested by Joris in the [R chatroom][1]. 
Gavin: 4.70 
James & EDi: > 200 # * ran only one replicate due to the large order of magnitude difference 

Me pareció extraño que no haya una función de envoltura para la tarea en cuestión. Tal vez algún día podamos hacer

apply(X = my.df, MARGIN = 3, INDEX = my.groups, FUN = mean) # :) 
+0

Su trama de datos tiene 10 filas, y GRP tiene 4 valores. ¿Cómo se supone que coincidan? – hadley

+0

@hadley: el marco de datos tiene 4 columnas, y grp tiene 4 valores, por lo que coincide con ... –

+0

@hadley, quiero dividir por columnas, no por filas, por lo que debería coincidir con la longitud (my.df) == longitud (grp). –

Respuesta

6

Usted puede utilizar la misma lógica, pero en una forma más conveniente:

sapply(levels(grp),function(x)rowMeans(my.df[which(grp==x)])) 
+0

Esa versión es ~ x2 más rápida que la que le mostré a Joris. La indexación usualmente es+1 –

0

¿Esto funciona?

aggregate(t(my.df), list(grp), mean) 
+0

+1 buen uso de agregado! – Ramnath

+0

Este método ignora silenciosamente el hecho de que el vector grp no tiene la longitud correcta. – hadley

0

¿Qué tal:

my.df2 <- data.frame(t(my.df),grp) 
aggregate(.~grp,my.df2,mean) 
+0

Lo siento, pero esta solución lleva demasiado tiempo en mi conjunto de datos en funcionamiento (ver mi respuesta actualizada). –

5

Convertir my.df a una lista y dividir que, a continuación, aplicar su función a cada subconjunto de componentes de la lista, después de coaccionar a una trama de datos:

lapply(split(as.list(my.df), grp), function(x) rowMeans(as.data.frame(x))) 

Esto da:

> lapply(split(as.list(my.df), grp), function(x) rowMeans(as.data.frame(x))) 
$`1` 
[1] 0.8229189 0.4901288 0.2057578 0.6531641 0.3897858 0.4225179 
[7] 0.3905410 0.3928784 0.1715857 0.3973192 

$`2` 
[1] 0.61348623 0.61229702 0.31938521 0.28325342 0.25857158 
[6] 0.49071991 0.01179999 0.57639186 0.38407240 0.17467337 

lo que equivale a @ "se aplica de hombre pobre" de Roman:

> roman <- lapply(as.list(as.numeric(levels(grp))), 
+     FUN = function(x, cn, data) { 
+      rowMeans(data[grp %in% x]) 
+     }, cn = grp, data = my.df) 
> gavin <- lapply(split(as.list(my.df), grp), 
+     function(x) rowMeans(as.data.frame(x))) 
> all.equal(roman, gavin) 
[1] "names for current but not for target" 

a excepción de los nombres de los componentes.

+0

Este método ignora silenciosamente el hecho de que el vector grp no tiene la longitud correcta. – hadley

+0

@hadley ¿cuál no es la longitud correcta? 'grp' es de longitud 4 y' as.list (my.df) 'también es de longitud 4. ¿Por qué esto no es equivalente a' split (1: 4, grp) '? Una lista es un vector después de todo. –

+0

@hadley dado su comentario a la pregunta Q, creo que ha entendido mal lo que se requería. –

Cuestiones relacionadas