Copiando el dataframe seleccionando una fila por grupo

Estoy intentando colapsar un dataframe eliminando todas las filas excepto una de cada grupo de filas con valores idénticos en una columna en particular. En otras palabras, la primera fila de cada grupo.

Por ejemplo, me gustaría convertir esto

> d = data.frame(x=c(1,1,2,4),y=c(10,11,12,13),z=c(20,19,18,17)) > d xyz 1 1 10 20 2 1 11 19 3 2 12 18 4 4 13 17 

Dentro de esto:

  xyz 1 1 11 19 2 2 12 18 3 4 13 17 

Estoy usando agregado para hacer esto actualmente, pero el rendimiento es inaceptable con más datos:

 > d.ordered = d[order(-d$y),] > aggregate(d.ordered,by=list(key=d.ordered$x),FUN=function(x){x[1]}) 

Probé split / unsplit con el mismo argumento de función que aquí, pero unsplit se queja de números de fila duplicados.

¿Es eso una posibilidad? ¿Hay un modismo R para convertir el vector de longitud de rle en los índices de las filas que comienzan cada ejecución, que luego puedo usar para extraer esas filas del dataframe?

Tal vez duplicated() puede ayudar:

 R> d[ !duplicated(d$x), ] xyz 1 1 10 20 3 2 12 18 4 4 13 17 R> 

Editar Shucks, no importa. Este elige el primero en cada bloque de repeticiones, querías el último. Entonces aquí hay otro bash de usar plyr :

 R> ddply(d, "x", function(z) tail(z,1)) xyz 1 1 11 19 2 2 12 18 3 4 13 17 R> 

Aquí, plyr hace el arduo trabajo de encontrar subconjuntos únicos, alternar sobre ellos y aplicar la función suministrada, que simplemente devuelve el último conjunto de observaciones en un bloque z usando tail(z, 1) .

Solo para agregar un poco a lo que Dirk proporcionó … duplicated tiene un argumento del fromLast que puede usar para seleccionar la última fila:

 d[ !duplicated(d$x,fromLast=TRUE), ] 

Aquí hay una solución data.table que será eficiente en tiempo y memoria para grandes conjuntos de datos

 library(data.table) DT <- as.data.table(d) # convert to data.table setkey(DT, x) # set key to allow binary search using `J()` DT[J(unique(x)), mult ='last'] # subset out the last row for each x DT[J(unique(x)), mult ='first'] # if you wanted the first row for each x