A veces necesito obtener solo la primera fila de un conjunto de datos agrupados por un identificador, como al recuperar la edad y el género cuando hay múltiples observaciones por individuo. ¿Cuál es una forma rápida (o la más rápida) de hacer esto en R? Usé agregado () a continuación y sospecho que hay mejores formas. Antes de publicar esta pregunta, busqué un poco en Google, encontré e intenté ddply, y me sorprendió que fuera extremadamente lento y me dio errores de memoria en mi conjunto de datos (400,000 filas x 16 cols, 7,000 ID únicos), mientras que la versión agregada () fue razonablemente rápido.
(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: Vea la respuesta de Chase y el comentario de Matt Parker para lo que considero el enfoque más elegante. Consulte la respuesta de @Matthew Dowle para obtener la solución más rápida que utiliza el data.table
paquete.
fuente
diff()
para que pueda recoger la primera IDdx
.Respuestas:
¿Es su columna de ID realmente un factor? Si de hecho es numérico, creo que puede usar la
diff
función para su ventaja. También podría obligarlo a numérico conas.numeric()
.fuente
dx[c(TRUE, dx$ID[-1] != dx$ID[-length(dx$ID)], ]
para datos no numéricos: obtengo 0.03 por carácter, 0.05 por factores. PD: hay un extra)
en tu primerasystem.time()
función, después del segundo cero.data.table
solución a continuación debería ser la más rápida, por lo que lo comprobaría si fuera usted (probablemente debería ser la respuesta aceptada aquí).Siguiendo la respuesta de Steve, hay una forma mucho más rápida en data.table:
Si simplemente necesita la primera fila de cada grupo, es mucho más rápido unirse a esa fila directamente. ¿Por qué crear el objeto .SD cada vez, solo para usar la primera fila?
Compare el 0.064 de data.table con la "alternativa de Matt Parker a la solución de Chase" (que parecía ser la más rápida hasta ahora):
Entonces ~ 5 veces más rápido, pero es una tabla pequeña con menos de 1 millón de filas. A medida que aumenta el tamaño, también lo hace la diferencia.
fuente
[.data.table
puede ser la función ... Creo que no me di cuenta de que no creaste un.SD
objeto si realmente no lo necesitas. ¡Buena esa!dxt <- data.table(dx, key='ID')
en la llamada a system.time (), es más rápido que la solución de @ Matt.SD[1L]
estaba completamente optimizado y, en realidad, la respuesta de @SteveLianoglou sería el doble de rápida para 5e7 filas.No necesitas multiples
merge()
pasos, soloaggregate()
ambas variables de interés:Tiempos de comparación:
1) la solución de Matt:
2) la solución reshape2 de Zach:
3) Solución data.table de Steve:
4) La solución rápida de Chase usando numérico, no factor
ID
,:y 5) la alternativa de Matt Parker a la solución de Chase, por carácter o factor
ID
, que es ligeramente más rápida que la numérica de ChaseID
:fuente
dx$ID <- sample(as.numeric(dx$ID)) #assuming IDs arent presorted system.time(replicate(1000, { dy <- dx[order(dx$ID),] dy[ diff(c(0,dy$ID)) != 0, ] })) user system elapsed 0.58 0.00 0.58
ID
para que el resultado fuera comparable a otras soluciones.Puede intentar usar el paquete data.table .
Para su caso particular, lo bueno es que es (increíblemente) rápido. La primera vez que me presentaron, estaba trabajando en objetos data.frame con cientos de miles de filas. "Normal"
aggregate
o losddply
métodos se tomaron ~ 1-2 minutos para completar (esto fue antes de que Hadley introdujera elidata.frame
mojoddply
). Utilizandodata.table
, la operación se realizó literalmente en cuestión de segundos.La desventaja es que es muy rápido porque recurrirá a su data.table (es como un data.frame) por "columnas clave" y usará una estrategia de búsqueda inteligente para encontrar subconjuntos de sus datos. Esto dará como resultado un reordenamiento de sus datos antes de recopilar estadísticas sobre ellos.
Dado que solo querrá la primera fila de cada grupo, tal vez el reordenamiento estropeará qué fila es la primera, por lo que podría no ser apropiado en su situación.
De todos modos, tendrás que juzgar si
data.table
es apropiado o no aquí, pero así es como lo usarías con los datos que has presentado:Actualización: Matthew Dowle (el desarrollador principal del paquete data.table) ha proporcionado una forma mejor / más inteligente / (extremadamente) más eficiente de usar data.table para resolver este problema como una de las respuestas aquí ... definitivamente échale un vistazo .
fuente
Prueba reshape2
fuente
Tu podrías intentar
Sin
plyr
embargo, no tengo idea de si esto será más rápido .fuente