filtro dplyr: obtenga filas con un mínimo de variable, pero solo el primero si múltiples mínimos

Quiero hacer un filtro agrupado usando dplyr , de manera que dentro de cada grupo solo se devuelva esa fila que tenga el valor mínimo de la variable x .

Mi problema es: como se esperaba, en el caso de mínimos múltiples, se devuelven todas las filas con el valor mínimo. Pero en mi caso, solo quiero la primera fila si hay varios mínimos.

Aquí hay un ejemplo:

 df <- data.frame( A=c("A", "A", "A", "B", "B", "B", "C", "C", "C"), x=c(1, 1, 2, 2, 3, 4, 5, 5, 5), y=rnorm(9) ) library(dplyr) df.g <- group_by(df, A) filter(df.g, x == min(x)) 

Como era de esperar, se devuelven todos los mínimos:

 Source: local data frame [6 x 3] Groups: A A xy 1 A 1 -1.04584335 2 A 1 0.97949399 3 B 2 0.79600971 4 C 5 -0.08655151 5 C 5 0.16649962 6 C 5 -0.05948012 

Con ddply, me habría acercado a la tarea de esa manera:

 library(plyr) ddply(df, .(A), function(z) { z[z$x == min(z$x), ][1, ] }) 

… que funciona:

  A xy 1 A 1 -1.04584335 2 B 2 0.79600971 3 C 5 -0.08655151 

P: ¿Hay alguna manera de abordar esto en dplyr? (Por razones de velocidad)

Actualizar

Con dplyr> = 0.3 puedes usar la función slice en combinación con which.min , que sería mi enfoque favorito para esta tarea:

 df %>% group_by(A) %>% slice(which.min(x)) #Source: local data frame [3 x 3] #Groups: A # # A xy #1 A 1 0.2979772 #2 B 2 -1.1265265 #3 C 5 -1.1952004 

Respuesta original

Para los datos de muestra, también es posible usar dos filter uno detrás de otro:

 group_by(df, A) %>% filter(x == min(x)) %>% filter(1:n() == 1) 

Solo para completar: esta es la solución final de dplyr , derivada de los comentarios de @hadley y @Arun:

 library(dplyr) df.g <- group_by(df, A) filter(df.g, rank(x, ties.method="first")==1) 

Por lo que vale, aquí hay una solución de datos. Para aquellos que puedan estar interesados:

 # approach with setting keys dt <- as.data.table(df) setkey(dt, A,x) dt[J(unique(A)), mult="first"] # without using keys dt <- as.data.table(df) dt[dt[, .I[which.min(x)], by=A]$V1] 

Esto se puede lograr usando row_number combinado con group_by . row_number maneja los vínculos asignando un rango no solo por el valor sino también por el orden relativo dentro del vector. Para obtener la primera fila de cada grupo con el valor mínimo de x :

 df.g <- group_by(df, A) filter(df.g, row_number(x) == 1) 

Para obtener más información, consulte la viñeta dplyr en las funciones de la ventana .

Me gusta sqldf por su simplicidad …

 sqldf("select A,min(X),y from 'df.g' group by A") 

Salida:

 A min(X) y 1 A 1 -1.4836989 2 B 2 0.3755771 3 C 5 0.9284441 

Otra forma de hacerlo:

 set.seed(1) x <- data.frame(a = rep(1:2, each = 10), b = rnorm(20)) x <- dplyr::arrange(x, a, b) dplyr::filter(x, !duplicated(a)) 

Resultado:

  ab 1 1 -0.8356286 2 2 -2.2146999 

También se podría adaptar fácilmente para obtener la fila en cada grupo con el valor máximo.