14 votos

Formas rápidos en R para conseguir la primera fila de un marco de datos agrupado por identificador

A veces necesito para obtener sólo la primera fila de un conjunto de datos agrupados por un identificador, como cuando la recuperación de edad y de género cuando hay varias observaciones por individuo. ¿Qué es un rápido (o la más rápida) para hacer esto en R? He utilizado agregado (a) por debajo y la sospecha de que hay mejores maneras. Antes de publicar esta pregunta he buscado un poco en google, encontré y trató de ddply, y se sorprendió de que era muy lento y me dio errores de memoria en mi conjunto de datos (400,000 filas x 16 cols, 7.000 de Identificadores únicos), mientras que el agregado() versión fue bastante rápida.

(dx <- data.frame(ID = factor(c(1,1,2,2,3,3)), AGE = c(30,30,40,40,35,35), FEM = factor(c(1,1,0,0,1,1))))
# ID AGE FEM
#  1  30   1
#  1  30   1
#  2  40   0
#  2  40   0
#  3  35   1
#  3  35   1
ag <- data.frame(ID=levels(dx$ID))
ag <- merge(ag, aggregate(AGE ~ ID, data=dx, function(x) x[1]), "ID")
ag <- merge(ag, aggregate(FEM ~ ID, data=dx, function(x) x[1]), "ID")
ag
# ID AGE FEM
#  1  30   1
#  2  40   0
#  3  35   1
#same result:
library(plyr)
ddply(.data = dx, .var = c("ID"), .fun = function(x) x[1,])

ACTUALIZACIÓN: Ver Chase respuesta y Matt Parker comentario por lo que considero que es el método más elegante. Ver a @Mateo Dowle la respuesta para la solución más rápida que utiliza la data.table paquete.

11voto

Mohit Jain Puntos 412

¿Es la columna de ID realmente un factor? Si de hecho es numérico, creo que se puede utilizar la diff función de su ventaja. También podría obligar a numérico con as.numeric() .

dx <- data.frame(
    ID = sort(sample(1:7000, 400000, TRUE))
    , AGE = sample(18:65, 400000, TRUE)
    , FEM = sample(0:1, 400000, TRUE)
)

dx[ diff(c(0,dx$ID)) != 0, ]

10voto

David J. Sokol Puntos 1730

Usted no necesita múltiples merge() pasos, sólo aggregate() ambas variables de interés:

> aggregate(dx[, -1], by = list(ID = dx$ID), head, 1)
  ID AGE FEM
1  1  30   1
2  2  40   0
3  3  35   1

> system.time(replicate(1000, aggregate(dx[, -1], by = list(ID = dx$ID), 
+                                       head, 1)))
   user  system elapsed 
  2.531   0.007   2.547 
> system.time(replicate(1000, {ag <- data.frame(ID=levels(dx$ID))
+ ag <- merge(ag, aggregate(AGE ~ ID, data=dx, function(x) x[1]), "ID")
+ ag <- merge(ag, aggregate(FEM ~ ID, data=dx, function(x) x[1]), "ID")
+ }))
   user  system elapsed 
  9.264   0.009   9.301

Comparación de los tiempos:

1) de Matt solución:

> system.time(replicate(1000, {
+ agg <- by(dx, dx$ID, FUN = function(x) x[1, ])
+ # Which returns a list that you can then convert into a data.frame thusly:
+ do.call(rbind, agg)
+ }))
   user  system elapsed 
  3.759   0.007   3.785

2) Zach del reshape2 solución:

> system.time(replicate(1000, {
+ dx <- melt(dx,id=c('ID','FEM'))
+ dcast(dx,ID+FEM~variable,fun.aggregate=mean)
+ }))
   user  system elapsed 
 12.804   0.032  13.019

3) Steve datos.tabla de solución:

> system.time(replicate(1000, {
+ dxt <- data.table(dx, key='ID')
+ dxt[, .SD[1,], by=ID]
+ }))
   user  system elapsed 
  5.484   0.020   5.608 
> dxt <- data.table(dx, key='ID') ## one time step
> system.time(replicate(1000, {
+ dxt[, .SD[1,], by=ID] ## try this one line on own
+ }))
   user  system elapsed 
  3.743   0.006   3.784

4) Chase rápida utilizando la solución numérica, no factor, ID:

> dx2 <- within(dx, ID <- as.numeric(ID))
> system.time(replicate(1000, {
+ dy <- dx[order(dx$ID),]
+ dy[ diff(c(0,dy$ID)) != 0, ]
+ }))
   user  system elapsed 
  0.663   0.000   0.663

y 5) Matt Parker alternativa a la Persecución de la solución, por carácter o factor ID, que es ligeramente más rápido que Chase numéricos ID uno:

> system.time(replicate(1000, {
+ dx[c(TRUE, dx$ID[-1] != dx$ID[-length(dx$ID)]), ]
+ }))
   user  system elapsed 
  0.513   0.000   0.516

10voto

Ishmaeel Puntos 7720

Puedes probar a utilizar los datos.tabla paquete.

Para su caso en particular, la ventaja es que (increíblemente) rápido. La primera vez que me presentaron a él, yo estaba trabajando en los datos.marco objetos con cientos de miles de filas. "Normal" aggregate o ddply métodos fueron tomadas ~ 1-2 minutos para completar (esto fue antes de Hadley introdujo el idata.frame mojo en ddply). El uso de data.table, la operación fue, literalmente, se hace en cuestión de segundos.

La desventaja es que es tan rápido porque se resort sus datos.tabla (es como de datos.frame) por "columnas de clave" y el uso inteligente de la estrategia de búsqueda para encontrar subconjuntos de datos. Esto resultará en un reordenamiento de sus datos antes de recoger estadísticas sobre él.

Dado que usted sólo desea que la primera fila de cada grupo, tal vez el reordenamiento se hace un lío hasta que la fila es la primera, que es la razón por la que podría no ser apropiado en su situación.

De todos modos, usted tendrá que juzgar si es o no data.table es apropiado aquí, pero esta es la manera de utilizarlo con los datos que ha presentado:

install.packages('data.table') ## if yo udon't have it already
library(data.table)
dxt <- data.table(dx, key='ID')
dxt[, .SD[1,], by=ID]
     ID AGE FEM
[1,]  1  30   1
[2,]  2  40   0
[3,]  3  35   1

Actualización: Mateo Dowle (el principal desarrollador de los datos.tabla paquete) ha proporcionado una mejor y más inteligente/(muy) de la manera más eficiente de utilizar los datos.tabla para resolver este problema como una de las respuestas aquí ... definitivamente comprobar que fuera.

4voto

Dillie-O Puntos 16780

Usted podría intentar

agg <- by(dx, dx$ID, FUN = function(x) x[1, ])
# Which returns a list that you can then convert into a data.frame thusly:
do.call(rbind, agg)

No tengo ni idea si esto será todo más rápido que plyr , aunque.

4voto

Boris Tsirelson Puntos 191

Probar reshape2

library(reshape2)
dx <- melt(dx,id=c('ID','FEM'))
dcast(dx,ID+FEM~variable,fun.aggregate=mean)

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X