Como selecionar as linhas com valores máximos em cada grupo com dplyr?

Eu gostaria de selecionar uma linha com valor máximo em cada grupo com dplyr.

Em primeiro lugar eu gerei alguns dados randoms para mostrar minha pergunta

set.seed(1) df <- expand.grid(list(A = 1:5, B = 1:5, C = 1:5)) df$value <- runif(nrow(df)) 

No plyr, eu poderia usar uma function personalizada para selecionar essa linha.

 library(plyr) ddply(df, .(A, B), function(x) x[which.max(x$value),]) 

Em dplyr, estou usando este código para obter o valor máximo, mas não as linhas com valor máximo (coluna C neste caso).

 library(dplyr) df %>% group_by(A, B) %>% summarise(max = max(value)) 

Como eu poderia conseguir isso? Obrigado por qualquer sugestão.

 sessionInfo() R version 3.1.0 (2014-04-10) Platform: x86_64-w64-mingw32/x64 (64-bit) locale: [1] LC_COLLATE=English_Australia.1252 LC_CTYPE=English_Australia.1252 [3] LC_MONETARY=English_Australia.1252 LC_NUMERIC=C [5] LC_TIME=English_Australia.1252 attached base packages: [1] stats graphics grDevices utils datasets methods base other attached packages: [1] dplyr_0.2 plyr_1.8.1 loaded via a namespace (and not attached): [1] assertthat_0.1.0.99 parallel_3.1.0 Rcpp_0.11.1 [4] tools_3.1.0 

Tente isto:

 result <- df %>% group_by(A, B) %>% filter(value == max(value)) %>% arrange(A,B,C) 

Parece funcionar:

 identical( as.data.frame(result), ddply(df, .(A, B), function(x) x[which.max(x$value),]) ) #[1] TRUE 

Como apontado por @docendo nos comentários, a slice pode ser preferida aqui, conforme a resposta do @RoyalITS abaixo, se você deseja apenas uma linha por grupo. Essa resposta retornará várias linhas se houver várias com um valor máximo idêntico.

Você pode usar top_n

 df %>% group_by(A, B) %>% top_n(n=1) 

Isto irá classificar pela última coluna ( value ) e retornar as primeiras n=1 linhas.

Atualmente, você não pode alterar esse padrão sem causar um erro (consulte https://github.com/hadley/dplyr/issues/426 )

 df %>% group_by(A,B) %>% slice(which.max(value)) 

Essa solução mais detalhada fornece maior controle sobre o que acontece no caso de um valor máximo duplicado (neste exemplo, uma linha aleatória será tomada)

 library(dplyr) df %>% group_by(A, B) %>% mutate(the_rank = rank(-value, ties.method = "random")) %>% filter(the_rank == 1) %>% select(-the_rank)