Tableaux récapitulatifs
Objectifs
- Réviser la sélection des lignes avec
filter()et la création de variables avecmutate() - Créer des tableaux d’effectifs avec
count()en croisant plus d’une variable - Créer des tables plus complexes, avec des résumés statistiques divers, stratifiés par une variable avec la fonction
summarize()
Setup
Prérequis : cette session part du principe que vous avez effectué les sessions obligatoires précédentes, en particulier que vous savez importer vos données et utiliser les verbes principaux de {dplyr}. Aller vous rafraîchir si besoin.
Cette session utilise une version nettoyée de la liste linéaire rougeole à Mandoul (Tchad).
Ouvrez votre projet RStudio et créez un nouveau script dans le sous-dossier R appelé tables.R avec les métadonnées appropriées et une section “Paquets” qui importe : {rio}, {here} et {tidyverse}. Ajoutez une section “Import données” qui charge la version nettoyée de la liste linéaire de la rougeole dans R.
Introduction : agrégation des données
Récapitulons. Dans les sessions précédentes vous avez effectué l’une des tâches les plus importantes d’un épidémiologiste : le nettoyage des données. Maintenant que vous disposez de données propres et standardisées, vous pouvez commencer à les analyser. On commence généralement par une analyse descriptive à base de tableaux :
- tableaux d’effectifs et fréquences univariés pour compter les occurrences de différentes valeurs
- statistiques descriptives des variables numériques (moyenne, médiane, écart-type)
- tableaux croisés pour examiner la relation entre différentes variables catégorielles
- tableaux descriptifs stratifiés par une variable
Croiser plusieurs colonnes (tables de contingence)
Au cours de la session d’exploration des données, vous avez utilisé la fonction count() pour créer une table contenant les effectifs d’une variable catégorielle. Aujourd’hui nous allons apprendre à calculer les effectifs en croisant deux variables ou plus. Ces tableaux sont appelés tableaux de contingence.
Par exemple, nous avions calculé le nombre de patients par sous-préfecture, mais nous voulons aller plus loin et calculer le nombre patients par sous-préfecture et stratifié par classe d’âge. Cela nous permettrait de voir si des zones ont des patients anormalement âgés et identifier de bons candidats pour des campagnes de rattrapage.
Pour cela il suffit de passer plusieurs noms de colonnes à count() :
df_linelist |>
count(sous_prefecture, age_groupe)Créez un tableau contenant le nombre de patients stratifiés par sous_prefecture et si le patient a été hospitalisé ou pas (hospitalisation). Que se passe-t-il si vous modifiez l’ordre des arguments fourni à count() ?
En utilisant count(), répondez aux questions suivantes :
- Combien de patients étaient des femmes ? Quelle est la proportion ?
- Quelles sont toutes les valeurs possibles de la variable
statut_sortie? - Combien de patients âgés de 1 à 4 ans se sont rétablis ?
Filtrer les valeurs NA
En examinant les catégories de statut_sortie, vous devriez avoir remarqué que certains patients n’ont pas de statut : la valeur est NA :
df_linelist |>
count(statut_sortie) |>
mutate(prop = n / sum(n))Observez le résultat du code ci-dessus. Quel est le terme technique pour la proportion de patients décédés ? Êtes-vous satisfait la manière dont le calcul ci-dessus est effectué ?
La proportion de cas décédés est également appelée létalité. Pour calculer précisément la mortalité, nous voulons souvent nous assurer que le dénominateur ne comprend que les patients dont nous connaissons le statut. Ici, nous voulons donc ignorer tous les cas avec NA ou “sortis contre avis médical” dans le calcul.
Nous pouvons faire ceci avec la fonction filter(). Nous avons vu dans la session précédente que l’on peut tester l’inégalité avec l’opérateur !=. Par contre, comme souvent avec R, le travail avec les valeurs NA est un peu particulier. Pour tester si une valeur est NA on utilise la fonction is.na(). Pour tester si une valeur n’est pas NA, on préface la fonction d’un signe !, qui indique à R de “faire le contraire”: !is.na() teste si une valeur n’est pas NA.
Donc pour supprimer les patients sans statut ou étant sorti contre avis médical, on écrit :
df_linelist |>
filter(statut_sortie != "sortie contre avis medical", # inégalité
!is.na(statut_sortie)) |> # n'est pas NA pour le statut à la sortie
count(statut_sortie)Quelle autre condition pourriez-vous utiliser dans filter() pour obtenir le même résultat ?
Reprenez votre code précédent et ajoutez un filtre pour calculer les proportions seulement sur les patients guéris et décédés. La proportion des décès est la létalité. Stockez ce nouveau data frame dans un objet df_sortie qui ressemble à ceci :
# A tibble: 2 × 3
statut_sortie n prop
<chr> <int> <dbl>
1 deces 503 0.105
2 gueri 4271 0.895
Bonus. La fonction drop_na(...) du paquet {tidyr} est un “raccourci” pour la commande filter(!is.na(...)) :
df_linelist |>
drop_na(statut_sortie) |>
count(statut_sortie)drop_na() est particulièrement utile car vous pouvez lui donner plusieurs noms de colonnes pour filtrer. Mais attention, cela supprimera toutes les lignes où une ou plusieurs de ces colonnes ont une valeur manquante. Faites très attention à votre nombre d’observations !
Tableau récapitulatif : statistiques par sous-préfecture
Maintenant que nous avons produit quelques tableaux de fréquences simples, nous pouvons augmenter la complexité. Une tâche courante en épidémiologie consiste à examiner les statistiques résumées par groupe d’une variable (i.e. stratifiés par une variable catégorique).
Dans ce tutoriel, nous allons ensemble construire une grosse table rassemblant de nombreuses statistiques descriptives sur les patients, stratifiées par sous-préfecture. Nous vous montrerons comment créer les statistiques descriptives suivantes :
- Combien de patients ont été consultés ?
- Quel est leur âge moyen ?
- Quelle a été la date d’admission la plus ancienne ?
- Combien de patients ont été hospitalisés ?
- etc.
Notre table finale ressemblera à ça :
# A tibble: 7 × 10
sous_prefecture n_patients moy_age min_admission n_femmes n_hosp moy_age_hosp
<chr> <int> <dbl> <date> <int> <int> <dbl>
1 Moissala 1808 6.84 2022-08-14 923 612 5.49
2 Bouna 1376 6.56 2023-01-11 669 412 5.67
3 Bedjondo 534 7.07 2023-06-09 251 184 5.21
4 Bekourou 496 6.84 2023-06-17 251 164 6.04
5 Bedaya 435 7.10 2023-07-04 209 147 6.16
6 Koumra 253 7.11 2023-08-14 138 84 6.26
7 Goundi 178 6.79 2023-08-22 88 66 5.53
# ℹ 3 more variables: moy_age_femmes <dbl>, prop_femmes <dbl>, prop_hosp <dbl>
C’est exactement pour cela que la fonction summarize() a été créée ! Elle nous permet de calculer des statistiques résumées sur un jeu de données, et la syntaxe est similaire à celle de mutate() :
# NE PAS EXÉCUTER (PSEUDO-CODE)
df |>
mutate(nouvelle_colonne = fonction(une_colonne))
df |>
summarize(.by = stratifier_par,
nouvelle_colonne = fonction_qui_résumme(une_colonne))Considérons le code suivant, où nous résumons les données pour calculer l’âge moyen de tous les patients.
df_linelist |>
summarize(moy_age = mean(age))# A tibble: 1 × 1
moy_age
<dbl>
1 6.82
Notez que ce code renvoi une seule valeur pour l’âge moyen. Aucune variable n’a été fournie par laquelle stratifier, donc summarize() a renvoyé une statistique récapitulative pour l’ensemble du data frame. Pour calculer l’âge moyen par groupe (i.e., stratifié par une variable), nous devons fournir une variable (ou plusieurs dans un vecteur) à l’argument .by :
df_linelist |>
summarize(.by = sexe, # Faire le résumé (ici, la moyenne) par sexe
moy_age = mean(age))# A tibble: 2 × 2
sexe moy_age
<chr> <dbl>
1 f 6.77
2 m 6.87
Jetez un œil aux résultats ci-dessus. Comment les interprétez-vous ?
Nous allons à présent nous entraîner à utiliser summarize() en créant la table par sous-préfecture que nous vous avons montré plus haut, étape par étape.
Commençons par appeler un summarize() vide qui va illustrer que toutes les statistiques seront calculées par sous_prefecture.
Exécutez le code suivant :
df_linelist |>
summarize(.by = sous_prefecture)Que se passe-t-il lorsque vous exécutez ces lignes ?
Effectifs
Nous voulons d’abord examiner le nombre de patients dans chaque sous_prefecture. Cela peut être fait en utilisant la fonction n() qui compte les lignes :
df_linelist |>
summarize(.by = sous_prefecture,
n_patients = n()) # effectifs de chaque sous-préfectureReproduire les lignes ci-dessus, ce sera la base de votre table. Notez que pour le moment, c’est équivalent à faire un count() (qui est un raccourcis de summarize() + n()).
Statistiques descriptives de variables continues
Nous pouvons utiliser les fonctions mean(), median(), min(), max() (et autres) pour résumer les variables continues. Par exemple, nous pouvons calculer l’âge moyen :
df_linelist |>
summarize(
.by = sous_prefecture, # calcule tout le reste par sous-préfecture
n_patients = n(), # nombre de patients
moy_age = mean(age) # age moyen
)# A tibble: 7 × 3
sous_prefecture n_patients moy_age
<chr> <int> <dbl>
1 Moissala 1808 6.84
2 Bouna 1376 6.56
3 Bedjondo 534 7.07
4 Bekourou 496 6.84
5 Bedaya 435 7.10
6 Koumra 253 7.11
7 Goundi 178 6.79
Ajoutez la ligne pour calculer l’âge moyen par sous préfecture dans votre table. Puis ajoutez une colonne min_admission qui contient la date d’admission minimale par sous-préfecture à votre tableau. Êtes-vous satisfait des résultats ?
N’oubliez pas qu’avec les fonctions arithmétiques telles que mean(), median(), min(), max(), vous devez indiquer explicitement à R de supprimer NA.
Effectifs avec une condition
Nous pourrions être intéressés par le nombre de patients qui répondent à une condition. Le nombre de patients de sexe féminin par exemple. On peut rajouter une condition lors du calcul des effectifs avec cette syntaxe :
# NE PAS EXÉCUTER (PSEUDO-CODE)
summarize(sum_category = sum(CONDITION_LOGIQUE, na.rm = TRUE))Cette somme nous permet de compter toutes les lignes où notre condition a été remplie (c’est à dire qu’elle renvoi TRUE). Par exemple :
df_linelist |>
summarize(
.by = sous_prefecture, # calcule tout le reste par sous-préfecture
n_femmes = sum(sexe == "f", na.rm = TRUE) # Nombre de femmes
)Ajoutez la ligne pour compter le nombre de femmes à votre code, puis ajoutez une variable n_hosp à votre tableau qui compte le nombre de patients hospitalisés (c’est-à-dire les lignes qui ont “oui” dans la variable hospitalisation).
Pour le moment votre table ressemble à ceci :
# A tibble: 7 × 6
sous_prefecture n_patients moy_age min_admission n_femmes n_hosp
<chr> <int> <dbl> <date> <int> <int>
1 Moissala 1808 6.84 2022-08-14 923 612
2 Bouna 1376 6.56 2023-01-11 669 412
3 Bedjondo 534 7.07 2023-06-09 251 184
4 Bekourou 496 6.84 2023-06-17 251 164
5 Bedaya 435 7.10 2023-07-04 209 147
6 Koumra 253 7.11 2023-08-14 138 84
7 Goundi 178 6.79 2023-08-22 88 66
Autres statistiques avec une condition
Parfois, nous voulons produire une statistique plus compliquée, par exemple l’âge moyen de tous les patients hospitalisés. Ici, la syntaxe est un peu différente :
# NE PAS EXÉCUTER (PSEUDO-CODE)
df |>
summarize(moyenne_categorie = mean(colonne_a_utiliser[CONDITION_LOGIQUE], na.rm = TRUE))Ici, nous avons :
- Indiqué quelle statistique nous voulons utiliser (
mean()) - Indiqué sur quelle colonne nous voulons calculer cette statistique (
colonne_a_utiliser) - Fourni une condition indiquant les observations de cette colonne à utiliser dans le calcul (
[CONDITION_LOGIQUE])
Par exemple, pour calculer la moyenne de la variable age uniquement pour les patients hospitalisés (c’est-à-dire dans les lignes où hospitalisation == "oui") nous écrivons :
df_linelist |>
summarize(.by = sous_prefecture,
n_patients = n(),
moy_age_hosp = mean(age[hospitalisation == "oui"], na.rm = TRUE))L’utilisation d’un test logique dans l’exemple ci-dessus est appelée indexation logique, où une condition est utilisée pour filtrer les observations que vous souhaitez prendre en compte lors d’un calcul. L’indexation logique est très puissante, mais plus complexe à écrire; ne vous inquiétez pas si ça vous prend quelques essais pour comprendre, c’est normal.
Ajoutez le calcul de l’age moyen des patients hospitalisés à votre table, puis ajoutez une colonne moy_age_femmes pour calculer l’âge moyen des patientES.
Votre table ressemble maintenant à ceci :
# A tibble: 7 × 8
sous_prefecture n_patients moy_age min_admission n_femme n_hosp moy_age_hosp
<chr> <int> <dbl> <date> <int> <int> <dbl>
1 Moissala 1808 6.84 2022-08-14 923 612 5.49
2 Bouna 1376 6.56 2023-01-11 669 412 5.67
3 Bedjondo 534 7.07 2023-06-09 251 184 5.21
4 Bekourou 496 6.84 2023-06-17 251 164 6.04
5 Bedaya 435 7.10 2023-07-04 209 147 6.16
6 Koumra 253 7.11 2023-08-14 138 84 6.26
7 Goundi 178 6.79 2023-08-22 88 66 5.53
# ℹ 1 more variable: moy_age_femmes <dbl>
Utiliser le data frame créé
Enfin, n’oubliez pas que summarize() renvoie un data frame que nous pouvons ensuite manipuler davantage (par exemple : avec filter() et mutate()).
Ajoutez un mutate() après avoir produit votre tableau récapitulatif pour calculer :
- La proportion de patients hospitalisés par sous-préfecture
- La proportion de patientES par sous-préfecture
L’en-tête de votre tableau final devrait ressembler à ceci :
# A tibble: 6 × 10
sous_prefecture n_patients moy_age min_admission n_femmes n_hosp moy_age_hosp
<chr> <int> <dbl> <date> <int> <int> <dbl>
1 Moissala 1808 6.84 2022-08-14 923 612 5.49
2 Bouna 1376 6.56 2023-01-11 669 412 5.67
3 Bedjondo 534 7.07 2023-06-09 251 184 5.21
4 Bekourou 496 6.84 2023-06-17 251 164 6.04
5 Bedaya 435 7.10 2023-07-04 209 147 6.16
6 Koumra 253 7.11 2023-08-14 138 84 6.26
# ℹ 3 more variables: moy_age_femmes <dbl>, prop_femmes <dbl>, prop_hosp <dbl>
C’est fini !
Soyiez fiers de vous, la création de tableaux récapitulatifs est une compétence importante pour un épidémiologistes, et le faire en R est très efficace ! N’oubliez pas de sauvegarder votre code !
Pour aller plus loin
Exercices supplémentaires
-
Statistiques par age : créez un tableau qui contient les statistiques suivantes par groupe d’âge :
- le nombre de patients
- la proportion d’hommes
- le nombre de décès
- la létalité (proportion de décès parmis les sorties connues)
- le nombre de décès parmi les patients atteints de pneumonie
-
Vaccination par âge : créez un tableau stratifié par classe d’âge qui montre :
- le nombre et la proportion de patients vaccinés contre la rougeole (oral ou par carte)
- le nombre et la proportion de patients ayant reçu une dose
- le nombre et la proportion de patients ayant reçu deux doses
-
Symptômes et signes : créez un tableau stratifié par le statut d’hospitalisation contenant :
- le nombre de patients
- la proportion de patients positifs au paludisme
- la proportion de patients avec de la fièvre
- la proportion de patients avec une éruption cutanée
- la proportion de patients avec de la toux
- la proportion de patients avec de des yeux rouges
- la proportion de patients avec une pneumonie
- la proportion de patients avec une encephalite
- la proportion de patients avec une malnutrition aïgue (MAS ou MAM, c’est à dire PB < 125 mm)
Calculer le nombre moyen de jours entre l’apparition des premiers symptômes et la consultation, par sous-préfecture.
Calculer la durée de séjour moyenne à l’hôpital (i.e. jours entre l’admission et la sortie) par statut à la sortie non manquant.
Exercices de défi
- Créez un tableau stratifié par sub-prefecture qui compte le nombre de patients décédés parmi ceux qui ont < 6 mois. Indice. Vous voulez compter les lignes (donc utiliser
sum()) qui remplissent une condition spécifique pour le résultat (statut_sortie == "deces"), mais uniquement lorsqueage_group == "< 6 months".
Ressources supplémentaires
Le chapitre du manuel EpiR sur le regroupement des données
-
Une fois que vous avez des tableaux, vous pouvez les personnaliser pour la publication à l’aide du paquet
{gt}: