libreria - funcion select en r
filtro dplyr: obtenga filas con un mínimo de variable, pero solo el primero si múltiples mínimos (6)
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 x y
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 x y
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 x y
#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)
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:
a b
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.
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]
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)