Tableaux récapitulatifs

Core
Tableaux résumés
Résummer les données dans des tables

Objectifs

  • Réviser la sélection des lignes avec filter() et la création de variables avec mutate()
  • 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
Astuce

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éfecture

Reproduire 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 ?

Astuce

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

  1. 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
  2. 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
  3. 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)
  4. Calculer le nombre moyen de jours entre l’apparition des premiers symptômes et la consultation, par sous-préfecture.

  5. 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

  1. 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 lorsque age_group == "< 6 months".

Ressources supplémentaires