r - img - tags$style shiny
Uniendo valores agregados de nuevo al marco de datos original (5)
Esta pregunta ya tiene una respuesta aquí:
Uno de los patrones de diseño que uso una y otra vez es realizar "agrupar por" o "dividir, aplicar, combinar (SAC)" en un marco de datos y luego unir los datos agregados a los datos originales. Esto es útil, por ejemplo, cuando se calcula la desviación de cada condado de la media estatal en un marco de datos con muchos estados y condados. Rara vez mi cálculo agregado es solo un medio simple, pero es un buen ejemplo. A menudo resuelvo este problema de la siguiente manera:
require(plyr)
set.seed(1)
## set up some data
group1 <- rep(1:3, 4)
group2 <- sample(c("A","B","C"), 12, rep=TRUE)
values <- rnorm(12)
df <- data.frame(group1, group2, values)
## got some data, so let''s aggregate
group1Mean <- ddply( df, "group1", function(x)
data.frame( meanValue = mean(x$values) ) )
df <- merge( df, group1Mean )
df
Que produce buenos datos agregados como los siguientes:
> df
group1 group2 values meanValue
1 1 A 0.48743 -0.121033
2 1 A -0.04493 -0.121033
3 1 C -0.62124 -0.121033
4 1 C -0.30539 -0.121033
5 2 A 1.51178 0.004804
6 2 B 0.73832 0.004804
7 2 A -0.01619 0.004804
8 2 B -2.21470 0.004804
9 3 B 1.12493 0.758598
10 3 C 0.38984 0.758598
11 3 B 0.57578 0.758598
12 3 A 0.94384 0.758598
Esto funciona, pero ¿hay formas alternativas de hacer esto que mejoren la legibilidad, el rendimiento, etc.?
¿No puedes simplemente agregar x
a la función que pasas a ddply
?
df <- ddply( df, "group1", function(x)
data.frame( x, meanValue = mean(x$values) ) )
Creo que ave()
es más útil aquí que plyr call you show (no estoy lo suficientemente familiarizado con plyr para saber si puede hacer lo que quiera con plyr directamente o no, ¡me sorprendería si no puede!) o las otras alternativas de base R ( aggregate()
, tapply()
) .:
> with(df, ave(values, group1, FUN = mean))
[1] -0.121033381 0.004803931 0.758597929 -0.121033381 0.004803931
[6] 0.758597929 -0.121033381 0.004803931 0.758597929 -0.121033381
[11] 0.004803931 0.758597929
Puede utilizar within()
o transform()
para incrustar este resultado directamente en df
:
> df2 <- within(df, meanValue <- ave(values, group1, FUN = mean))
> head(df2)
group1 group2 values meanValue
1 1 A 0.4874291 -0.121033381
2 2 B 0.7383247 0.004803931
3 3 B 0.5757814 0.758597929
4 1 C -0.3053884 -0.121033381
5 2 A 1.5117812 0.004803931
6 3 C 0.3898432 0.758597929
> df3 <- transform(df, meanValue = ave(values, group1, FUN = mean))
> all.equal(df2,df3)
[1] TRUE
Y si el pedido es importante:
> head(df2[order(df2$group1, df2$group2), ])
group1 group2 values meanValue
1 1 A 0.48742905 -0.121033381
10 1 A -0.04493361 -0.121033381
4 1 C -0.30538839 -0.121033381
7 1 C -0.62124058 -0.121033381
5 2 A 1.51178117 0.004803931
11 2 A -0.01619026 0.004803931
En términos de rendimiento, puede realizar este mismo tipo de operación utilizando el paquete data.table
, que se ha agregado en forma agregada y es muy rápido gracias a los índices y una implementación basada en C. Por ejemplo, dado df
ya existe desde su ejemplo:
library("data.table")
dt<-as.data.table(df)
setkey(dt,group1)
dt<-dt[,list(group2,values,meanValue=mean(values)),by=group1]
dt
group1 group2 values meanValue
[1,] 1 A 0.82122120 0.18810771
[2,] 1 C 0.78213630 0.18810771
[3,] 1 C 0.61982575 0.18810771
[4,] 1 A -1.47075238 0.18810771
[5,] 2 B 0.59390132 0.03354688
[6,] 2 A 0.07456498 0.03354688
[7,] 2 B -0.05612874 0.03354688
[8,] 2 A -0.47815006 0.03354688
[9,] 3 B 0.91897737 -0.20205707
[10,] 3 C -1.98935170 -0.20205707
[11,] 3 B -0.15579551 -0.20205707
[12,] 3 A 0.41794156 -0.20205707
No lo he evaluado, pero en mi experiencia es mucho más rápido.
Si decides ir por el camino de data.table, que creo que vale la pena explorar si trabajas con grandes conjuntos de datos, realmente necesitas leer los documentos porque hay algunas diferencias en el marco de datos que pueden morderte si no eres consciente de ellos. Sin embargo, notablemente data.table generalmente funciona con cualquier función que espere un marco de datos, ya que una tabla de datos afirmará que su tipo es el marco de datos (la tabla de datos se hereda del marco de datos).
[Feb 2011]
[Ago 2012] Actualización de Mateo:
La novedad de v1.8.2 lanzada a CRAN en julio de 2012 es :=
por grupo. Esto es muy similar a la respuesta anterior, pero agrega la nueva columna por referencia a dt
por lo que no hay copia ni necesidad de un paso de combinación o de volver a enlistar las columnas existentes para regresar junto al agregado. No es necesario setkey
primero la setkey
, y hace frente a grupos no contiguos (es decir, grupos que no están agrupados).
Esto es significativamente más rápido para conjuntos de datos grandes, y tiene una sintaxis simple y corta:
dt <- as.data.table(df)
dt[, meanValue := mean(values), by = group1]
Una línea de código hace el truco:
new <- ddply( df, "group1", transform, numcolwise(mean))
new
group1 group2 values meanValue
1 1 A 0.48742905 -0.121033381
2 1 A -0.04493361 -0.121033381
3 1 C -0.62124058 -0.121033381
4 1 C -0.30538839 -0.121033381
5 2 A 1.51178117 0.004803931
6 2 B 0.73832471 0.004803931
7 2 A -0.01619026 0.004803931
8 2 B -2.21469989 0.004803931
9 3 B 1.12493092 0.758597929
10 3 C 0.38984324 0.758597929
11 3 B 0.57578135 0.758597929
12 3 A 0.94383621 0.758597929
identical(df, new)
[1] TRUE
Una posibilidad dplyr
:
library(dplyr)
df %>%
group_by(group1) %>%
mutate(meanValue = mean(values))
Esto devuelve el marco de datos en el orden original. Agregue el orden arrange(group1)
a la tubería si desea ordenar por "grupo1".