1 Introduction

La manipulation de données implique souvent d’appliquer les mêmes opérations séparément à différents groupes au sein des données. Ce motif, parfois appelé “split-apply-combine”, est facilement réalisable dans {dplyr} en enchaînant le verbe group_by() avec d’autres verbes de manipulation tels que filter(), mutate() et arrange() (que vous avez déjà vus !).

Dans cette leçon, vous deviendrez confiant avec ce type de manipulations groupées.

Commençons.

2 Objectifs d’apprentissage

  1. Vous pouvez utiliser group_by() avec arrange(), filter() et mutate() pour effectuer des opérations groupées sur un jeu de données.

3 Packages

Cette leçon exigera la suite de packages {tidyverse} et le package {here} :

if(!require(pacman)) install.packages("pacman")
pacman::p_load(tidyverse, here)

4 Jeux de données

Dans cette leçon, nous utiliserons encore les données de l’enquête sérologique COVID-19 menée à Yaoundé, au Cameroun. Ci-dessous, nous importons les données, créons un petit sous-ensemble du jeu de données, yao et un sous-ensemble encore plus petit, yao_sexe_poids.

yao <- 
  read_csv(here::here('data/fr_yaounde_data.csv'))  %>% 
  select(sexe, age, cat_age, poids_kg, occupation, resultat_igg, resultat_igm)

yao
yao_sexe_poids <- 
  yao %>% 
  select(sexe, poids_kg)

yao_sexe_poids

Pour les questions pratiques, nous utiliserons également le jeu de données sur la sarcopénie que vous avez déjà vu :

sarcopenia <- read_csv(here::here('data/fr_sarcopenia_elderly.csv'))

sarcopenia

5 Organisation par groupe

La fonction arrange() ordonne les lignes d’un jeu de données par les valeurs des colonnes sélectionnées. Cette fonction est sensible aux groupements uniquement lorsque nous définissons son argument .by_group à TRUE. Pour illustrer cela, considérons le jeu de données yao_sexe_poids :

yao_sexe_poids

Nous pouvons organiser ce jeu de données par poids de cette façon :

yao_sexe_poids %>%
  arrange(poids_kg)

Comme prévu, les poids les plus faibles ont été placés en haut de la trame de données.

Si nous regroupons d’abord les données, nous pourrions nous attendre à un résultat différent :

yao_sexe_poids %>% 
  group_by(sexe) %>%
  arrange(poids_kg)

Mais comme vous le voyez, l’organisation est toujours la même.

Ce n’est que lorsque nous définissons l’argument .by_group à TRUE que nous obtenons quelque chose de différent :

yao_sexe_poids %>%
  group_by(sexe) %>%
  arrange(poids_kg, .by_group = TRUE)

Maintenant, les données sont d’abord triées par sexe (toutes les femmes en premier), puis par poids.

arrange() peut regrouper automatiquement

En réalité, nous n’avons pas besoin de group_by() pour organiser par groupe ; nous pouvons simplement mettre plusieurs variables dans la fonction arrange() pour obtenir le même effet.

Ainsi, cette simple commande arrange() :

yao_sexe_poids %>%
  arrange(sexe, poids_kg)

est équivalente à la commande plus complexe group_by(), arrange() utilisée précédemment :

yao_sexe_poids %>%
  group_by(sexe) %>%
  arrange(poids_kg, .by_group = TRUE)

La commande arrange(sexe, poids_kg) dit à R d’organiser les lignes d’abord par sexe, puis par poids.

Évidemment, cette syntaxe, avec juste arrange(), et pas de group_by() est plus simple, donc vous pouvez vous y tenir.

Rappel

desc() pour l’ordre décroissant

Rappelez-vous que pour classer en ordre décroissant, nous pouvons mettre la variable cible dans desc(). Donc, par exemple, pour trier par sexe et poids, mais avec les personnes les plus lourdes en haut, nous pouvons exécuter :

yao_sexe_poids %>%
  arrange(sexe, desc(poids_kg))

Avec une commande arrange(), triez les données sarcopenia d’abord par sexe, puis par force de préhension. (Si c’est fait correctement, la première ligne devrait être celle d’une femme avec une force de préhension de 1,3 kg). Pour rendre l’organisation claire, vous devriez d’abord select() les variables de sexe et de force de préhension.

# Complétez le code avec votre réponse :
Q_force_de_prehension_arrangee <- 
  sarcopenia %>%
  select(______________________________) %>%
  arrange(______________________________)

Le jeu de données sarcopenia contient une colonne groupe_d_age, qui stocke les groupes d’âge en chaîne de caractères (les groupes d’âge sont “Sixties”, “Seventies” et “Eighties”). Convertissez cette variable en un facteur avec les niveaux dans le bon ordre (d’abord “Sixties”, puis “Seventies” et ainsi de suite). (Astuce : Revenez sur la leçon case_when() si vous ne voyez pas comment réattribuer un niveau à un facteur).

Ensuite, avec une commande imbriquée à arrange(), classez les données d’abord par la variable facteur groupe_d_age nouvellement créée (les individus les plus jeunes en premier) puis par taille_metres, avec les individus les plus petits en premier.

# Complétez le code avec votre réponse :
Q_groupe_d_age_taille <- 
  sarcopenia %>% 
  "ÉCRIVEZ_VOTRE_RÉPONSE_ICI"

6 Filtrage par groupe

La fonction filter() conserve ou supprime des lignes en fonction d’une condition. Si filter() est appliquée à des données groupées, l’opération de filtrage est réalisée séparément pour chaque groupe.

Pour illustrer cela, considérons à nouveau le jeu de données yao_sexe_poids :

yao_sexe_poids

Si nous voulons filtrer les données pour la personne la plus lourde, nous pourrions exécuter :

yao_sexe_poids %>% 
  filter(poids_kg == max(poids_kg))

Mais si nous voulons obtenir la personne la plus lourde par groupe de sexe (l’homme le plus lourd et la femme la plus lourde), nous pouvons utiliser group_by(sex) puis filter() :

yao_sexe_poids %>% 
  group_by(sexe) %>% 
  filter(poids_kg == max(poids_kg))

Parfait ! Le code ci-dessus peut être traduit par “Pour chaque groupe de sexe, conserve la ligne avec la valeur maximale poids_kg”.

Filtrage avec des groupements imbriqués

La fonction filter() fonctionne bien avec un nombre quelconque de groupements imbriqués.

Par exemple, si nous voulons voir l’homme le plus lourd et la femme la plus lourde par groupe d’âge, nous pourrions exécuter le code suivant sur le jeu de données yao :

yao %>% 
  group_by(sexe, cat_age) %>% 
  filter(poids_kg == max(poids_kg))

Ce code regroupe par sexe et catégorie d’âge, puis trouve la personne la plus lourde dans chaque sous-catégorie.

(Pourquoi avons-nous 10 lignes dans la sortie ? Eh bien, 2 groupes de sexe x 5 groupes d’âge = 10 groupements uniques.)

La sortie est un peu dispersée, donc nous pouvons enchaîner cela avec la fonction arrange(), pour organiser par sexe et groupe d’âge.

yao %>% 
  group_by(sexe, cat_age) %>% 
  filter(poids_kg == max(poids_kg)) %>% 
  arrange(sexe, cat_age)

Maintenant, les données sont plus faciles à lire. Toutes les femmes viennent en premier, puis les hommes. Mais nous remarquons un arrangement étrange des groupes d’âge ! Ceux âgés de 5 à 14 ans devraient venir en premier dans l’arrangement. Bien sûr, nous avons appris comment corriger cela avec la fonction factor() et son argument levels :

yao %>%
  mutate(cat_age = factor(
    cat_age,
    levels = c("5 - 14", "15 - 29", "30 - 44", "45 - 64", "65 +")
  )) %>%
  group_by(sexe, cat_age) %>%
  filter(poids_kg == max(poids_kg)) %>%
  arrange(sexe, cat_age)

Maintenant, nous avons une sortie bien organisée !

Groupez le jeu de données sarcopenia par groupe d’âge et sexe, puis filtrez pour obtenir l’index de masse musculaire le plus élevé dans chaque groupe (imbriqué).

# Complétez le code avec votre réponse :
Q_index_de_muscle_squelettique_max <- 
  sarcopenia %>% 
  "ÉCRIVEZ_VOTRE_RÉPONSE_ICI"

7 Modification par groupe

mutate() est utilisé pour modifier les colonnes ou pour en créer de nouvelles. Avec des données groupées, mutate() opère sur chaque groupe indépendamment.

Considérons d’abord un appel régulier à mutate(), pas un groupé. Imaginez que vous voulez ajouter une colonne qui classe les répondants par poids. Cela peut être fait avec la fonction rank() à l’intérieur d’un appel à mutate() :

yao_sexe_poids %>%
  mutate(poids_ordre = rank(poids_kg))

La sortie montre que la première ligne est le 901ème individu le plus léger. Mais il serait plus intuitif de classer dans l’ordre décroissant avec la personne la plus lourde en premier. Nous pouvons le faire avec la fonction desc() :

yao_sexe_poids %>%
  mutate(poids_ordre = rank(desc(poids_kg)))

La sortie montre que la personne de la première ligne est la 71ème personne la plus lourde.


Maintenant, essayons d’écrire un appel groupé à mutate(). Imaginez que nous voulons ajouter cette colonne de classement par poids par groupe de sexe dans le jeu de données. Autrement dit, nous voulons connaître le classement de chaque personne par poids dans leur catégorie de sexe. Dans ce cas, nous pouvons combiner group_by(sex) avec mutate() :

yao_sexe_poids %>%
  group_by(sexe) %>%
  mutate(poids_ordre = rank(desc(poids_kg)))

Nous voyons maintenant que la personne de la première ligne est la 53ème femme la plus lourde. (Le .5 indique que ce rang est à égalité avec quelqu’un d’autre dans les données.)

Nous pourrions également organiser les données pour rendre les choses plus claires :

yao_sexe_poids %>%
  group_by(sexe) %>%
  mutate(poids_ordre = rank(desc(poids_kg))) %>% 
  arrange(sexe, poids_ordre)

Modification avec des groupes imbriqués

Bien sûr, comme avec les autres verbes que nous avons vus, mutate() fonctionne aussi avec des groupes imbriqués.

Par exemple, ci-dessous nous créons le groupe imbriqué d’âge et de sexe avec le jeu de données yao, puis nous ajoutons une colonne de rang avec mutate() :

yao %>%
  group_by(sexe, cat_age) %>%
  mutate(poids_ordre = rank(desc(poids_kg)))

La sortie montre que la personne de la première ligne est la 20ème femme la plus lourde dans le groupe d’âge de 45 à 64 ans.

Avec les données sarcopenia, groupez par groupe_d_age, puis dans une nouvelle variable appelée force_prehension_ordre, calculez le rang de la force de préhension de chaque individu par groupe d’âge. (Pour calculer le rang, utilisez mutate() et la fonction rank() avec sa méthode par défaut pour les égalités.)

# Complétez le code avec votre réponse :
Q_classement_force_de_prehension <- 
  sarcopenia %>% 
  "ÉCRIVEZ_VOTRE_RÉPONSE_ICI"

Attention

N’oubliez pas de dégrouper les données avant de procéder à une analyse plus approfondie

Comme il a été mentionné auparavant, il est important de dégrouper vos données avant de faire une analyse plus détaillée.

Considérez ce dernier exemple, où nous avons calculé le rang de poids des individus par groupe d’âge et de sexe :

yao %>%
  group_by(sexe, cat_age) %>%
  mutate(poids_ordre = rank(desc(poids_kg)))

Si, dans le processus d’analyse, vous avez stocké cette sortie comme un nouveau jeu de données :

yao_modifie <- 
  yao %>%
  group_by(sexe, cat_age) %>%
  mutate(poids_ordre = rank(desc(poids_kg)))

Et puis, plus tard, vous avez repris le jeu de données et essayé une autre analyse, par exemple, en filtrant pour obtenir la personne la plus âgée dans les données :

yao_modifie %>%
  filter(age == max(age))

Vous pourriez être confus par la sortie! Pourquoi y a-t-il 55 lignes de “personnes les plus âgées”?

Cela serait dû au fait que vous avez oublié de dégrouper les données avant de les stocker pour une analyse plus approfondie. Faisons cela correctement maintenant

yao_modifie <- 
  yao %>%
  group_by(sexe, cat_age) %>%
  mutate(poids_ordre = rank(desc(poids_kg)))%>%
  ungroup()

Maintenant, nous pouvons obtenir correctement la/les personne(s) la/les plus âgée(s) dans le jeu de données :

yao_modifie %>%
  filter(age == max(age))

8 Conclusion

group_by() est un outil merveilleux pour arranger, modifier, filtrer en fonction des groupes au sein d’une ou plusieurs variables.

Fig: arrange() et son utilisation combinée avec group_by(). Fig: mutate() et son utilisation combinée avec group_by().

Fig: filter() et son utilisation combinée avec group_by()
Fig: filter() et son utilisation combinée avec group_by()

Il existe de nombreuses façons de combiner ces verbes pour manipuler vos données. Nous vous invitons à prendre un peu de temps pour essayer ces verbes dans différentes combinaisons !

À la prochaine !

Contributeurs

Les membres de l’équipe suivants ont contribué à cette leçon :

Références

Certains matériaux de cette leçon ont été adaptés des sources suivantes :

L’œuvre d’art a été adaptée de :