filtro dplyr: ottieni righe con il minimo di variabile, ma solo il primo se multiplo minimo

Voglio creare un filtro raggruppato usando dplyr , in modo che all’interno di ogni gruppo venga restituita solo quella riga che ha il valore minimo della variabile x .

Il mio problema è: come previsto, nel caso di minimi multipli vengono restituite tutte le righe con il valore minimo. Ma nel mio caso, voglio solo la prima riga se sono presenti più minimi.

Ecco un esempio:

 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)) 

Come previsto, vengono restituiti tutti i minimi:

 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, avrei affrontato il compito in questo modo:

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

… che funziona:

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

D: C’è un modo per avvicinarsi a questo in dplyr? (Per motivi di velocità)

Aggiornare

Con dplyr> = 0.3 puoi usare la funzione slice in combinazione con which.min , che sarebbe il mio approccio preferito per questa attività:

 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 

Risposta originale

Per i dati di esempio, è anche ansible utilizzare due filter uno dopo l’altro:

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

Solo per completezza: ecco la soluzione finale di dplyr , derivata dai commenti di @hadley e @Arun:

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

Per quello che vale, ecco una soluzione data.table , per coloro che potrebbero essere interessati:

 # 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] 

Questo può essere ottenuto usando row_number combinazione con group_by . row_number gestisce i legami assegnando un rango non solo dal valore ma anche dall’ordine relativo all’interno del vettore. Per ottenere la prima riga di ogni gruppo con il valore minimo di x :

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

Per ulteriori informazioni vedere la vignetta dplyr sulle funzioni della finestra .

Mi piace sqldf per la sua semplicità ..

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

Produzione:

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

Un altro modo per farlo:

 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)) 

Risultato:

  ab 1 1 -0.8356286 2 2 -2.2146999 

Potrebbe anche essere facilmente adattato per ottenere la riga in ogni gruppo con il valore massimo.