web-dev-qa-db-fra.com

Comment sélectionner les lignes avec les valeurs maximales dans chaque groupe avec dplyr?

Je voudrais sélectionner une ligne avec une valeur maximale dans chaque groupe avec dplyr.

Tout d'abord, je génère des données aléatoires pour montrer ma question

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

Dans plyr, je pourrais utiliser une fonction personnalisée pour sélectionner cette ligne.

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

Dans dplyr, j'utilise ce code pour obtenir la valeur maximale, mais pas les lignes avec la valeur maximale (colonne C dans ce cas).

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

Comment pourrais-je y parvenir? Merci pour toute suggestion.

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        
82
Bangyou

Essaye ça:

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

Semble fonctionner:

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

Comme l'a souligné @docendo dans les commentaires, slice peut être préféré ici, comme indiqué dans la réponse de @RoyalITS ci-dessous si vous ne voulez strictement qu'une rangée par groupe. Cette réponse renverra plusieurs lignes s'il y en a plusieurs avec une valeur maximale identique. 

94
thelatemail

Vous pouvez utiliser top_n

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

Cela classera par la dernière colonne (value) et renverra les premières lignes n=1.

Actuellement, vous ne pouvez pas modifier ce paramètre par défaut sans provoquer d'erreur (Voir https://github.com/hadley/dplyr/issues/426 )

56
mnel
df %>% group_by(A,B) %>% slice(which.max(value))
47
RoyalTS

Cette solution plus détaillée permet de mieux contrôler ce qui se passe en cas de duplication de la valeur maximale (dans cet exemple, l'une des lignes correspondantes sera sélectionnée de manière aléatoire).

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

Pour moi, cela a aidé à compter le nombre de valeurs par groupe. Copiez la table de comptage dans un nouvel objet. Puis filtrez le maximum du groupe en fonction de la première caractéristique de regroupement. Par exemple:

count_table  <- df %>%
                group_by(A, B) %>%
                count() %>%
                arrange(A, desc(n))

count_table %>% 
    group_by(A) %>%
    filter(n == max(n))

ou

count_table %>% 
    group_by(A) %>%
    top_n(1, n)
0
ksvrd

Plus généralement, je pense que vous voudrez peut-être avoir le "top" des lignes qui sont triées dans un groupe donné. 

Dans le cas où une seule valeur est supprimée, vous avez essentiellement trié selon une colonne. Toutefois, il est souvent utile de trier de manière hiérarchique sur plusieurs colonnes (par exemple: une colonne de date et une colonne d’heure). 

# Answering the question of getting row with max "value".
df %>% 
  # Within each grouping of A and B values.
  group_by( A, B) %>% 
  # Sort rows in descending order by "value" column.
  arrange( desc(value) ) %>% 
  # Pick the top 1 value
  slice(1) %>% 
  # Remember to ungroup in case you want to do further work without grouping.
  ungroup()

# Answering an extension of the question of 
# getting row with the max value of the lowest "C".
df %>% 
  # Within each grouping of A and B values.
  group_by( A, B) %>% 
  # Sort rows in ascending order by C, and then within that by 
  # descending order by "value" column.
  arrange( C, desc(value) ) %>% 
  # Pick the one top row based on the sort
  slice(1) %>% 
  # Remember to ungroup in case you want to do further work without grouping.
  ungroup()
0
Kalin