¿Cómo seleccionar la primera y la última fila dentro de una variable de agrupación en un dataframe?

¿Cómo puedo seleccionar la primera y la última fila para cada id única en el siguiente dataframe?

 tmp  id d gr mm area #> 1 15 1 2 3.4 1 #> 2 15 1 1 4.9 2 #> 3 15 1 1 4.4 1 #> 4 15 1 1 5.5 2 #> 5 21 1 1 4.0 2 #> 6 21 1 2 3.8 2 #> 7 22 1 1 4.0 2 #> 8 22 1 1 4.9 2 #> 9 22 1 2 4.6 2 #> 10 23 1 1 2.7 2 #> 11 23 1 1 4.0 2 #> 12 23 1 2 3.0 2 #> 13 24 1 1 3.0 2 #> 14 24 1 1 2.0 3 #> 15 24 1 1 4.0 2 #> 16 24 1 2 2.0 3 

Una solución plyr ( tmp es su dataframe):

 library("plyr") ddply(tmp, .(id), function(x) x[c(1, nrow(x)), ]) # id d gr mm area # 1 15 1 2 3.4 1 # 2 15 1 1 5.5 2 # 3 21 1 1 4.0 2 # 4 21 1 2 3.8 2 # 5 22 1 1 4.0 2 # 6 22 1 2 4.6 2 # 7 23 1 1 2.7 2 # 8 23 1 2 3.0 2 # 9 24 1 1 3.0 2 # 10 24 1 2 2.0 3 

O con dplyr (ver también aquí ):

 library("dplyr") tmp %>% group_by(id) %>% slice(c(1, n())) %>% ungroup() # # A tibble: 10 × 5 # id d gr mm area #      # 1 15 1 2 3.4 1 # 2 15 1 1 5.5 2 # 3 21 1 1 4.0 2 # 4 21 1 2 3.8 2 # 5 22 1 1 4.0 2 # 6 22 1 2 4.6 2 # 7 23 1 1 2.7 2 # 8 23 1 2 3.0 2 # 9 24 1 1 3.0 2 # 10 24 1 2 2.0 3 

Una solución rápida y corta de data.table :

 tmp[, .SD[c(1,.N)], by=id] 

donde .SD representa cada (S) ubset de (D) ata, .N es el número de filas en cada grupo y tmp es un data.table ; por ejemplo, como se proporciona mediante fread() de forma predeterminada o mediante la conversión de un data.frame utilizando setDT() .

Tenga en cuenta que si un grupo solo contiene una fila, esa fila aparecerá dos veces en el resultado porque esa fila es la primera y la última fila de ese grupo. Para evitar la repetición en ese caso, gracias a @Thell:

 tmp[, .SD[unique(c(1,.N))], by=id] 

Alternativamente, lo siguiente hace que la lógica sea explícita para el caso especial .N==1 :

 tmp[, if (.N==1) .SD else .SD[c(1,.N)], by=id] 

No necesita .SD[1] en la primera parte de if porque en ese caso .N es 1 así que .SD debe ser solo una fila de todos modos.

Puede envolver j en {} y tener una página completa de código dentro de {} si lo desea. Siempre que la última expresión dentro de {} devuelva un objeto similar a una list para astackrse (como una list simple, data.table o data.frame ).

 tmp[, { ...; if (.N==1) .SD else .SD[c(1,.N)] } , by=id] 

Aquí hay una solución en la base R Si hay varios grupos con la misma id este código devuelve la primera y la última fila para cada uno de esos grupos individuales.

EDITAR: 12 de enero de 2017

Esta solución puede ser un poco más intuitiva que mi otra respuesta más abajo:

 lmy.df = read.table(text = ' id d gr mm area 15 1 2 3.40 1 15 1 1 4.90 2 15 1 1 4.40 1 15 1 1 5.50 2 21 1 1 4.00 2 21 1 2 3.80 2 22 1 1 4.00 2 23 1 1 2.70 2 23 1 1 4.00 2 23 1 2 3.00 2 24 1 1 3.00 2 24 1 1 2.00 3 24 1 1 4.00 2 24 1 2 2.00 3 ', header = TRUE) head <- aggregate(lmy.df, by=list(lmy.df$id), FUN = function(x) { first = head(x,1) } ) tail <- aggregate(lmy.df, by=list(lmy.df$id), FUN = function(x) { last = tail(x,1) } ) head$order = 'first' tail$order = 'last' my.output <- rbind(head, tail) my.output # Group.1 id d gr mm area order #1 15 15 1 2 3.4 1 first #2 21 21 1 1 4.0 2 first #3 22 22 1 1 4.0 2 first #4 23 23 1 1 2.7 2 first #5 24 24 1 1 3.0 2 first #6 15 15 1 1 5.5 2 last #7 21 21 1 2 3.8 2 last #8 22 22 1 1 4.0 2 last #9 23 23 1 2 3.0 2 last #10 24 24 1 2 2.0 3 last 

EDITAR: 18 de junio de 2016

Desde que publiqué mi respuesta original, he aprendido que es mejor utilizar la lapply que apply . Esto se debe a que apply no funciona si cada grupo tiene el mismo número de filas. Ver aquí: Error al numerar filas por grupo

 lmy.df = read.table(text = ' id d gr mm area 15 1 2 3.40 1 15 1 1 4.90 2 15 1 1 4.40 1 15 1 1 5.50 2 21 1 1 4.00 2 21 1 2 3.80 2 22 1 1 4.00 2 23 1 1 2.70 2 23 1 1 4.00 2 23 1 2 3.00 2 24 1 1 3.00 2 24 1 1 2.00 3 24 1 1 4.00 2 24 1 2 2.00 3 ', header = TRUE) lmy.seq <- rle(lmy.df$id)$lengths lmy.df$first <- unlist(lapply(lmy.seq, function(x) seq(1,x))) lmy.df$last <- unlist(lapply(lmy.seq, function(x) seq(x,1,-1))) lmy.df lmy.df2 <- lmy.df[lmy.df$first==1 | lmy.df$last == 1,] lmy.df2 # id d gr mm area first last #1 15 1 2 3.4 1 1 4 #4 15 1 1 5.5 2 4 1 #5 21 1 1 4.0 2 1 2 #6 21 1 2 3.8 2 2 1 #7 22 1 1 4.0 2 1 1 #8 23 1 1 2.7 2 1 3 #10 23 1 2 3.0 2 3 1 #11 24 1 1 3.0 2 1 4 #14 24 1 2 2.0 3 4 1 

Aquí hay un ejemplo en el que cada grupo tiene dos filas:

 lmy.df = read.table(text = ' id d gr mm area 15 1 2 3.40 1 15 1 1 4.90 2 21 1 1 4.00 2 21 1 2 3.80 2 22 1 1 4.00 2 22 1 1 6.00 2 23 1 1 2.70 2 23 1 2 3.00 2 24 1 1 3.00 2 24 1 2 2.00 3 ', header = TRUE) lmy.seq <- rle(lmy.df$id)$lengths lmy.df$first <- unlist(lapply(lmy.seq, function(x) seq(1,x))) lmy.df$last <- unlist(lapply(lmy.seq, function(x) seq(x,1,-1))) lmy.df lmy.df2 <- lmy.df[lmy.df$first==1 | lmy.df$last == 1,] lmy.df2 # id d gr mm area first last #1 15 1 2 3.4 1 1 2 #2 15 1 1 4.9 2 2 1 #3 21 1 1 4.0 2 1 2 #4 21 1 2 3.8 2 2 1 #5 22 1 1 4.0 2 1 2 #6 22 1 1 6.0 2 2 1 #7 23 1 1 2.7 2 1 2 #8 23 1 2 3.0 2 2 1 #9 24 1 1 3.0 2 1 2 #10 24 1 2 2.0 3 2 1 

Respuesta original:

 my.seq <- data.frame(rle(my.df$id)$lengths) my.df$first <- unlist(apply(my.seq, 1, function(x) seq(1,x))) my.df$last <- unlist(apply(my.seq, 1, function(x) seq(x,1,-1))) my.df2 <- my.df[my.df$first==1 | my.df$last == 1,] my.df2 id d gr mm area first last 1 15 1 2 3.4 1 1 4 4 15 1 1 5.5 2 4 1 5 21 1 1 4.0 2 1 2 6 21 1 2 3.8 2 2 1 7 22 1 1 4.0 2 1 3 9 22 1 2 4.6 2 3 1 10 23 1 1 2.7 2 1 3 12 23 1 2 3.0 2 3 1 13 24 1 1 3.0 2 1 4 16 24 1 2 2.0 3 4 1 
Intereting Posts