0% ont trouvé ce document utile (0 vote)
293 vues797 pages

Analyse de données d'enquêtes avec R

Ce guide décrit comment analyser des données d'enquêtes avec le langage R. Il contient de nombreuses sections sur les bases du langage, la manipulation et le traitement des données.

Transféré par

Kamel Kamel
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

Thèmes abordés

  • Data science,
  • dplyr,
  • Analyse bivariée,
  • Analyse de réseaux,
  • Modèles de comptage,
  • Tests de Chi²,
  • Modèles statistiques,
  • Visualisation de données,
  • Exportation de données,
  • Packages R pour l'analyse
0% ont trouvé ce document utile (0 vote)
293 vues797 pages

Analyse de données d'enquêtes avec R

Ce guide décrit comment analyser des données d'enquêtes avec le langage R. Il contient de nombreuses sections sur les bases du langage, la manipulation et le traitement des données.

Transféré par

Kamel Kamel
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

Thèmes abordés

  • Data science,
  • dplyr,
  • Analyse bivariée,
  • Analyse de réseaux,
  • Modèles de comptage,
  • Tests de Chi²,
  • Modèles statistiques,
  • Visualisation de données,
  • Exportation de données,
  • Packages R pour l'analyse

guide-R

Guide pour l’analyse de données d’enquêtes avec R

Joseph Larmarange

21 février 2024
Table des matières

Préface 15
Remerciements . . . . . . . . . . . . . . . . . . . . . . 17
Licence . . . . . . . . . . . . . . . . . . . . . . . . . . 17

I Bases du langage 18

1 Packages 19
1.1 Installation (CRAN) . . . . . . . . . . . . . . . . 20
1.2 Chargement . . . . . . . . . . . . . . . . . . . . . 20
1.3 Mise à jour . . . . . . . . . . . . . . . . . . . . . 21
1.4 Installation depuis GitHub . . . . . . . . . . . . . 22
1.5 Le tidyverse . . . . . . . . . . . . . . . . . . . . . 23

2 Vecteurs 26
2.1 Types et classes . . . . . . . . . . . . . . . . . . . 26
2.2 Création d’un vecteur . . . . . . . . . . . . . . . 27
2.3 Longueur d’un vecteur . . . . . . . . . . . . . . . 30
2.4 Combiner des vecteurs . . . . . . . . . . . . . . . 31
2.5 Vecteurs nommés . . . . . . . . . . . . . . . . . . 31
2.6 Indexation par position . . . . . . . . . . . . . . 33
2.7 Indexation par nom . . . . . . . . . . . . . . . . 34
2.8 Indexation par condition . . . . . . . . . . . . . . 35
2.9 Assignation par indexation . . . . . . . . . . . . 39
2.10 En résumé . . . . . . . . . . . . . . . . . . . . . . 40
2.11 webin-R . . . . . . . . . . . . . . . . . . . . . . . 41

3 Listes 42
3.1 Propriétés et création . . . . . . . . . . . . . . . 42
3.2 Indexation . . . . . . . . . . . . . . . . . . . . . . 45
3.3 En résumé . . . . . . . . . . . . . . . . . . . . . . 49
3.4 webin-R . . . . . . . . . . . . . . . . . . . . . . . 49

2
4 Tableaux de données 50
4.1 Propriétés et création . . . . . . . . . . . . . . . 50
4.2 Indexation . . . . . . . . . . . . . . . . . . . . . . 52
4.3 Afficher les données . . . . . . . . . . . . . . . . . 56
4.4 En résumé . . . . . . . . . . . . . . . . . . . . . . 65
4.5 webin-R . . . . . . . . . . . . . . . . . . . . . . . 65

5 Tibbles 66
5.1 Le concept de tidy data . . . . . . . . . . . . . . 66
5.2 tibbles : des tableaux de données améliorés . . . 66
5.3 Données et tableaux imbriqués . . . . . . . . . . 71

6 Attributs 74

II Manipulation de données 77

7 Le pipe 78
7.1 Le pipe natif de R : |> . . . . . . . . . . . . . . . 79
7.2 Le pipe du tidyverse : %>% . . . . . . . . . . . . . 80
7.3 Vaut-il mieux utiliser |> ou %>% ? . . . . . . . . . 81
7.4 Accéder à un élément avec purrr::pluck() et
purrr::chuck() . . . . . . . . . . . . . . . . . . 82

8 dplyr 85
8.1 Opérations sur les lignes . . . . . . . . . . . . . . 86
8.1.1 filter() . . . . . . . . . . . . . . . . . . . . 86
8.1.2 slice() . . . . . . . . . . . . . . . . . . . . 91
8.1.3 arrange() . . . . . . . . . . . . . . . . . . 92
8.1.4 slice_sample() . . . . . . . . . . . . . . . 94
8.1.5 distinct() . . . . . . . . . . . . . . . . . . 95
8.2 Opérations sur les colonnes . . . . . . . . . . . . 97
8.2.1 select() . . . . . . . . . . . . . . . . . . . 97
8.2.2 relocate() . . . . . . . . . . . . . . . . . . 102
8.2.3 rename() . . . . . . . . . . . . . . . . . . 103
8.2.4 rename_with() . . . . . . . . . . . . . . . 104
8.2.5 pull() . . . . . . . . . . . . . . . . . . . . 105
8.2.6 mutate() . . . . . . . . . . . . . . . . . . . 105
8.3 Opérations groupées . . . . . . . . . . . . . . . . 106
8.3.1 group_by() . . . . . . . . . . . . . . . . . 106
8.3.2 summarise() . . . . . . . . . . . . . . . . . 111
8.3.3 count() . . . . . . . . . . . . . . . . . . . 113

3
8.3.4 Grouper selon plusieurs variables . . . . . 114
8.4 Cheatsheet . . . . . . . . . . . . . . . . . . . . . 119
8.5 webin-R . . . . . . . . . . . . . . . . . . . . . . . 119

9 Facteurs et forcats 120


9.1 Création d’un facteur . . . . . . . . . . . . . . . . 120
9.2 Changer l’ordre des modalités . . . . . . . . . . . 123
9.3 Modifier les modalités . . . . . . . . . . . . . . . 128
9.4 Découper une variable numérique en classes . . . 134

10 Combiner plusieurs variables 139


10.1 if_else() . . . . . . . . . . . . . . . . . . . . . . . 139
10.2 case_when() . . . . . . . . . . . . . . . . . . . . 142
10.3 recode_if() . . . . . . . . . . . . . . . . . . . . . 144

11 Étiquettes de variables 149


11.1 Principe . . . . . . . . . . . . . . . . . . . . . . . 149
11.2 Manipulation sur un vecteur / une colonne . . . 151
11.3 Manipulation sur un tableau de données . . . . . 153
11.4 Préserver les étiquettes . . . . . . . . . . . . . . . 154

12 Étiquettes de valeurs 156


12.1 La classe haven_labelled . . . . . . . . . . . . . 156
12.2 Manipulation sur un vecteur / une colonne . . . 157
12.3 Manipulation sur un tableau de données . . . . . 161
12.4 Conversion . . . . . . . . . . . . . . . . . . . . . 162
12.4.1 Quand convertir les vecteurs labellisés ? . 162
12.4.2 Convertir un vecteur labellisé en facteur . 164
12.4.3 Convertir un vecteur labellisé en numé-
rique ou en texte . . . . . . . . . . . . . . 166
12.4.4 Conversion conditionnelle en facteurs . . . 168

13 Valeurs manquantes 172


13.1 Valeurs manquantes étiquetées (tagged NAs) . . . 173
13.1.1 Création et test . . . . . . . . . . . . . . . 173
13.1.2 Valeurs uniques, doublons et tris . . . . . 176
13.1.3 Tagged NAs et étiquettes de valeurs . . . 177
13.1.4 Conversion en user NAs . . . . . . . . . . 179
13.2 Valeurs manquantes définies par l’utilisateurs
(user NAs) . . . . . . . . . . . . . . . . . . . . . 180
13.2.1 Création . . . . . . . . . . . . . . . . . . . 181
13.2.2 Tests . . . . . . . . . . . . . . . . . . . . . 183

4
13.2.3 Conversion . . . . . . . . . . . . . . . . . 184

14 Import & Export de données 187


14.1 Importer un fichier texte . . . . . . . . . . . . . . 187
14.1.1 Structure d’un fichier texte . . . . . . . . 187
14.1.2 Interface graphique avec RStudio . . . . . 188
14.1.3 Dans un script . . . . . . . . . . . . . . . 189
14.2 Importer un fichier Excel . . . . . . . . . . . . . 190
14.3 Importer depuis des logiciels de statistique . . . . 191
14.3.1 SPSS . . . . . . . . . . . . . . . . . . . . 191
14.3.2 SAS . . . . . . . . . . . . . . . . . . . . . 192
14.3.3 Stata . . . . . . . . . . . . . . . . . . . . . 193
14.3.4 dBase . . . . . . . . . . . . . . . . . . . . 193
14.4 Sauver ses données . . . . . . . . . . . . . . . . . 193
14.5 Export de tableaux de données . . . . . . . . . . 195

15 Mettre en forme des nombres 196


15.1 label_number() . . . . . . . . . . . . . . . . . . 197
15.2 Les autres fonctions de {scales} . . . . . . . . . 199
15.2.1 label_comma() . . . . . . . . . . . . . . . 199
15.2.2 label_percent() . . . . . . . . . . . . . 200
15.2.3 label_dollar() . . . . . . . . . . . . . . 200
15.2.4 label_pvalue() . . . . . . . . . . . . . . 201
15.2.5 label_scientific() . . . . . . . . . . . 201
15.2.6 label_bytes() . . . . . . . . . . . . . . . 202
15.2.7 label_ordinal() . . . . . . . . . . . . . 202
15.2.8 label_date(), label_date_short() &
label_time() . . . . . . . . . . . . . . . 203
15.2.9 label_wrap() . . . . . . . . . . . . . . . 203
15.3 Les fonctions de formatage de {gtsummary} . . . 204
15.3.1 style_number() . . . . . . . . . . . . . . 204
15.3.2 style_sigfig() . . . . . . . . . . . . . . 205
15.3.3 style_percent() . . . . . . . . . . . . . 206
15.3.4 style_pvalue() . . . . . . . . . . . . . . 207
15.3.5 style_ratio() . . . . . . . . . . . . . . . 207
15.4 Bonus : signif_stars() de {ggstats} . . . . . 208

16 Couleurs & Palettes 209


16.1 Noms de couleur . . . . . . . . . . . . . . . . . . 209
16.2 Couleurs RVB et code hexadécimal . . . . . . . . 210

5
16.3 Palettes de couleurs . . . . . . . . . . . . . . . . 211
16.3.1 Color Brewer . . . . . . . . . . . . . . . . 211
16.3.2 Palettes de Paul Tol . . . . . . . . . . . . 213
16.3.3 Interface unifiée avec {paletteer} . . . . 215

III Analyses 218

17 Graphiques avec ggplot2 219


17.1 Ressources . . . . . . . . . . . . . . . . . . . . . . 219
17.2 Les bases de ggplot2 . . . . . . . . . . . . . . . . 219
17.3 Cheatsheet . . . . . . . . . . . . . . . . . . . . . 224
17.4 Exploration visuelle avec esquisse . . . . . . . . 224
17.5 webin-R . . . . . . . . . . . . . . . . . . . . . . . 226
17.6 Combiner plusieurs graphiques . . . . . . . . . . 227

18 Statistique univariée & Intervalles de confiance 233


18.1 Exploration graphique . . . . . . . . . . . . . . . 233
18.1.1 Variable continue . . . . . . . . . . . . . . 233
18.1.2 Variable catégorielle . . . . . . . . . . . . 237
18.2 Tableaux et tris à plat . . . . . . . . . . . . . . . 240
18.2.1 Thème du tableau . . . . . . . . . . . . . 242
18.2.2 Étiquettes des variables . . . . . . . . . . 244
18.2.3 Statistiques affichées . . . . . . . . . . . . 247
18.2.4 Affichage du nom des statistiques . . . . . 251
18.2.5 Forcer le type de variable . . . . . . . . . 253
18.2.6 Afficher des statistiques sur plusieurs
lignes (variables continues) . . . . . . . . 256
18.2.7 Mise en forme des statistiques . . . . . . . 257
18.2.8 Données manquantes . . . . . . . . . . . . 262
18.2.9 Ajouter les effectifs observés . . . . . . . . 264
18.3 Calcul manuel . . . . . . . . . . . . . . . . . . . . 264
18.3.1 Variable continue . . . . . . . . . . . . . . 264
18.3.2 Variable catégorielle . . . . . . . . . . . . 266
18.4 Intervalles de confiance . . . . . . . . . . . . . . . 268
18.4.1 Avec gtsummary . . . . . . . . . . . . . . 268
18.4.2 Calcul manuel . . . . . . . . . . . . . . . 270
18.5 webin-R . . . . . . . . . . . . . . . . . . . . . . . 274

6
19 Statistique bivariée & Tests de comparaison 275
19.1 Deux variables catégorielles . . . . . . . . . . . . 275
19.1.1 Tableau croisé avec gtsummary . . . . . . 275
19.1.2 Représentations graphiques . . . . . . . . 278
19.1.3 Calcul manuel . . . . . . . . . . . . . . . 285
19.1.4 Test du Chi² et dérivés . . . . . . . . . . . 288
19.1.5 Comparaison de deux proportions . . . . 290
19.2 Une variable continue selon une variable catégo-
rielle . . . . . . . . . . . . . . . . . . . . . . . . . 294
19.2.1 Tableau comparatif avec gtsummary . . . 294
19.2.2 Représentations graphiques . . . . . . . . 296
19.2.3 Calcul manuel . . . . . . . . . . . . . . . 302
19.2.4 Tests de comparaison . . . . . . . . . . . 303
19.2.5 Différence de deux moyennes . . . . . . . 306
19.3 Deux variables continues . . . . . . . . . . . . . . 307
19.3.1 Représentations graphiques . . . . . . . . 307
19.3.2 Tester la relation entre les deux variables 314
19.4 Matrice de corrélations . . . . . . . . . . . . . . . 315
19.5 webin-R . . . . . . . . . . . . . . . . . . . . . . . 317

20 Échelles de Likert 318


20.1 Exemple de données . . . . . . . . . . . . . . . . 318
20.2 Tableau de fréquence . . . . . . . . . . . . . . . . 319
20.3 Représentations graphiques . . . . . . . . . . . . 323

21 Régression linéaire 328


21.1 Modèle à une seule variable explicative continue . 328
21.2 Modèle à une seule variable explicative catégorielle333
21.3 Modèle à plusieurs variables explicatives . . . . . 336

22 Régression logistique binaire 338


22.1 Préparation des données . . . . . . . . . . . . . . 338
22.2 Statistiques descriptives . . . . . . . . . . . . . . 345
22.3 Calcul de la régression logistique binaire . . . . . 347
22.4 Interpréter les coefficients . . . . . . . . . . . . . 348
22.5 La notion d’odds ratio . . . . . . . . . . . . . . . 351
22.6 Afficher les écarts-types plutôt que les intervalles
de confiance . . . . . . . . . . . . . . . . . . . . . 355
22.7 Afficher toutes les comparaisons (pairwise
contrasts) . . . . . . . . . . . . . . . . . . . . . . 359
22.8 Identifier les variables ayant un effet significatif . 362

7
22.9 Régressions logistiques univariées . . . . . . . . . 365
22.10Présenter l’ensemble des résultats dans un même
tableau . . . . . . . . . . . . . . . . . . . . . . . 367
22.11webin-R . . . . . . . . . . . . . . . . . . . . . . . 369

23 Sélection pas à pas d’un modèle 370


23.1 Données d’illustration . . . . . . . . . . . . . . . 370
23.2 Présentation de l’AIC . . . . . . . . . . . . . . . 372
23.3 Sélection pas à pas descendante . . . . . . . . . . 372
23.4 Sélection pas à pas ascendante . . . . . . . . . . 378
23.5 Forcer certaines variables dans le modèle réduit . 381
23.6 Minimisation du BIC . . . . . . . . . . . . . . . . 382
23.7 Afficher les indicateurs de performance . . . . . . 384
23.8 Sélection pas à pas et valeurs manquantes . . . . 385

24 Prédictions marginales, contrastes marginaux & ef-


fets marginaux 390
24.1 Terminologie . . . . . . . . . . . . . . . . . . . . 391
24.2 Données d’illustration . . . . . . . . . . . . . . . 392
24.3 Prédictions marginales . . . . . . . . . . . . . . . 395
24.3.1 Prédictions marginales moyennes . . . . . 395
24.3.2 Prédictions marginales à la moyenne . . . 403
24.3.3 Variantes . . . . . . . . . . . . . . . . . . 412
24.4 Contrastes marginaux . . . . . . . . . . . . . . . 415
24.4.1 Contrastes marginaux moyens . . . . . . . 415
24.4.2 Contrastes marginaux à la moyenne . . . 423
24.5 Pentes marginales / Effets marginaux . . . . . . 426
24.5.1 Pentes marginales moyennes / Effets
marginaux moyens . . . . . . . . . . . . . 426
24.5.2 Pentes marginales à la moyenne / Effets
marginaux à la moyenne . . . . . . . . . . 431
24.6 Lectures complémentaires (en anglais) . . . . . . 432
24.7 webin-R . . . . . . . . . . . . . . . . . . . . . . . 432

25 Contrastes (variables catégorielles) 433


25.1 Contrastes de type traitement . . . . . . . . . . . 433
25.1.1 Exemple 1 : un modèle linéaire avec une
variable catégorielle . . . . . . . . . . . . 433
25.1.2 Exemple 2 : une régression logistique avec
deux variables catégorielles . . . . . . . . 436
25.1.3 Changer la modalité de référence . . . . . 441

8
25.2 Contrastes de type somme . . . . . . . . . . . . . 444
25.2.1 Exemple 1 : un modèle linéaire avec une
variable catégorielle . . . . . . . . . . . . 444
25.2.2 Exemple 2 : une régression logistique avec
deux variables catégorielles . . . . . . . . 448
25.3 Contrastes par différences successives . . . . . . . 450
25.3.1 Exemple 1 : un modèle linéaire avec une
variable catégorielle . . . . . . . . . . . . 451
25.3.2 Exemple 2 : une régression logistique avec
deux variables catégorielles . . . . . . . . 452
25.4 Autres types de contrastes . . . . . . . . . . . . . 455
25.4.1 Contrastes de type Helmert . . . . . . . . 455
25.4.2 Contrastes polynomiaux . . . . . . . . . . 457
25.5 Lectures additionnelles . . . . . . . . . . . . . . . 458

26 Interactions 459
26.1 Données d’illustration . . . . . . . . . . . . . . . 459
26.2 Modèle sans interaction . . . . . . . . . . . . . . 460
26.3 Définition d’une interaction . . . . . . . . . . . . 462
26.4 Significativité de l’interaction . . . . . . . . . . . 464
26.5 Interprétation des coefficients . . . . . . . . . . . 466
26.6 Définition alternative de l’interaction . . . . . . . 471
26.7 Identifier les interactions pertinentes . . . . . . . 475
26.8 Pour aller plus loin . . . . . . . . . . . . . . . . . 477
26.9 webin-R . . . . . . . . . . . . . . . . . . . . . . . 478

27 Multicolinéarité 479
27.1 Définition . . . . . . . . . . . . . . . . . . . . . . 479
27.2 Mesure de la colinéarité . . . . . . . . . . . . . . 481
27.3 La multicolinéarité est-elle toujours un problème ?487
27.4 webin-R . . . . . . . . . . . . . . . . . . . . . . . 489

IV Données pondérées avec survey 490

28 Définir un plan d’échantillonnage 491


28.1 Différents types d’échantillonnage . . . . . . . . . 491
28.2 Avec survey::svydesign() . . . . . . . . . . . . 492
28.3 Avec srvyr::as_survey_design() . . . . . . . . 497
28.4 webin-R . . . . . . . . . . . . . . . . . . . . . . . 501

9
29 Manipulation de données pondérées 502
29.1 Utilisation de {srvyr} . . . . . . . . . . . . . . . 503
29.2 Lister / Rechercher des variables . . . . . . . . . 505
29.3 Extraire un sous-échantillon . . . . . . . . . . . . 506

30 Analyses uni- et bivariées pondérées 507


30.1 La fonction tbl_svysummary() . . . . . . . . . . 507
30.2 Calcul manuel avec {survey} . . . . . . . . . . . 512
30.3 Intervalles de confiance et tests statistiques . . . 515
30.4 Calcul manuel avec {srvyr} . . . . . . . . . . . . 517
30.5 Impact du plan d’échantillonnage . . . . . . . . . 520

31 Graphiques pondérés 525

32 Régression logistique binaire pondérée 529


32.1 Données des exemples . . . . . . . . . . . . . . . 529
32.2 Calcul de la régression logistique binaire . . . . . 530
32.3 Sélection de modèle . . . . . . . . . . . . . . . . . 531
32.4 Affichage des résultats . . . . . . . . . . . . . . . 533
32.5 Prédictions marginales . . . . . . . . . . . . . . . 535

V Manipulation avancée 537

33 Fusion de tables 538


33.1 Jointures avec dplyr . . . . . . . . . . . . . . . . 538
33.1.1 Clés implicites . . . . . . . . . . . . . . . 538
33.1.2 Clés explicites . . . . . . . . . . . . . . . . 541
33.1.3 Types de jointures . . . . . . . . . . . . . 544
33.2 Jointures avec merge() . . . . . . . . . . . . . . . 550
33.3 Ajouter des observations avec bind_rows() . . . 551

34 Dates avec lubridate 555


34.1 Création de dates / de dates-heures . . . . . . . . 555
34.1.1 lors de l’import d’un fichier CSV . . . . . 556
34.1.2 à partir d’une chaîne de caractères . . . . 560
34.1.3 à partir des composants . . . . . . . . . . 561
34.1.4 conversion . . . . . . . . . . . . . . . . . . 563
34.2 Manipuler les composants d’une date/date-heure 563
34.2.1 Extraire un composant . . . . . . . . . . . 563
34.2.2 Arrondis . . . . . . . . . . . . . . . . . . . 565
34.2.3 Modifier un composant . . . . . . . . . . 566

10
34.3 Durées, périodes, intervalles & Arithmétique . . . 567
34.3.1 Durées (Duration) . . . . . . . . . . . . . 567
34.3.2 Périodes (Period) . . . . . . . . . . . . . . 570
34.3.3 Intervalles (Interval) . . . . . . . . . . . . 573
34.4 Calcul d’un âge . . . . . . . . . . . . . . . . . . . 576
34.5 Fuseaux horaires . . . . . . . . . . . . . . . . . . 577
34.6 Pour aller plus loin . . . . . . . . . . . . . . . . . 580

35 Chaînes de texte avec stringr 581


35.1 Concaténer des chaînes . . . . . . . . . . . . . . . 582
35.2 Convertir en majuscules / minuscules . . . . . . . 583
35.3 Découper des chaînes . . . . . . . . . . . . . . . . 584
35.4 Extraire des sous-chaînes par position . . . . . . 586
35.5 Détecter des motifs . . . . . . . . . . . . . . . . . 586
35.6 Expressions régulières . . . . . . . . . . . . . . . 588
35.7 Extraire des motifs . . . . . . . . . . . . . . . . . 588
35.8 Remplacer des motifs . . . . . . . . . . . . . . . . 590
35.9 Modificateurs de motifs . . . . . . . . . . . . . . 591
35.10Insérer une variable dans une chaîne de caractères592
35.11Ressources . . . . . . . . . . . . . . . . . . . . . . 593

36 Réorganisation avec tidyr 594


36.1 Tidy data . . . . . . . . . . . . . . . . . . . . . . 594
36.2 Trois règles pour des données bien rangées . . . . 596
36.3 pivot_longer() : rassembler des colonnes . . . . 599
36.4 pivot_wider() : disperser des lignes . . . . . . . 601
36.5 separate() : séparer une colonne en plusieurs
colonnes . . . . . . . . . . . . . . . . . . . . . . . 604
36.6 separate_rows() : séparer une colonne en plu-
sieurs lignes . . . . . . . . . . . . . . . . . . . . . 605
36.7 unite() : regrouper plusieurs colonnes en une
seule . . . . . . . . . . . . . . . . . . . . . . . . . 607
36.8 extract() : créer de nouvelles colonnes à partir
d’une colonne de texte . . . . . . . . . . . . . . . 608
36.9 complete() : compléter des combinaisons de va-
riables manquantes . . . . . . . . . . . . . . . . . 610
36.10Ressources . . . . . . . . . . . . . . . . . . . . . . 613
36.11Fichiers volumineux . . . . . . . . . . . . . . . . 614
36.12webin-R . . . . . . . . . . . . . . . . . . . . . . . 614

11
37 Conditions logiques 615
37.1 Opérateurs de comparaison . . . . . . . . . . . . 615
37.2 Comparaison et valeurs manquantes . . . . . . . 618
37.3 Opérateurs logiques (algèbre booléenne) . . . . . 620
37.3.1 Opérations logiques et Valeurs manquantes622
37.3.2 L’opérateur %in% . . . . . . . . . . . . . . 623
37.4 Aggrégation . . . . . . . . . . . . . . . . . . . . . 623
37.5 Programmation . . . . . . . . . . . . . . . . . . . 624

38 Transformations multiples 626


38.1 Transformations multiples sur les colonnes . . . . 626
38.1.1 Usage de base . . . . . . . . . . . . . . . . 626
38.1.2 Fonctions multiples . . . . . . . . . . . . . 631
38.1.3 Accéder à la colonne courante . . . . . . . 632
38.1.4 pick() . . . . . . . . . . . . . . . . . . . . 633
38.2 Sélection de lignes à partir d’une sélection de
colonnes . . . . . . . . . . . . . . . . . . . . . . . 635
38.3 Transformations multiples sur les lignes . . . . . 636
38.3.1 Création . . . . . . . . . . . . . . . . . . . 636
38.3.2 Statistiques ligne par ligne . . . . . . . . . 639

VI Analyses avancées 644

39 Analyse factorielle 645


39.1 Principe général . . . . . . . . . . . . . . . . . . 646
39.2 Première illustration : ACM sur les loisirs . . . . 647
39.2.1 Calcul de l’ACM . . . . . . . . . . . . . . 649
39.2.2 Exploration graphique interactive . . . . . 649
39.2.3 Représentations graphiques . . . . . . . . 650
39.2.4 Variance expliquée et valeurs propres . . . 651
39.2.5 Contribution aux axes . . . . . . . . . . . 653
39.2.6 Représentation des modalités dans le
plan factoriel . . . . . . . . . . . . . . . . 654
39.2.7 Représentation des individus dans le plan
factoriel . . . . . . . . . . . . . . . . . . . 656
39.2.8 Récupérer les coordonnées des individus
/ des variables . . . . . . . . . . . . . . . 660
39.3 Ajout de variables / d’observations additionnelles 661
39.4 Gestion des valeurs manquantes . . . . . . . . . . 664
39.5 webin-R . . . . . . . . . . . . . . . . . . . . . . . 668

12
39.6 Lectures additionnelles . . . . . . . . . . . . . . . 668

40 Classification ascendante hiérarchique 669


40.1 Calculer une matrice des distances . . . . . . . . 669
40.1.1 Distance de Gower . . . . . . . . . . . . . 670
40.1.2 Distance du Φ² . . . . . . . . . . . . . . . 672
40.1.3 Illustration . . . . . . . . . . . . . . . . . 673
40.2 Calcul du dendrogramme . . . . . . . . . . . . . 674
40.2.1 Représentation graphique du dendro-
gramme . . . . . . . . . . . . . . . . . . . 676
40.3 Découper le dendrogramme . . . . . . . . . . . . 678
40.3.1 Classes obtenues avec la distance de Gower678
40.3.2 Classes obtenues à partir de l’ACM (dis-
tance du Φ²) . . . . . . . . . . . . . . . . 683
40.4 Calcul de l’ACM et de la CAH avec FactoMineR 688
40.5 Caractériser la typologie . . . . . . . . . . . . . . 693
40.6 webin-R . . . . . . . . . . . . . . . . . . . . . . . 700

41 Régression logistique multinomiale 701


41.1 Données d’illustration . . . . . . . . . . . . . . . 701
41.2 Calcul du modèle multinomial . . . . . . . . . . . 703
41.3 Affichage des résultats du modèle . . . . . . . . . 704
41.4 Données pondérées . . . . . . . . . . . . . . . . . 714
41.4.1 avec svrepmisc::svymultinom() . . . . 714
41.4.2 avec svyVGAM::svy_glm() . . . . . . . . . 718
41.5 webin-R . . . . . . . . . . . . . . . . . . . . . . . 720

42 Régression logistique ordinale 721


42.1 Données d’illustration . . . . . . . . . . . . . . . 721
42.2 Calcul du modèle ordinal . . . . . . . . . . . . . 723
42.2.1 avec MASS::polr() . . . . . . . . . . . . . 723
42.2.2 Fonctions alternatives . . . . . . . . . . . 724
42.3 Affichage des résultats du modèle . . . . . . . . . 726
42.4 Données pondérées . . . . . . . . . . . . . . . . . 730
42.4.1 avec survey::svyolr() . . . . . . . . . . 730
42.4.2 avec svrepmisc::svymultinom() . . . . 730
42.4.3 avec svyVGAM::svy_glm() . . . . . . . . . 733

13
43 Modèles de comptage (Poisson & apparentés) 736
43.1 Modèle de Poisson . . . . . . . . . . . . . . . . . 736
43.1.1 Préparation des données du premier
exemple . . . . . . . . . . . . . . . . . . . 737
43.1.2 Calcul & Interprétation du modèle de
Poisson . . . . . . . . . . . . . . . . . . . 739
43.1.3 Évaluation de la surdispersion . . . . . . . 743
43.2 Modèle de quasi-Poisson . . . . . . . . . . . . . . 747
43.3 Modèle binomial négatif . . . . . . . . . . . . . . 750
43.4 Exemple avec une plus grande surdispersion . . . 754
43.5 Modèles de comptage avec une variable binaire . 758
43.6 Données pondérées et plan d’échantillonnage . . 766
43.7 Lectures complémentaires . . . . . . . . . . . . . 768

44 Modèles d’incidence / de taux 769


44.1 Premier exemple (données individuelles, évène-
ment unique) . . . . . . . . . . . . . . . . . . . . 770
44.2 Deuxième exemple (données agrégées) . . . . . . 772
44.3 Troisième exemple (données individuelles, évène-
ment unique) . . . . . . . . . . . . . . . . . . . . 774
44.4 Lectures complémentaires . . . . . . . . . . . . . 777

45 Modèles de comptage zero-inflated et hurdle 779


45.1 Données d’illustration . . . . . . . . . . . . . . . 779
45.2 Modèles de comptage classique . . . . . . . . . . 780
45.3 Modèles zero-inflated . . . . . . . . . . . . . . . . 782
45.4 Modèles hurdle . . . . . . . . . . . . . . . . . . . 789
45.5 Modèles de taux zero-inflated ou hurdle . . . . . 792
45.6 Lectures complémentaires . . . . . . . . . . . . . 792

46 Quel modèle choisir ? 794

VII Pour aller plus loin 795

47 Ressources documentaires 796


47.1 Ressources génériques . . . . . . . . . . . . . . . 796
47.2 Analyse de réseaux . . . . . . . . . . . . . . . . . 797
47.3 Analyse spatiale & Cartographie . . . . . . . . . 797
47.4 Analyse textuelle . . . . . . . . . . . . . . . . . . 797

14
Préface

Á Guide en cours d’écriture

Ce guide est encore incomplet. Le plan prévu est visible


à cette adresse : [Link]
R/issues/12.
En attendant, nous vous conseillons de compléter votre
lecture par le site analyse-R.

Ce guide porte sur l’analyse de données d’enquêtes avec le lo-


giciel R, un logiciel libre de statistiques et de traitement de
données. Les exemples présentés ici relèvent principalement du
champs des sciences sociales quantitatives et des sciences de
la santé. Ils peuvent néanmoins s’appliquer à d’autre champs
disciplinaires. Comme tout ouvrage, ce guide ne peut être ex-
haustif.
Ce guide présente comment réaliser des analyses statistiques et
diverses opérations courantes (comme la manipulation de don-
nées ou la production de graphiques) avec R. Il ne s’agit pas
d’un cours de statistiques : les différents chapitres présupposent
donc que vous avez déjà une connaissance des différentes tech-
niques présentées. Si vous souhaitez des précisions théoriques
/ méthodologiques à propos d’un certain type d’analyses, nous
vous conseillons d’utiliser votre moteur de recherche préféré. En
effet, on trouve sur internet de très nombreux supports de cours
(sans compter les nombreux ouvrages spécialisés disponibles en
librairie).
De même, il ne s’agit pas d’une introduction ou d’un guide pour
les utilisatrices et utilisateurs débutant·es. Si vous découvrez
R, nous vous conseillons la lecture de l’Introduction à R et au
tidyverse de Julien Barnier ([Link]
Néanmoins, quelques rappels sur les bases du langage sont four-
nis dans la section Bases du langage. Une bonne compréhension

15
de ces dernières, bien qu’un peu ardue de prime abord, permet
de comprendre le sens des commandes que l’on utilise et de
pleinement exploiter la puissance que R offre en matière de
manipulation de données.
R disposent de nombreuses extensions ou packages (plus de
16 000) et il existe souvent plusieurs manières de procéder pour
arriver au même résultat. En particulier, en matière de mani-
pulation de données, on oppose1 souvent base R qui repose sur 1
Une comparaison des deux syntaxes
les fonctions disponibles en standard dans R, la majorité étant est illustrée par une vignette dédiée
de dplyr.
fournies dans les packages {base}, {utils} ou encore {stats},
qui sont toujours chargés par défaut, et le {tidyverse} qui est
une collection de packages comprenant, entre autres, {dplyr},
{tibble}, {tidyr}, {forcats} ou encore {ggplot2}. Il y a un
débat ouvert, parfois passionné, sur le fait de privilégier l’une ou
l’autre approche, et les avantages et inconvénients de chacune
dépendent de nombreux facteurs, comme la lisibilité du code ou
bien les performances en temps de calcul. Dans ce guide, nous
avons adopté un point de vue pragmatique et utiliserons, le plus
souvent mais pas exclusivement, les fonctions du {tidyverse},
de même que nous avons privilégié d’autres packages, comme
{gtsummary} ou {ggstats} par exemple pour la statistique des-
criptive. Cela ne signifie pas, pour chaque point abordé, qu’il
s’agit de l’unique manière de procéder. Dans certains cas, il
s’agit simplement de préférences personnelles.
Bien qu’il en reprenne de nombreux contenus, ce guide ne se
substitue pas au site analyse-R. Il s’agit plutôt d’une version
complémentaire qui a vocation à être plus structurée et parfois
plus sélective dans les contenus présentés.
En complément, on pourra également se référer aux webin-R,
une série de vidéos avec partage d’écran, librement accessibles
sur YouTube : [Link]
Cette version du guide a utilisé r [Link]()[["[Link]"]].
Ce document est généré avec quarto et le code source est dis-
ponible sur GitHub. Pour toute suggestion ou correction, vous
pouvez ouvrir un ticket GitHub. Pour d’autres questions, vous
pouvez utiliser les forums de discussion disponibles en bas de
chaque page sur la version web du guide. Ce document est
régulièrement mis à jour. La dernière version est consultable
sur [Link]

16
Remerciements

Ce document a bénéficié de différents apports provenant notam-


ment de l’Introduction à R et de l’Introduction à R et au tidy-
verse de Julien Barnier et d’analyse-R : introduction à l’analyse
d’enquêtes avec R et RStudio. Certains chapitres se sont ap-
puyés sur l’ouvrage de référence R for data science par Hadley
Wickham, Mine Çetinkaya-Rundel et Garret Grolemund, ou en-
core sur les notes de cours d’Ewan Gallic.
Merci donc à Julien Barnier, Julien Biaudet, François Briatte,
Milan Bouchet-Valat, Mine Çetinkaya-Rundel, Ewen Gallic,
Frédérique Giraud, Joël Gombin, Garret Grolemund, Mayeul
Kauffmann, Christophe Lalanne, Nicolas Robette et Hadley
Wickham.

Licence

Ce document est mis à disposition selon les termes de la Li-


cence Creative Commons Attribution - Pas d’Utilisation Com-
merciale - Partage dans les Mêmes Conditions 4.0 Internatio-
nal.

17
partie I

Bases du langage

18
1 Packages

L’installation par défaut du logiciel R contient le cœur du pro-


gramme ainsi qu’un ensemble de fonctions de base fournissant
un grand nombre d’outils de traitement de données et d’analyse
statistiques.
R étant un logiciel libre, il bénéficie d’une forte communauté
d’utilisateurs qui peuvent librement contribuer au développe-
ment du logiciel en lui ajoutant des fonctionnalités supplémen-
taires. Ces contributions prennent la forme d’extensions (pa-
ckages en anglais) pouvant être installées par l’utilisateur et
fournissant alors diverses fonctionnalités supplémentaires.
Il existe un très grand nombre d’extensions (plus de 16 000
à ce jour), qui sont diffusées par un réseau baptisé CRAN
(Comprehensive R Archive Network).
La liste de toutes les extensions disponibles sur CRAN est
disponible ici : [Link]
Pour faciliter un peu le repérage des extensions, il existe un
ensemble de regroupements thématiques (économétrie, finance,
génétique, données spatiales…) baptisés Task views : [Link]
[Link]/web/views/.
On y trouve notamment une Task view dédiée aux sciences
sociales, listant de nombreuses extensions potentiellement utiles
pour les analyses statistiques dans ce champ disciplinaire : http:
//[Link]/web/views/[Link].
On peut aussi citer le site Awesome R ([Link]
inwf/awesome-R) qui fournit une liste d’extensions choisies et
triées par thématique.

19
1.1 Installation (CRAN)

L’installation d’une extension se fait par la fonction


[Link](), à qui on fournit le nom de l’extension.
Par exemple, si on souhaite installer l’extension {gtsummary} :

[Link]("gtsummary")

Sous RStudio, on pourra également cliquer sur Install dans


l’onglet Packages du quadrant inférieur droit.
Alternativement, on pourra avoir recours au package
{remotes} et à sa fonction remotes::install_cran() :

remotes::install_cran("gtsummary")

Ĺ Note

Le package {remotes} n’est pas disponible par dé-


faut sous R et devra donc être installé classiquement
avec [Link]("remotes"). À la différence de
[Link](), remotes::install_cran() vérifie
si le package est déjà installé et, si oui, si la version instal-
lée est déjà la dernière version, avant de procéder à une
installation complète si et seulement si cela est nécessaire.

1.2 Chargement

Une fois un package installé (c’est-à-dire que ses fichiers ont


été téléchargés et copiés sur votre ordinateur), ses fonctions
et objets ne sont pas directement accessibles. Pour pouvoir les
utiliser, il faut, à chaque session de travail, charger le pa-
ckage en mémoire avec la fonction library() ou la fonction
require() :

library(gtsummary)

20
À partir de là, on peut utiliser les fonctions de l’extension,
consulter leur page d’aide en ligne, accéder aux jeux de don-
nées qu’elle contient, etc.
Alternativement, pour accéder à un objet ou une fonction d’un
package sans avoir à le charger en mémoire, on pourra avoir re-
cours à l’opérateur ::. Ainsi, l’écriture p::f() signifie la fonc-
tion f() du package p. Cette écriture sera notamment utilisée
tout au long de ce guide pour indiquer à quel package appar-
tient telle fonction : remotes::install_cran() indique que la
fonction install_cran() provient du packages {remotes}.

ĺ Important

Il est important de bien comprendre la différence entre


[Link]() et library(). La première va cher-
cher un package sur internet et l’installe en local sur le
disque dur de l’ordinateur. On n’a besoin d’effectuer cette
opération qu’une seule fois. La seconde lit les informations
de l’extension sur le disque dur et les met à disposition de
R. On a besoin de l’exécuter à chaque début de session
ou de script.

1.3 Mise à jour

Pour mettre à jour l’ensemble des packages installés, il suffit


d’exécuter la fonction [Link]() :

[Link]()

Sous RStudio, on pourra alternativement cliquer sur Update


dans l’onglet Packages du quadrant inférieur droit.
Si on souhaite désinstaller une extension précédemment instal-
lée, on peut utiliser la fonction [Link]() :

[Link]("gtsummary")

21
Ď Installer / Mettre à jour les packages utilisés par un
projet

Après une mise à jour majeure de R, il est souvent né-


cessaire de réinstaller tous les packages utilisés. De même,
on peut parfois souhaiter mettre à jour uniquement les
packages utilisés par un projet donné sans avoir à mettre
à jour tous les autres packages présents sur son PC.
Une astuce consiste à avoir recours à la fonction
renv::dependencies() qui examine le code du projet
courant pour identifier les packages utilisés, puis à pas-
ser cette liste de packages à remotes::install_cran()
qui installera les packages manquants ou pour lesquels une
mise à jour est disponible.
Il vous suffit d’exécuter la commande ci-dessous :

renv::dependencies() |>
purrr::pluck("Package") |>
unique() |>
remotes::install_cran()

1.4 Installation depuis GitHub

Certains packages ne sont pas disponibles sur CRAN mais


seulement sur GitHub, une plateforme de développement in-
formatique. Il s’agit le plus souvent de packages qui ne sont pas
encore suffisamment matures pour être diffusés sur CRAN (sa-
chant que des vérifications strictes sont effectués avant qu’un
package ne soit référencés sur CRAN).
Dans d’autres cas de figure, la dernière version stable d’un pa-
ckage est disponible sur CRAN tandis que la version en cours
de développement est, elle, disponible sur GitHub. Il faut être
vigilant avec les versions de développement. Parfois, elle corrige
un bug ou introduit une nouvelle fonctionnalité qui n’est pas en-
core dans la version stable. Mais les versions de développement
peuvent aussi contenir de nouveaux bugs ou des fonctionnalités
instables.

22
Á Sous Windows

Pour les utilisatrices et utilisateurs sous Windows, il faut


être conscient que le code source d’un package doit être
compilé afin de pouvoir être utilisé. CRAN fournit une
version des packages déjà compilée pour Windows ce qui
facilite l’installation.
Par contre, lorsque l’on installe un package depuis Gi-
tHub, R ne récupère que le code source et il est donc
nécessaire de compiler localement le package. Pour cela, il
est nécessaire que soit installé sur le PC un outil complé-
mentaire appelé RTools. Il est téléchargeable à l’adresse
[Link]

Le code source du package {labelled} est disponible


sur GitHub à l’adresse h t t p s : / / g i t h u b . c o m / l a r m a
r a n g e / l a b e l l ed. Pour installer la version de dévelop-
pement de {labelled},on aura recours à la fonction
remotes::install_github() à laquelle on passera la
partie située à droite de [Link] dans l’URL
du package, à savoir :

remotes::install_github("larmarange/labelled")

1.5 Le tidyverse

Le terme {tidyverse} est une contraction de tidy (qu’on pour-


rait traduire par bien rangé) et de universe. Il s’agit en fait
d’une collection de packages conçus pour travailler ensemble et
basés sur une philosophie commune.
Ils abordent un très grand nombre d’opérations courantes dans
R (la liste n’est pas exhaustive) :

• visualisation ({ggplot2})
• manipulation des tableaux de données ({dplyr},
{tidyr})
• import/export de données ({readr}, {readxl}, {haven})
• manipulation de variables ({forcats}, {stringr},
{lubridate})

23
• programmation ({purrr}, {magrittr}, {glue})

Un des objectifs de ces extensions est de fournir des fonctions


avec une syntaxe cohérente, qui fonctionnent bien ensemble, et
qui retournent des résultats prévisibles. Elles sont en grande
partie issues du travail d’Hadley Wickham, qui travaille désor-
mais pour RStudio.
{tidyverse} est également le nom d’une extension générique
qui permets d’installer en une seule commande l’ensemble des
packages constituant le tidyverse :

[Link]("tidyverse")

Lorsque l’on charge le package {tidyverse} avec library(),


cela charge également en mémoire les principaux packages du
tidyverse2 . 2
Si on a besoin d’un autre package
du tidyverse comme {lubridate}, il
faudra donc le charger individuelle-
library(tidyverse)
ment.

-- Attaching core tidyverse packages ------------------------ tidyverse 2.0.0 --


v dplyr 1.1.2 v readr 2.1.4
v forcats 1.0.0 v stringr 1.5.0
v ggplot2 3.4.3 v tibble 3.2.1
v lubridate 1.9.2 v tidyr 1.3.0
v purrr 1.0.1
-- Conflicts ------------------------------------------ tidyverse_conflicts() --
x dplyr::filter() masks stats::filter()
x dplyr::lag() masks stats::lag()
i Use the conflicted package (<[Link] to force all conflicts to become

24
Figure 1.1: Packages chargés avec library(tidyverse)

25
2 Vecteurs

Les vecteurs sont l’objet de base de R et correspondent à une


liste de valeurs. Leurs propriétés fondamentales sont :

• les vecteurs sont unidimensionnels (i.e. ce sont des objets


à une seule dimension, à la différence d’une matrice par
exemple) ;
• toutes les valeurs d’un vecteur sont d’un seul et même
type ;
• les vecteurs ont une longueur qui correspond au nombre
de valeurs contenues dans le vecteur.

2.1 Types et classes

Dans R, il existe plusieurs types fondamentaux de vecteurs et,


en particulier, :

• les nombres réels (c’est-à-dire les nombres décimaux3 ), 3


Pour rappel, R étant anglophone,
par exemple 5.23 ; le caractère utilisé pour indiqué les
chiffres après la virgule est le point
• les nombres entiers, que l’on saisi en ajoutant le suffixe
(.).
L4 , par exemple 4L ;
4
• les chaînes de caractères (qui correspondent à du texte), R utilise 32 bits pour représenter
des nombres entiers, ce qui corres-
que l’on saisit avec des guillemets doubles (") ou simples pond en informatique à des entiers
('), par exemple "abc" ; longs ou long integers en anglais, d’où
• les valeurs logiques ou valeurs booléennes, à savoir vrai la lettre L utilisée pour indiquer un
ou faux, que l’on représente avec les mots TRUE et FALSE nombre entier.
(en majuscules5 ). 5
On peut également utiliser les rac-
courcis T et F. Cependant, pour une
En plus de ces types de base, il existe de nombreux autres types meilleure lisibilité du code, il est pré-
de vecteurs utilisés pour représenter toutes sortes de données, férable d’utiliser les versions longues
TRUE et FALSE.
comme les facteurs (voir Chapitre 9) ou les dates (voir Cha-
pitre 34).

26
La fonction class() renvoie la nature d’un vecteur tandis que
la fonction typeof() indique la manière dont un vecteur est
stocké de manière interne par R.

Table 2.1: Le type et la classe des principaux types de vecteurs

x class(x) typeof(x)
3L integer integer
5.3 numeric double
TRUE logical logical
"abc" character character
factor("a") factor integer
[Link]("2020-01-01") Date double

Ď Astuce

Pour un vecteur numérique, le type est "double" car R


utilise une double précision pour stocker en mémoire les
nombres réels.
En interne, les facteurs sont représentés par un nombre
entier auquel est attaché une étiquette, c’est pourquoi
typeof() renvoie "integer".
Quand aux dates, elles sont stockées en interne sous la
forme d’un nombre réel représentant le nombre de jours
depuis le 1er janvier 1970, d’où le fait que typeof() ren-
voie "double".

2.2 Création d’un vecteur

Pour créer un vecteur, on utilisera la fonction c() en lui passant


la liste des valeurs à combiner6 . 6
La lettre c est un raccourci du mot
anglais combine, puisque cette fonc-
tion permet de combiner des valeurs
taille <- c(1.88, 1.65, 1.92, 1.76, NA, 1.72)
individuelles dans un vecteur unique.
taille

[1] 1.88 1.65 1.92 1.76 NA 1.72

27
sexe <- c("h", "f", "h", "f", "f", "f")
sexe

[1] "h" "f" "h" "f" "f" "f"

urbain <- c(TRUE, TRUE, FALSE, FALSE, FALSE, TRUE)


urbain

[1] TRUE TRUE FALSE FALSE FALSE TRUE

Nous l’avons vu, toutes les valeurs d’un vecteur doivent obliga-
toirement être du même type. Dès lors, si on essaie de combiner
des valeurs de différents types, R essaiera de les convertir au
mieux. Par exemple :

x <- c(2L, 3.14, "a")


x

[1] "2" "3.14" "a"

class(x)

[1] "character"

Dans le cas présent, toutes les valeurs ont été converties en


chaînes de caractères.
Dans certaines situations, on peut avoir besoin de créer un vec-
teur d’une certaine longueur mais dont toutes les valeurs sont
identiques. Cela se réalise facilement avec rep() à qui on indi-
quera la valeur à répéter puis le nombre de répétitions :

rep(2, 10)

[1] 2 2 2 2 2 2 2 2 2 2

On peut aussi lui indiquer plusieurs valeurs qui seront alors


répétées en boucle :

28
rep(c("a", "b"), 3)

[1] "a" "b" "a" "b" "a" "b"

Dans d’autres situations, on peut avoir besoin de créer un vec-


teur contenant une suite de valeurs, ce qui se réalise aisément
avec seq() à qui on précisera les arguments from (point de
départ), to (point d’arrivée) et by (pas). Quelques exemples
valent mieux qu’un long discours :

seq(1, 10)

[1] 1 2 3 4 5 6 7 8 9 10

seq(5, 17, by = 2)

[1] 5 7 9 11 13 15 17

seq(10, 0)

[1] 10 9 8 7 6 5 4 3 2 1 0

seq(100, 10, by = -10)

[1] 100 90 80 70 60 50 40 30 20 10

seq(1.23, 5.67, by = 0.33)

[1] 1.23 1.56 1.89 2.22 2.55 2.88 3.21 3.54 3.87 4.20 4.53 4.86 5.19 5.52

L’opérateur : est un raccourci de la fonction seq() pour créer


une suite de nombres entiers. Il s’utilise ainsi :

29
1:5

[1] 1 2 3 4 5

24:32

[1] 24 25 26 27 28 29 30 31 32

55:43

[1] 55 54 53 52 51 50 49 48 47 46 45 44 43

2.3 Longueur d’un vecteur

La longueur d’un vecteur correspond au nombre de valeurs qui


le composent. Elle s’obtient avec length() :

length(taille)

[1] 6

length(c("a", "b"))

[1] 2

La longueur d’un vecteur vide (NULL) est zéro.

length(NULL)

[1] 0

30
2.4 Combiner des vecteurs

Pour combiner des vecteurs, rien de plus simple. Il suffit


d’utiliser c() ! Les valeurs des différents vecteurs seront mises
bout à bout pour créer un unique vecteur.

x <- c(2, 1, 3, 4)
length(x)

[1] 4

y <- c(9, 1, 2, 6, 3, 0)
length(y)

[1] 6

z <- c(x, y)
z

[1] 2 1 3 4 9 1 2 6 3 0

length(z)

[1] 10

2.5 Vecteurs nommés

Les différentes valeurs d’un vecteur peuvent être nommées. Une


première manière de nommer les éléments d’un vecteur est de
le faire à sa création :

sexe <- c(
Michel = "h", Anne = "f",
Dominique = NA, Jean = "h",

31
Claude = NA, Marie = "f"
)

Lorsqu’on affiche le vecteur, la présentation change quelque


peu.

sexe

Michel Anne Dominique Jean Claude Marie


"h" "f" NA "h" NA "f"

La liste des noms s’obtient avec names().

names(sexe)

[1] "Michel" "Anne" "Dominique" "Jean" "Claude" "Marie"

Pour ajouter ou modifier les noms d’un vecteur, on doit attri-


buer un nouveau vecteur de noms :

names(sexe) <- c("Michael", "Anna", "Dom", "John", "Alex", "Mary")


sexe

Michael Anna Dom John Alex Mary


"h" "f" NA "h" NA "f"

Pour supprimer tous les noms, il y a la fonction unname() :

anonyme <- unname(sexe)


anonyme

[1] "h" "f" NA "h" NA "f"

32
2.6 Indexation par position

L’indexation est l’une des fonctionnalités les plus puissantes


mais aussi les plus difficiles à maîtriser de R. Il s’agit
d’opérations permettant de sélectionner des sous-ensembles de
valeurs en fonction de différents critères. Il existe trois types
d’indexation : (i) l’indexation par position, (ii) l’indexation
par nom et (iii) l’indexation par condition. Le principe est
toujours le même : on indique entre crochets7 ([]) ce qu’on 7
Pour rappel, les crochets
souhaite garder ou non. s’obtiennent sur un clavier fran-
çais de type PC en appuyant sur la
Commençons par l’indexation par position encore appelée in- touche Alt Gr et la touche ( ou ).
dexation directe. Ce mode le plus simple d’indexation consiste
à indiquer la position des éléments à conserver.
Reprenons notre vecteur taille :

taille

[1] 1.88 1.65 1.92 1.76 NA 1.72

Si on souhaite le premier élément du vecteur, on peut faire :

taille[1]

[1] 1.88

Si on souhaite les trois premiers éléments ou les éléments 2, 5


et 6 :

taille[1:3]

[1] 1.88 1.65 1.92

taille[c(2, 5, 6)]

[1] 1.65 NA 1.72

Si on veut le dernier élément :

33
taille[length(taille)]

[1] 1.72

Il est tout à fait possible de sélectionner les valeurs dans le


désordre :

taille[c(5, 1, 4, 3)]

[1] NA 1.88 1.76 1.92

Dans le cadre de l’indexation par position, il est également pos-


sible de spécifier des nombres négatifs, auquel cas cela signifiera
toutes les valeurs sauf celles-là. Par exemple :

taille[c(-1, -5)]

[1] 1.65 1.92 1.76 1.72

À noter, si on indique une position au-delà de la longueur du


vecteur, R renverra NA. Par exemple :

taille[23:25]

[1] NA NA NA

2.7 Indexation par nom

Lorsqu’un vecteur est nommé, il est dès lors possible d’accéder


à ses valeurs à partir de leur nom. Il s’agit de l’indexation par
nom.

sexe["Anna"]

Anna
"f"

34
sexe[c("Mary", "Michael", "John")]

Mary Michael John


"f" "h" "h"

Par contre il n’est pas possible d’utiliser l’opérateur - comme


pour l’indexation directe. Pour exclure un élément en fonc-
tion de son nom, on doit utiliser une autre forme d’indexation,
l’indexation par condition, expliquée dans la section suivante.
On peut ainsi faire…

sexe[names(sexe) != "Dom"]

… pour sélectionner tous les éléments sauf celui qui s’appelle


Dom.

2.8 Indexation par condition

L’indexation par condition consiste à fournir un vecteur logique


indiquant si chaque élément doit être inclus (si TRUE) ou exclu
(si FALSE). Par exemple :

sexe

Michael Anna Dom John Alex Mary


"h" "f" NA "h" NA "f"

sexe[c(TRUE, FALSE, FALSE, TRUE, FALSE, FALSE)]

Michael John
"h" "h"

Écrire manuellement une telle condition n’est pas très pratique


à l’usage. Mais supposons que nous ayons également à notre
disposition les deux vecteurs suivants, également de longueur
6.

35
urbain <- c(TRUE, TRUE, FALSE, FALSE, FALSE, TRUE)
poids <- c(80, 63, 75, 87, 82, 67)

Le vecteur urbain est un vecteur logique. On peut directement


l’utiliser pour avoir le sexe des enquêtés habitant en milieu
urbain :

sexe[urbain]

Michael Anna Mary


"h" "f" "f"

Supposons qu’on souhaite maintenant avoir la taille des indi-


vidus pesant 80 kilogrammes ou plus. Nous pouvons effectuer
une comparaison à l’aide des opérateurs de comparaison sui-
vants :
Table 2.2: Opérateurs de comparaison

Opérateur de comparaison Signification


== égal à
%in% appartient à
!= différent de
> strictement supérieur à
< strictement inférieur à
>= supérieur ou égal à
<= inférieur ou égal à

Voyons tout de suite un exemple :

poids >= 80

[1] TRUE FALSE FALSE TRUE TRUE FALSE

Que s’est-il passé ? Nous avons fourni à R une condition et il


nous a renvoyé un vecteur logique avec autant d’éléments qu’il
y a d’observations et dont la valeur est TRUE si la condition

36
est remplie et FALSE dans les autres cas. Nous pouvons alors
utiliser ce vecteur logique pour obtenir la taille des participants
pesant 80 kilogrammes ou plus :

taille[poids >= 80]

[1] 1.88 1.76 NA

On peut combiner ou modifier des conditions à l’aide des opé-


rateurs logiques habituels :

Table 2.3: Opérateurs logiques

Opérateur logique Signification


& et logique
| ou logique
! négation logique

Supposons que je veuille identifier les personnes pesant 80 kilo-


grammes ou plus et vivant en milieu urbain :

poids >= 80 & urbain

[1] TRUE FALSE FALSE FALSE FALSE FALSE

Les résultats sont différents si je souhaite isoler les personnes


pesant 80 kilogrammes ou plus ou vivant milieu urbain :

poids >= 80 | urbain

[1] TRUE TRUE FALSE TRUE TRUE TRUE

ĺ Comparaison et valeur manquante

Une remarque importante : quand l’un des termes d’une


condition comporte une valeur manquante (NA), le résultat

37
de cette condition n’est pas toujours TRUE ou FALSE, il
peut aussi être à son tour une valeur manquante.

taille

[1] 1.88 1.65 1.92 1.76 NA 1.72

taille > 1.8

[1] TRUE FALSE TRUE FALSE NA FALSE

On voit que le test NA > 1.8 ne renvoie ni vrai ni faux,


mais NA.
Une autre conséquence importante de ce comportement
est qu’on ne peut pas utiliser l’opérateur l’expression ==
NA pour tester la présence de valeurs manquantes. On uti-
lisera à la place la fonction ad hoc [Link]() :

[Link](taille > 1.8)

[1] FALSE FALSE FALSE FALSE TRUE FALSE

Pour compliquer encore un peu le tout, lorsqu’on utilise


une condition pour l’indexation, si la condition renvoie NA,
R ne sélectionne pas l’élément mais retourne quand même
la valeur NA. Ceci a donc des conséquences sur le résultat
d’une indexation par comparaison.
Par exemple si je cherche à connaître le poids des per-
sonnes mesurant 1,80 mètre ou plus :

taille

[1] 1.88 1.65 1.92 1.76 NA 1.72

poids

[1] 80 63 75 87 82 67

poids[taille > 1.8]

38
[1] 80 75 NA

Les éléments pour lesquels la taille n’est pas connue ont


été transformés en NA, ce qui n’influera pas le calcul d’une
moyenne. Par contre, lorsqu’on utilisera assignation et in-
dexation ensemble, cela peut créer des problèmes. Il est
donc préférable lorsqu’on a des valeurs manquantes de les
exclure ainsi :

poids[taille > 1.8 & ![Link](taille)]

[1] 80 75

2.9 Assignation par indexation

L’indexation peut être combinée avec l’assignation (opérateur


<-) pour modifier seulement certaines parties d’un vecteur. Ceci
fonctionne pour les différents types d’indexation évoqués précé-
demment.

v <- 1:5
v

[1] 1 2 3 4 5

v[1] <- 3
v

[1] 3 2 3 4 5

sexe["Alex"] <- "non-binaire"


sexe

Michael Anna Dom John Alex


"h" "f" NA "h" "non-binaire"
Mary
"f"

39
Enfin on peut modifier plusieurs éléments d’un seul coup soit en
fournissant un vecteur, soit en profitant du mécanisme de recy-
clage. Les deux commandes suivantes sont ainsi rigoureusement
équivalentes :

sexe[c(1,3,4)] <- c("Homme", "Homme", "Homme")


sexe[c(1,3,4)] <- "Homme"

L’assignation par indexation peut aussi être utilisée pour ajou-


ter une ou plusieurs valeurs à un vecteur :

length(sexe)

[1] 6

sexe[7] <- "f"


sexe

Michael Anna Dom John Alex


"Homme" "f" "Homme" "Homme" "non-binaire"
Mary
"f" "f"

length(sexe)

[1] 7

2.10 En résumé

• Un vecteur est un objet unidimensionnel contenant une


liste de valeurs qui sont toutes du même type (entières,
numériques, textuelles ou logiques).
• La fonction class() permet de connaître le type du vec-
teur et la fonction length() sa longueur, c’est-à-dire son
nombre d’éléments.
• La fonction c() sert à créer et à combiner des vecteurs.

40
• Les valeurs manquantes sont représentées avec NA.
• Un vecteur peut être nommé, c’est-à-dire qu’un nom tex-
tuel a été associé à chaque élément. Cela peut se faire lors
de sa création ou avec la fonction names().
• L’indexation consiste à extraire certains éléments d’un
vecteur. Pour cela, on indique ce qu’on souhaite extraire
entre crochets ([]) juste après le nom du vecteur. Le type
d’indexation dépend du type d’information transmise.
• S’il s’agit de nombres entiers, c’est l’indexation par posi-
tion : les nombres représentent la position dans le vecteur
des éléments qu’on souhaite extraire. Un nombre négatif
s’interprète comme tous les éléments sauf celui-là.
• Si on indique des chaînes de caractères, c’est l’indexation
par nom : on indique le nom des éléments qu’on souhaite
extraire. Cette forme d’indexation ne fonctionne que si le
vecteur est nommé.
• Si on transmet des valeurs logiques, le plus souvent sous
la forme d’une condition, c’est l’indexation par condition :
TRUE indique les éléments à extraire et FALSE les éléments
à exclure. Il faut être vigilant aux valeurs manquantes (NA)
dans ce cas précis.
• Enfin, il est possible de ne modifier que certains éléments
d’un vecteur en ayant recours à la fois à l’indexation ([])
et à l’assignation (<-).

2.11 webin-R

On pourra également se référer au webin-R #02 (les bases du


langage R) sur YouTube.
[Link]

41
3 Listes

Par nature, les vecteurs ne peuvent contenir que des valeurs


de même type (numérique, textuel ou logique). Or, on peut
avoir besoin de représenter des objets plus complexes composés
d’éléments disparates. C’est ce que permettent les listes.

3.1 Propriétés et création

Une liste se crée tout simplement avec la fonction list() :

l1 <- list(1:5, "abc")


l1

[[1]]
[1] 1 2 3 4 5

[[2]]
[1] "abc"

Une liste est un ensemble d’objets, quels qu’ils soient, chaque


élément d’une liste pouvant avoir ses propres dimensions. Dans
notre exemple précédent, nous avons créé une liste l1 compo-
sée de deux éléments : un vecteur d’entiers de longueur 5 et un
vecteur textuel de longueur 1. La longueur d’une liste corres-
pond aux nombres d’éléments qu’elle contient et s’obtient avec
length() :

length(l1)

[1] 2

42
Comme les vecteurs, une liste peut être nommée et les noms
des éléments d’une liste sont accessibles avec names() :

l2 <- list(
minuscules = letters,
majuscules = LETTERS,
mois = [Link]
)
l2

$minuscules
[1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s"
[20] "t" "u" "v" "w" "x" "y" "z"

$majuscules
[1] "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P" "Q" "R" "S"
[20] "T" "U" "V" "W" "X" "Y" "Z"

$mois
[1] "January" "February" "March" "April" "May" "June"
[7] "July" "August" "September" "October" "November" "December"

length(l2)

[1] 3

names(l2)

[1] "minuscules" "majuscules" "mois"

Que se passe-t-il maintenant si on effectue la commande sui-


vante ?

l <- list(l1, l2)

À votre avis, quelle est la longueur de cette nouvelle liste l ?


5?

43
length(l)

[1] 2

Eh bien non ! Elle est de longueur 2 car nous avons créé une
liste composée de deux éléments qui sont eux-mêmes des listes.
Cela est plus lisible si on fait appel à la fonction str() qui
permet de visualiser la structure d’un objet.

str(l)

List of 2
$ :List of 2
..$ : int [1:5] 1 2 3 4 5
..$ : chr "abc"
$ :List of 3
..$ minuscules: chr [1:26] "a" "b" "c" "d" ...
..$ majuscules: chr [1:26] "A" "B" "C" "D" ...
..$ mois : chr [1:12] "January" "February" "March" "April" ...

Une liste peut contenir tous types d’objets, y compris d’autres


listes. Pour combiner les éléments d’une liste, il faut utiliser la
fonction append() :

l <- append(l1, l2)


length(l)

[1] 5

str(l)

List of 5
$ : int [1:5] 1 2 3 4 5
$ : chr "abc"
$ minuscules: chr [1:26] "a" "b" "c" "d" ...
$ majuscules: chr [1:26] "A" "B" "C" "D" ...
$ mois : chr [1:12] "January" "February" "March" "April" ...

44
Ĺ Note

On peut noter en passant qu’une liste peut tout à fait


n’être que partiellement nommée.

3.2 Indexation

Les crochets simples ([]) fonctionnent comme pour les vecteurs.


On peut utiliser à la fois l’indexation par position, l’indexation
par nom et l’indexation par condition.

[[1]]
[1] 1 2 3 4 5

[[2]]
[1] "abc"

$minuscules
[1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s"
[20] "t" "u" "v" "w" "x" "y" "z"

$majuscules
[1] "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P" "Q" "R" "S"
[20] "T" "U" "V" "W" "X" "Y" "Z"

$mois
[1] "January" "February" "March" "April" "May" "June"
[7] "July" "August" "September" "October" "November" "December"

l[c(1,3,4)]

[[1]]
[1] 1 2 3 4 5

$minuscules

45
[1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s"
[20] "t" "u" "v" "w" "x" "y" "z"

$majuscules
[1] "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P" "Q" "R" "S"
[20] "T" "U" "V" "W" "X" "Y" "Z"

l[c("majuscules", "minuscules")]

$majuscules
[1] "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P" "Q" "R" "S"
[20] "T" "U" "V" "W" "X" "Y" "Z"

$minuscules
[1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s"
[20] "t" "u" "v" "w" "x" "y" "z"

l[c(TRUE, TRUE, FALSE, FALSE, TRUE)]

[[1]]
[1] 1 2 3 4 5

[[2]]
[1] "abc"

$mois
[1] "January" "February" "March" "April" "May" "June"
[7] "July" "August" "September" "October" "November" "December"

Même si on extrait un seul élément, l’extraction obtenue avec


les crochets simples renvoie toujours une liste, ici composée d’un
seul élément :

str(l[1])

List of 1
$ : int [1:5] 1 2 3 4 5

46
Supposons que je souhaite calculer la moyenne des valeurs du
premier élément de ma liste. Essayons la commande suivante :

mean(l[1])

Warning in [Link](l[1]): l'argument n'est ni numérique, ni logique :


renvoi de NA

[1] NA

Nous obtenons un message d’erreur. En effet, R ne sait pas


calculer une moyenne à partir d’une liste. Ce qu’il lui faut, c’est
un vecteur de valeurs numériques. Autrement dit, ce que nous
cherchons à obtenir c’est le contenu même du premier élément
de notre liste et non une liste à un seul élément.
C’est ici que les doubles crochets ([[]]) vont rentrer en jeu.
Pour ces derniers, nous pourrons utiliser l’indexation par posi-
tion ou l’indexation par nom, mais pas l’indexation par condi-
tion. De plus, le critère qu’on indiquera doit indiquer un et un
seul élément de notre liste. Au lieu de renvoyer une liste à un
élément, les doubles crochets vont renvoyer l’élément désigné.

str(l[1])

List of 1
$ : int [1:5] 1 2 3 4 5

str(l[[1]])

int [1:5] 1 2 3 4 5

Maintenant, nous pouvons calculer notre moyenne :

mean(l[[1]])

[1] 3

Nous pouvons aussi utiliser l’indexation par nom.

47
l[["mois"]]

[1] "January" "February" "March" "April" "May" "June"


[7] "July" "August" "September" "October" "November" "December"

Mais il faut avouer que cette écriture avec doubles crochets


et guillemets est un peu lourde. Heureusement, un nouvel ac-
teur entre en scène : le symbole dollar ($). C’est un raccourci
des doubles crochets pour l’indexation par nom qu’on utilise
ainsi :

l$mois

[1] "January" "February" "March" "April" "May" "June"


[7] "July" "August" "September" "October" "November" "December"

Les écritures l$mois et l[["mois"]] sont équivalentes. Atten-


tion ! Cela ne fonctionne que pour l’indexation par nom.

l$1

Error: unexpected numeric constant in "l$1"

L’assignation par indexation fonctionne également avec les


doubles crochets ou le signe dollar :

l[[2]] <- list(c("un", "vecteur", "textuel"))


l$mois <- c("Janvier", "Février", "Mars")
l

[[1]]
[1] 1 2 3 4 5

[[2]]
[[2]][[1]]
[1] "un" "vecteur" "textuel"

48
$minuscules
[1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s"
[20] "t" "u" "v" "w" "x" "y" "z"

$majuscules
[1] "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P" "Q" "R" "S"
[20] "T" "U" "V" "W" "X" "Y" "Z"

$mois
[1] "Janvier" "Février" "Mars"

3.3 En résumé

• Les listes sont des objets unidimensionnels pouvant conte-


nir tout type d’objet, y compris d’autres listes.
• Elles ont une longueur qu’on obtient avec length().
• On crée une liste avec list() et on peut fusionner des
listes avec append().
• Tout comme les vecteurs, les listes peuvent être
nommées et les noms des éléments s’obtiennent avec
base::names().
• Les crochets simples ([]) permettent de sélectionner les
éléments d’une liste, en utilisant l’indexation par position,
l’indexation par nom ou l’indexation par condition. Cela
renvoie toujours une autre liste.
• Les doubles crochets ([[]]) renvoient directement le
contenu d’un élément de la liste qu’on aura sélectionné
par position ou par nom.
• Le symbole $ est un raccourci pour facilement sélectionner
un élément par son nom, liste$nom étant équivalent à
liste[["nom"]].

3.4 webin-R

On pourra également se référer au webin-R #02 (les bases du


langage R) sur YouTube.
[Link]

49
4 Tableaux de données

Les tableaux de données, ou data frame en anglais, est un type


d’objets essentiel pour les données d’enquêtes.

4.1 Propriétés et création

Dans R, les tableaux de données sont tout simplement des listes


(voir Chapitre 3) avec quelques propriétés spécifiques :

• les tableaux de données ne peuvent contenir que des vec-


teurs ;
• tous les vecteurs d’un tableau de données ont la même
longueur ;
• tous les éléments d’un tableau de données sont nommés
et ont chacun un nom unique.

Dès lors, un tableau de données correspond aux fichiers de don-


nées qu’on a l’habitude de manipuler dans d’autres logiciels de
statistiques comme SPSS ou Stata. Les variables sont organi-
sées en colonnes et les observations en lignes.
On peut créer un tableau de données avec la fonction
[Link]() :

df <- [Link](
sexe = c("f", "f", "h", "h"),
age = c(52, 31, 29, 35),
blond = c(FALSE, TRUE, TRUE, FALSE)
)
df

sexe age blond


1 f 52 FALSE

50
2 f 31 TRUE
3 h 29 TRUE
4 h 35 FALSE

str(df)

'[Link]': 4 obs. of 3 variables:


$ sexe : chr "f" "f" "h" "h"
$ age : num 52 31 29 35
$ blond: logi FALSE TRUE TRUE FALSE

Un tableau de données étant une liste, la fonction length() ren-


verra le nombre d’éléments de la liste, donc dans le cas présent
le nombre de variables, et names() leurs noms :

length(df)

[1] 3

names(df)

[1] "sexe" "age" "blond"

Comme tous les éléments d’un tableau de données ont la même


longueur, cet objet peut être vu comme bidimensionnel. Les
fonctions nrow(), ncol() et dim() donnent respectivement le
nombre de lignes, le nombre de colonnes et les dimensions de
notre tableau.

nrow(df)

[1] 4

ncol(df)

[1] 3

51
dim(df)

[1] 4 3

De plus, tout comme les colonnes ont un nom, il est aussi pos-
sible de nommer les lignes avec [Link]() :

[Link](df) <- c("Anna", "Mary-Ann", "Michael", "John")


df

sexe age blond


Anna f 52 FALSE
Mary-Ann f 31 TRUE
Michael h 29 TRUE
John h 35 FALSE

4.2 Indexation

Les tableaux de données étant des listes, nous pouvons donc


utiliser les crochets simples ([]), les crochets doubles ([[]]) et
le symbole dollar ($) pour extraire des parties de notre tableau,
de la même manière que pour n’importe quelle liste.

df[1]

sexe
Anna f
Mary-Ann f
Michael h
John h

df[[1]]

[1] "f" "f" "h" "h"

52
df$sexe

[1] "f" "f" "h" "h"

Cependant, un tableau de données étant un objet bidimension-


nel, il est également possible d’extraire des données sur deux
dimensions, à savoir un premier critère portant sur les lignes et
un second portant sur les colonnes. Pour cela, nous utiliserons
les crochets simples ([]) en séparant nos deux critères par une
virgule (,).
Un premier exemple :

df

sexe age blond


Anna f 52 FALSE
Mary-Ann f 31 TRUE
Michael h 29 TRUE
John h 35 FALSE

df[3, 2]

[1] 29

Cette première commande indique que nous souhaitons la troi-


sième ligne de la seconde colonne, autrement dit l’âge de Mi-
chael. Le même résultat peut être obtenu avec l’indexation par
nom, l’indexation par condition, ou un mélange de tout ça.

df["Michael", "age"]

[1] 29

df[c(F, F, T, F), c(F, T, F)]

[1] 29

53
df[3, "age"]

[1] 29

df["Michael", 2]

[1] 29

Il est également possible de préciser un seul critère. Par exemple,


si je souhaite les deux premières observations, ou les variables
sexe et blond :

df[1:2,]

sexe age blond


Anna f 52 FALSE
Mary-Ann f 31 TRUE

df[,c("sexe", "blond")]

sexe blond
Anna f FALSE
Mary-Ann f TRUE
Michael h TRUE
John h FALSE

Il a suffi de laisser un espace vide avant ou après la virgule.

Á Avertissement

ATTENTION ! Il est cependant impératif de laisser la


virgule pour indiquer à R qu’on souhaite effectuer une
indexation à deux dimensions. Si on oublie la virgule, cela
nous ramène au mode de fonctionnement des listes. Et le
résultat n’est pas forcément le même :

54
df[2, ]

sexe age blond


Mary-Ann f 31 TRUE

df[, 2]

[1] 52 31 29 35

df[2]

age
Anna 52
Mary-Ann 31
Michael 29
John 35

Ĺ Note

Au passage, on pourra noter quelques subtilités sur le ré-


sultat renvoyé.

str(df[2, ])

'[Link]': 1 obs. of 3 variables:


$ sexe : chr "f"
$ age : num 31
$ blond: logi TRUE

str(df[, 2])

num [1:4] 52 31 29 35

str(df[2])

'[Link]': 4 obs. of 1 variable:


$ age: num 52 31 29 35

55
str(df[[2]])

num [1:4] 52 31 29 35

df[2, ] signifie qu’on veut toutes les variables pour le


second individu. Le résultat est un tableau de données
à une ligne et trois colonnes. df[2] correspond au mode
d’extraction des listes et renvoie donc une liste à un élé-
ment, en l’occurrence un tableau de données à quatre ob-
servations et une variable. df[[2]] quant à lui renvoie
le contenu de cette variable, soit un vecteur numérique
de longueur quatre. Reste df[, 2] qui renvoie toutes les
observations pour la seconde colonne. Or l’indexation bi-
dimensionnelle a un fonctionnement un peu particulier :
par défaut elle renvoie un tableau de données mais s’il
y a une seule variable dans l’extraction, c’est un vecteur
qui est renvoyé. Pour plus de détails, on pourra consulter
l’entrée d’aide help("[.[Link]").

4.3 Afficher les données

Prenons un tableau de données un peu plus conséquent, en


l’occurrence le jeu de données ?questionr::hdv2003 dispo-
nible dans l’extension {questionr} et correspondant à un ex-
trait de l’enquête Histoire de vie réalisée par l’INSEE en 2003.
Il contient 2000 individus et 20 variables.

library(questionr)
data(hdv2003)

Si on demande d’afficher l’objet hdv2003 dans la console (résul-


tat non reproduit ici), R va afficher l’ensemble du contenu de
hdv2003 à l’écran ce qui, sur un tableau de cette taille, ne sera
pas très lisible. Pour une exploration visuelle, le plus simple est
souvent d’utiliser la visionneuse intégrée à RStudio et qu’on
peut appeler avec la fonction View().

56
View(hdv2003)

Figure 4.1: Interface View() de R RStudio

Les fonctions head() et tail(), qui marchent également sur


les vecteurs, permettent d’afficher seulement les premières (res-
pectivement les dernières) lignes d’un tableau de données :

head(hdv2003)

id age sexe nivetud poids


1 1 28 Femme Enseignement superieur y compris technique superieur 2634.398
2 2 23 Femme <NA> 9738.396
3 3 59 Homme Derniere annee d'etudes primaires 3994.102
4 4 34 Homme Enseignement superieur y compris technique superieur 5731.662
5 5 71 Femme Derniere annee d'etudes primaires 4329.094
6 6 35 Femme Enseignement technique ou professionnel court 8674.699
occup qualif [Link] clso
1 Exerce une profession Employe 8 Oui
2 Etudiant, eleve <NA> 2 Oui
3 Exerce une profession Technicien 2 Non
4 Exerce une profession Technicien 1 Non
5 Retraite Employe 0 Oui
6 Exerce une profession Employe 5 Non
relig [Link] [Link]
1 Ni croyance ni appartenance Peu important Insatisfaction
2 Ni croyance ni appartenance <NA> <NA>

57
3 Ni croyance ni appartenance Aussi important que le reste Equilibre
4 Appartenance sans pratique Moins important que le reste Satisfaction
5 Pratiquant regulier <NA> <NA>
6 Ni croyance ni appartenance Le plus important Equilibre
[Link] [Link] [Link] cuisine bricol cinema sport [Link]
1 Non Non Non Oui Non Non Non 0
2 Non Non Non Non Non Oui Oui 1
3 Non Non Non Non Non Non Oui 0
4 Non Non Non Oui Oui Oui Oui 2
5 Non Non Non Non Non Non Non 3
6 Non Non Non Non Non Oui Oui 2

tail(hdv2003, 2)

id age sexe nivetud poids


1999 1999 24 Femme Enseignement technique ou professionnel court 13740.810
2000 2000 66 Femme Enseignement technique ou professionnel long 7709.513
occup qualif [Link] clso
1999 Exerce une profession Employe 2 Non
2000 Au foyer Employe 3 Non
relig [Link] [Link]
1999 Appartenance sans pratique Moins important que le reste Equilibre
2000 Appartenance sans pratique <NA> <NA>
[Link] [Link] [Link] cuisine bricol cinema sport [Link]
1999 Non Non Non Non Non Oui Non 0.3
2000 Non Oui Non Oui Non Non Non 0.0

L’extension {dplyr} propose une fonction dplyr::glimpse()


(ce qui signifie aperçu en anglais) qui permet de visualiser ra-
pidement et de manière condensée le contenu d’un tableau de
données.

library(dplyr)
glimpse(hdv2003)

Rows: 2,000
Columns: 20
$ id <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1~

58
$ age <int> 28, 23, 59, 34, 71, 35, 60, 47, 20, 28, 65, 47, 63, 67, ~
$ sexe <fct> Femme, Femme, Homme, Homme, Femme, Femme, Femme, Homme, ~
$ nivetud <fct> "Enseignement superieur y compris technique superieur", ~
$ poids <dbl> 2634.3982, 9738.3958, 3994.1025, 5731.6615, 4329.0940, 8~
$ occup <fct> "Exerce une profession", "Etudiant, eleve", "Exerce une ~
$ qualif <fct> Employe, NA, Technicien, Technicien, Employe, Employe, O~
$ [Link] <int> 8, 2, 2, 1, 0, 5, 1, 5, 4, 2, 3, 4, 1, 5, 2, 3, 4, 0, 2,~
$ clso <fct> Oui, Oui, Non, Non, Oui, Non, Oui, Non, Oui, Non, Oui, O~
$ relig <fct> Ni croyance ni appartenance, Ni croyance ni appartenance~
$ [Link] <fct> Peu important, NA, Aussi important que le reste, Moins i~
$ [Link] <fct> Insatisfaction, NA, Equilibre, Satisfaction, NA, Equilib~
$ [Link] <fct> Non, Non, Non, Non, Non, Non, Non, Non, Non, Non, Non, N~
$ [Link] <fct> Non, Non, Non, Non, Non, Non, Non, Non, Non, Non, Non, N~
$ [Link] <fct> Non, Non, Non, Non, Non, Non, Oui, Oui, Non, Non, Non, N~
$ cuisine <fct> Oui, Non, Non, Oui, Non, Non, Oui, Oui, Non, Non, Oui, N~
$ bricol <fct> Non, Non, Non, Oui, Non, Non, Non, Oui, Non, Non, Oui, O~
$ cinema <fct> Non, Oui, Non, Oui, Non, Oui, Non, Non, Oui, Oui, Oui, N~
$ sport <fct> Non, Oui, Oui, Oui, Non, Oui, Non, Non, Non, Oui, Non, O~
$ [Link] <dbl> 0.0, 1.0, 0.0, 2.0, 3.0, 2.0, 2.9, 1.0, 2.0, 2.0, 1.0, 0~

L’extension {labelled} propose une fonction labelled::look_for()


qui permet de lister les différentes variables d’un fichier de
données :

library(labelled)
look_for(hdv2003)

pos variable label col_type missing


1 id — int 0
2 age — int 0
3 sexe — fct 0

4 nivetud — fct 112

59
5 poids — dbl 0
6 occup — fct 0

7 qualif — fct 347

8 [Link] — int 0
9 clso — fct 0

10 relig — fct 0

11 [Link] — fct 952

12 [Link] — fct 952

13 [Link] — fct 0

14 [Link] — fct 0

15 [Link] — fct 0

16 cuisine — fct 0

17 bricol — fct 0

60
18 cinema — fct 0

19 sport — fct 0

20 [Link] — dbl 5
values

Homme
Femme
N'a jamais fait d'etudes
A arrete ses etudes, avant la derniere ann~
Derniere annee d'etudes primaires
1er cycle
2eme cycle
Enseignement technique ou professionnel co~
Enseignement technique ou professionnel lo~
Enseignement superieur y compris technique~

Exerce une profession


Chomeur
Etudiant, eleve
Retraite
Retire des affaires
Au foyer
Autre inactif
Ouvrier specialise
Ouvrier qualifie
Technicien
Profession intermediaire
Cadre
Employe
Autre

Oui
Non
Ne sait pas
Pratiquant regulier
Pratiquant occasionnel
Appartenance sans pratique

61
Ni croyance ni appartenance
Rejet
NSP ou NVPR
Le plus important
Aussi important que le reste
Moins important que le reste
Peu important
Satisfaction
Insatisfaction
Equilibre
Non
Oui
Non
Oui
Non
Oui
Non
Oui
Non
Oui
Non
Oui
Non
Oui

Lorsqu’on a un gros tableau de données avec de nombreuses


variables, il peut être difficile de retrouver la ou les variables
d’intérêt. Il est possible d’indiquer à labelled::look_for()
un mot-clé pour limiter la recherche. Par exemple :

look_for(hdv2003, "trav")

pos variable label col_type missing values


11 [Link] — fct 952 Le plus important
Aussi important que le reste
Moins important que le reste
Peu important
12 [Link] — fct 952 Satisfaction
Insatisfaction

62
Equilibre

Il est à noter que si la recherche n’est pas sensible à la casse


(i.e. aux majuscules et aux minuscules), elle est sensible aux
accents.
La méthode summary() qui fonctionne sur tout type d’objet
permet d’avoir quelques statistiques de base sur les différentes
variables de notre tableau, les statistiques affichées dépendant
du type de variable.

summary(hdv2003)

id age sexe
Min. : 1.0 Min. :18.00 Homme: 899
1st Qu.: 500.8 1st Qu.:35.00 Femme:1101
Median :1000.5 Median :48.00
Mean :1000.5 Mean :48.16
3rd Qu.:1500.2 3rd Qu.:60.00
Max. :2000.0 Max. :97.00

nivetud poids
Enseignement technique ou professionnel court :463 Min. : 78.08
Enseignement superieur y compris technique superieur:441 1st Qu.: 2221.82
Derniere annee d'etudes primaires :341 Median : 4631.19
1er cycle :204 Mean : 5535.61
2eme cycle :183 3rd Qu.: 7626.53
(Other) :256 Max. :31092.14
NA's :112
occup qualif [Link]
Exerce une profession:1049 Employe :594 Min. : 0.000
Chomeur : 134 Ouvrier qualifie :292 1st Qu.: 1.000
Etudiant, eleve : 94 Cadre :260 Median : 2.000
Retraite : 392 Ouvrier specialise :203 Mean : 3.283
Retire des affaires : 77 Profession intermediaire:160 3rd Qu.: 5.000
Au foyer : 171 (Other) :144 Max. :22.000
Autre inactif : 83 NA's :347
clso relig
Oui : 936 Pratiquant regulier :266
Non :1037 Pratiquant occasionnel :442

63
Ne sait pas: 27 Appartenance sans pratique :760
Ni croyance ni appartenance:399
Rejet : 93
NSP ou NVPR : 40

[Link] [Link] [Link] [Link]


Le plus important : 29 Satisfaction :480 Non:1986 Non:1953
Aussi important que le reste:259 Insatisfaction:117 Oui: 14 Oui: 47
Moins important que le reste:708 Equilibre :451
Peu important : 52 NA's :952
NA's :952

[Link] cuisine bricol cinema sport [Link]


Non:1776 Non:1119 Non:1147 Non:1174 Non:1277 Min. : 0.000
Oui: 224 Oui: 881 Oui: 853 Oui: 826 Oui: 723 1st Qu.: 1.000
Median : 2.000
Mean : 2.247
3rd Qu.: 3.000
Max. :12.000
NA's :5

On peut également appliquer summary() à une variable parti-


culière.

summary(hdv2003$sexe)

Homme Femme
899 1101

summary(hdv2003$age)

Min. 1st Qu. Median Mean 3rd Qu. Max.


18.00 35.00 48.00 48.16 60.00 97.00

64
4.4 En résumé

• Les tableaux de données sont des listes avec des propriétés


particulières :
i. tous les éléments sont des vecteurs ;
ii. tous les vecteurs ont la même longueur ;
iii. tous les vecteurs ont un nom et ce nom est unique.
• On peut créer un tableau de données avec [Link]().
• Les tableaux de données correspondent aux fichiers de
données qu’on utilise usuellement dans d’autres logiciels
de statistiques : les variables sont représentées en colonnes
et les observations en lignes.
• Ce sont des objets bidimensionnels : ncol() renvoie le
nombre de colonnes et nrow() le nombre de lignes.
• Les doubles crochets ([[]]) et le symbole dollar ($) fonc-
tionnent comme pour les listes et permettent d’accéder
aux variables.
• Il est possible d’utiliser des coordonnées bidimensionnelles
avec les crochets simples ([]) en indiquant un critère sur
les lignes puis un critère sur les colonnes, séparés par une
virgule (,).

4.5 webin-R

On pourra également se référer au webin-R #02 (les bases du


langage R) sur YouTube.
[Link]

65
5 Tibbles

5.1 Le concept de tidy data

Le {tidyverse} est en partie fondé sur le concept de tidy data,


développé à l’origine par Hadley Wickham dans un article de
2014 du Journal of Statistical Software.
Il s’agit d’un modèle d’organisation des données qui vise à facili-
ter le travail souvent long et fastidieux de nettoyage et de prépa-
ration préalable à la mise en oeuvre de méthodes d’analyse.
Les principes d’un jeu de données tidy sont les suivants :

1. chaque variable est une colonne


2. chaque observation est une ligne
3. chaque type d’observation est dans une table différente

Un chapitre dédié à {tidyr} (voir Chapitre 36) présente com-


ment définir et rendre des données tidy avec ce package.
Les extensions du {tidyverse}, notamment {ggplot2} et
{dplyr}, sont prévues pour fonctionner avec des données
tidy.

5.2 tibbles : des tableaux de données


améliorés

Une autre particularité du {tidyverse} est que ces exten-


sions travaillent avec des tableaux de données au format
tibble::tibble(), qui est une évolution plus moderne du
classique [Link] de R de base.
Ce format est fourni est géré par l’extension du même nom
({tibble}), qui fait partie du cœur du tidyverse. La plupart des

66
fonctions des extensions du tidyverse acceptent des [Link]
en entrée, mais retournent un tibble.
Contrairement aux data frames, les tibbles :

• n’ont pas de noms de lignes (rownames)


• autorisent des noms de colonnes invalides pour les data
frames (espaces, caractères spéciaux, nombres…) 8 8
Quand on veut utiliser des noms de
• s’affichent plus intelligemment que les data frames : seules ce type, on doit les entourer avec des
backticks (‘)
les premières lignes sont affichées, ainsi que quelques in-
formations supplémentaires utiles (dimensions, types des
colonnes…)
• ne font pas de partial matching sur les noms de colonnes
9 9
Dans R base, si une table d contient
• affichent un avertissement si on essaie d’accéder à une une colonne qualif, d$qual retour-
nera cette colonne.
colonne qui n’existe pas

Pour autant, les tibbles restent compatibles avec les data


frames.
Il est possible de créer un tibble manuellement avec
tibble::tibble().

library(tidyverse)
tibble(
x = c(1.2345, 12.345, 123.45, 1234.5, 12345),
y = c("a", "b", "c", "d", "e")
)

# A tibble: 5 x 2
x y
<dbl> <chr>
1 1.23 a
2 12.3 b
3 123. c
4 1234. d
5 12345 e

On peut ainsi facilement convertir un data frame en tibble avec


tibble::as_tibble() :

67
d <- as_tibble(mtcars)
d

# A tibble: 32 x 11
mpg cyl disp hp drat wt qsec vs am gear carb
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 21 6 160 110 3.9 2.62 16.5 0 1 4 4
2 21 6 160 110 3.9 2.88 17.0 0 1 4 4
3 22.8 4 108 93 3.85 2.32 18.6 1 1 4 1
4 21.4 6 258 110 3.08 3.22 19.4 1 0 3 1
5 18.7 8 360 175 3.15 3.44 17.0 0 0 3 2
6 18.1 6 225 105 2.76 3.46 20.2 1 0 3 1
7 14.3 8 360 245 3.21 3.57 15.8 0 0 3 4
8 24.4 4 147. 62 3.69 3.19 20 1 0 4 2
9 22.8 4 141. 95 3.92 3.15 22.9 1 0 4 2
10 19.2 6 168. 123 3.92 3.44 18.3 1 0 4 4
# i 22 more rows

D’ailleurs, quand on regarde la classe d’un tibble, on peut


s’apercevoir qu’un tibble hérite de la classe [Link] mais
possède en plus la classe tbl_df. Cela traduit bien le fait que
les tibbles restent des data frames.

class(d)

[1] "tbl_df" "tbl" "[Link]"

Si le data frame d’origine a des rownames, on peut d’abord les


convertir en colonnes avec tibble::rownames_to_column() :

d <- as_tibble(rownames_to_column(mtcars))
d

# A tibble: 32 x 12
rowname mpg cyl disp hp drat wt qsec vs am gear carb
<chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Mazda RX4 21 6 160 110 3.9 2.62 16.5 0 1 4 4
2 Mazda RX4 ~ 21 6 160 110 3.9 2.88 17.0 0 1 4 4

68
3 Datsun 710 22.8 4 108 93 3.85 2.32 18.6 1 1 4 1
4 Hornet 4 D~ 21.4 6 258 110 3.08 3.22 19.4 1 0 3 1
5 Hornet Spo~ 18.7 8 360 175 3.15 3.44 17.0 0 0 3 2
6 Valiant 18.1 6 225 105 2.76 3.46 20.2 1 0 3 1
7 Duster 360 14.3 8 360 245 3.21 3.57 15.8 0 0 3 4
8 Merc 240D 24.4 4 147. 62 3.69 3.19 20 1 0 4 2
9 Merc 230 22.8 4 141. 95 3.92 3.15 22.9 1 0 4 2
10 Merc 280 19.2 6 168. 123 3.92 3.44 18.3 1 0 4 4
# i 22 more rows

À l’inverse, on peut à tout moment convertir un tibble en data


frame avec tibble::[Link]() :

[Link](d)

rowname mpg cyl disp hp drat wt qsec vs am gear carb


1 Mazda RX4 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4 4
2 Mazda RX4 Wag 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4 4
3 Datsun 710 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4 1
4 Hornet 4 Drive 21.4 6 258.0 110 3.08 3.215 19.44 1 0 3 1
5 Hornet Sportabout 18.7 8 360.0 175 3.15 3.440 17.02 0 0 3 2
6 Valiant 18.1 6 225.0 105 2.76 3.460 20.22 1 0 3 1
7 Duster 360 14.3 8 360.0 245 3.21 3.570 15.84 0 0 3 4
8 Merc 240D 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4 2
9 Merc 230 22.8 4 140.8 95 3.92 3.150 22.90 1 0 4 2
10 Merc 280 19.2 6 167.6 123 3.92 3.440 18.30 1 0 4 4
11 Merc 280C 17.8 6 167.6 123 3.92 3.440 18.90 1 0 4 4
12 Merc 450SE 16.4 8 275.8 180 3.07 4.070 17.40 0 0 3 3
13 Merc 450SL 17.3 8 275.8 180 3.07 3.730 17.60 0 0 3 3
14 Merc 450SLC 15.2 8 275.8 180 3.07 3.780 18.00 0 0 3 3
15 Cadillac Fleetwood 10.4 8 472.0 205 2.93 5.250 17.98 0 0 3 4
16 Lincoln Continental 10.4 8 460.0 215 3.00 5.424 17.82 0 0 3 4
17 Chrysler Imperial 14.7 8 440.0 230 3.23 5.345 17.42 0 0 3 4
18 Fiat 128 32.4 4 78.7 66 4.08 2.200 19.47 1 1 4 1
19 Honda Civic 30.4 4 75.7 52 4.93 1.615 18.52 1 1 4 2
20 Toyota Corolla 33.9 4 71.1 65 4.22 1.835 19.90 1 1 4 1
21 Toyota Corona 21.5 4 120.1 97 3.70 2.465 20.01 1 0 3 1
22 Dodge Challenger 15.5 8 318.0 150 2.76 3.520 16.87 0 0 3 2
23 AMC Javelin 15.2 8 304.0 150 3.15 3.435 17.30 0 0 3 2
24 Camaro Z28 13.3 8 350.0 245 3.73 3.840 15.41 0 0 3 4

69
25 Pontiac Firebird 19.2 8 400.0 175 3.08 3.845 17.05 0 0 3 2
26 Fiat X1-9 27.3 4 79.0 66 4.08 1.935 18.90 1 1 4 1
27 Porsche 914-2 26.0 4 120.3 91 4.43 2.140 16.70 0 1 5 2
28 Lotus Europa 30.4 4 95.1 113 3.77 1.513 16.90 1 1 5 2
29 Ford Pantera L 15.8 8 351.0 264 4.22 3.170 14.50 0 1 5 4
30 Ferrari Dino 19.7 6 145.0 175 3.62 2.770 15.50 0 1 5 6
31 Maserati Bora 15.0 8 301.0 335 3.54 3.570 14.60 0 1 5 8
32 Volvo 142E 21.4 4 121.0 109 4.11 2.780 18.60 1 1 4 2

Là encore, on peut convertir la colonne rowname en “vrais”


rownames avec tibble::column_to_rownames() :

column_to_rownames([Link](d))

mpg cyl disp hp drat wt qsec vs am gear carb


Mazda RX4 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4 4
Mazda RX4 Wag 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4 4
Datsun 710 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4 1
Hornet 4 Drive 21.4 6 258.0 110 3.08 3.215 19.44 1 0 3 1
Hornet Sportabout 18.7 8 360.0 175 3.15 3.440 17.02 0 0 3 2
Valiant 18.1 6 225.0 105 2.76 3.460 20.22 1 0 3 1
Duster 360 14.3 8 360.0 245 3.21 3.570 15.84 0 0 3 4
Merc 240D 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4 2
Merc 230 22.8 4 140.8 95 3.92 3.150 22.90 1 0 4 2
Merc 280 19.2 6 167.6 123 3.92 3.440 18.30 1 0 4 4
Merc 280C 17.8 6 167.6 123 3.92 3.440 18.90 1 0 4 4
Merc 450SE 16.4 8 275.8 180 3.07 4.070 17.40 0 0 3 3
Merc 450SL 17.3 8 275.8 180 3.07 3.730 17.60 0 0 3 3
Merc 450SLC 15.2 8 275.8 180 3.07 3.780 18.00 0 0 3 3
Cadillac Fleetwood 10.4 8 472.0 205 2.93 5.250 17.98 0 0 3 4
Lincoln Continental 10.4 8 460.0 215 3.00 5.424 17.82 0 0 3 4
Chrysler Imperial 14.7 8 440.0 230 3.23 5.345 17.42 0 0 3 4
Fiat 128 32.4 4 78.7 66 4.08 2.200 19.47 1 1 4 1
Honda Civic 30.4 4 75.7 52 4.93 1.615 18.52 1 1 4 2
Toyota Corolla 33.9 4 71.1 65 4.22 1.835 19.90 1 1 4 1
Toyota Corona 21.5 4 120.1 97 3.70 2.465 20.01 1 0 3 1
Dodge Challenger 15.5 8 318.0 150 2.76 3.520 16.87 0 0 3 2
AMC Javelin 15.2 8 304.0 150 3.15 3.435 17.30 0 0 3 2
Camaro Z28 13.3 8 350.0 245 3.73 3.840 15.41 0 0 3 4
Pontiac Firebird 19.2 8 400.0 175 3.08 3.845 17.05 0 0 3 2

70
Fiat X1-9 27.3 4 79.0 66 4.08 1.935 18.90 1 1 4 1
Porsche 914-2 26.0 4 120.3 91 4.43 2.140 16.70 0 1 5 2
Lotus Europa 30.4 4 95.1 113 3.77 1.513 16.90 1 1 5 2
Ford Pantera L 15.8 8 351.0 264 4.22 3.170 14.50 0 1 5 4
Ferrari Dino 19.7 6 145.0 175 3.62 2.770 15.50 0 1 5 6
Maserati Bora 15.0 8 301.0 335 3.54 3.570 14.60 0 1 5 8
Volvo 142E 21.4 4 121.0 109 4.11 2.780 18.60 1 1 4 2

Ĺ Note

Les deux fonctions tibble::column_to_rownames() et


tibble::rownames_to_column() acceptent un argument
supplémentaire var qui permet d’indiquer un nom de co-
lonne autre que le nom rowname utilisé par défaut pour
créer ou identifier la colonne contenant les noms de lignes.

5.3 Données et tableaux imbriqués

Une des particularités des tibbles est qu’ils acceptent, à la diffé-


rence des data frames, des colonnes composées de listes et, par
extension, d’autres tibbles (qui sont des listes) !

d <- tibble(
g = c(1, 2, 3),
data = list(
tibble(x = 1, y = 2),
tibble(x = 4:5, y = 6:7),
tibble(x = 10)
)
)
d

# A tibble: 3 x 2
g data
<dbl> <list>
1 1 <tibble [1 x 2]>
2 2 <tibble [2 x 2]>
3 3 <tibble [1 x 1]>

71
d$data[[2]]

# A tibble: 2 x 2
x y
<int> <int>
1 4 6
2 5 7

Cette fonctionnalité, combinée avec les fonctions de {tidyr} et


de {purrr}, s’avère très puissante pour réaliser des opérations
multiples en peu de ligne de code.
Dans l’exemple ci-dessous, nous réalisons des régressions
linéaires par sous-groupe et les présentons dans un même
tableau. Pour le moment, le code présenté doit vous sembler
complexe et un peu obscur. Pas de panique : tout cela sera
clarifié dans les différents chapitres de ce guide. Ce qu’il y a à
retenir pour le moment, c’est la possibilité de stocker, dans les
colonnes d’un tibble, différent types de données, y compris des
sous-tableaux, des résultats de modèles et même des tableaux
mis en forme.

reg <-
iris |>
group_by(Species) |>
nest() |>
mutate(
model = map(
data,
~ lm([Link] ~ [Link] + [Link], data = .)
),
tbl = map(model, gtsummary::tbl_regression)
)
reg

# A tibble: 3 x 4
# Groups: Species [3]
Species data model tbl
<fct> <list> <list> <list>

72
1 setosa <tibble [50 x 4]> <lm> <tbl_rgrs>
2 versicolor <tibble [50 x 4]> <lm> <tbl_rgrs>
3 virginica <tibble [50 x 4]> <lm> <tbl_rgrs>

gtsummary::tbl_merge(
reg$tbl,
tab_spanner = paste0("**", reg$Species, "**")
)

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

95% p- 95% p- 95% p-


Characteristic
Beta CI value Beta CI value Beta CI value
[Link]
0.40 - 0.2 0.93 0.59, <0.0011.0 0.81, <0.001
0.20, 1.3 1.2
0.99
[Link]
0.71 - 0.2 - - 0.4 0.01 - >0.9
0.27, 0.32 1.1, 0.35,
1.7 0.49 0.37

73
6 Attributs

Les objets R peuvent avoir des attributs qui correspondent en


quelque sorte à des métadonnées associées à l’objet en question.
Techniquement, un attribut peut être tout type d’objet R (un
vecteur, une liste, une fonction…).
Parmi les attributs les plus courants, on retrouve notam-
ment :

• class : la classe de l’objet


• lenghth : sa longueur
• names : les noms donnés aux éléments de l’objet
• levels : pour les facteurs, les étiquettes des différents
niveaux
• label : une étiquette de variable

La fonction attributes() permet de lister tous les attributs


associés à un objet.

attributes(iris)

$names
[1] "[Link]" "[Link]" "[Link]" "[Link]" "Species"

$class
[1] "[Link]"

$[Link]
[1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
[19] 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
[37] 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
[55] 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
[73] 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
[91] 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108

74
[109] 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
[127] 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
[145] 145 146 147 148 149 150

Pour accéder à un attribut spécifique, on aura recours à attr()


en spécifiant à la fois l’objet considéré et le nom de l’attribut
souhaité.

iris |> attr("names")

[1] "[Link]" "[Link]" "[Link]" "[Link]" "Species"

Pour les attributs les plus courants de R, il faut noter qu’il


existe le plus souvent des fonctions spécifiques, comme class(),
names() ou [Link]().

class(iris)

[1] "[Link]"

names(iris)

[1] "[Link]" "[Link]" "[Link]" "[Link]" "Species"

La fonction attr(), associée à l’opérateur d’assignation (<-)


permet également de définir ses propres attributs.

attr(iris, "perso") <- "Des notes personnelles"


attributes(iris)

$names
[1] "[Link]" "[Link]" "[Link]" "[Link]" "Species"

$class
[1] "[Link]"

75
$[Link]
[1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
[19] 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
[37] 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
[55] 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
[73] 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
[91] 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
[109] 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
[127] 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
[145] 145 146 147 148 149 150

$perso
[1] "Des notes personnelles"

attr(iris, "perso")

[1] "Des notes personnelles"

76
partie II

Manipulation de données

77
7 Le pipe

Il est fréquent d’enchaîner des opérations en appelant successi-


vement des fonctions sur le résultat de l’appel précédent.
Prenons un exemple. Supposons que nous ayons un vecteur nu-
mérique v dont nous voulons calculer la moyenne puis l’afficher
via un message dans la console. Pour un meilleur rendu, nous
allons arrondir la moyenne à une décimale, mettre en forme le
résultat à la française, c’est-à-dire avec la virgule comme sé-
parateur des décimales, créer une phrase avec le résultat, puis
l’afficher dans la console. Voici le code correspondant, étape par
étape.

v <- c(1.2, 8.7, 5.6, 11.4)


m <- mean(v)
r <- round(m, digits = 1)
f <- format(r, [Link] = ",")
p <- paste0("La moyenne est de ", f, ".")
message(p)

La moyenne est de 6,7.

Cette écriture, n’est pas vraiment optimale, car cela entraîne la


création d’un grand nombre de variables intermédiaires totale-
ment inutiles. Nous pourrions dès lors imbriquer les différentes
fonctions les unes dans les autres :

message(paste0("La moyenne est de ", format(round(mean(v), digits = 1), [Link]

La moyenne est de 6,7.

78
Nous obtenons bien le même résultat, mais la lecture de cette
ligne de code est assez difficile et il n’est pas aisé de bien iden-
tifier à quelle fonction est rattaché chaque argument.
Une amélioration possible serait d’effectuer des retours à la
ligne avec une indentation adéquate pour rendre cela plus li-
sible.

message(
paste0(
"La moyenne est de ",
format(
round(
mean(v),
digits = 1),
[Link] = ","
),
"."
)
)

La moyenne est de 6,7.

C’est déjà mieux, mais toujours pas optimal.

7.1 Le pipe natif de R : |>

Depuis la version 4.1, R a introduit ce que l’on nomme un pipe


(tuyau en anglais), un nouvel opérateur noté |>.
Le principe de cet opérateur est de passer l’élément situé à
sa gauche comme premier argument de la fonction située à
sa droite. Ainsi, l’écriture x |> f() est équivalente à f(x) et
l’écriture x |> f(y) à f(x, y).
Parfois, on souhaite passer l’objet x à un autre endroit de la
fonction f() que le premier argument. Depuis la version 4.2,
R a introduit l’opérateur _,que l’on nomme un placeholder,
pour indiquer où passer l’objet de gauche. Ainsi, x |> f(y, a
= _) devient équivalent à f(y, a = x). ATTENTION : le

79
placeholder doit impérativement être transmis à un argument
nommé !
Tout cela semble encore un peu abstrait ? Reprenons notre
exemple précédent et réécrivons le code avec le pipe.

v |>
mean() |>
round(digits = 1) |>
format([Link] = ",") |>
paste0("La moyenne est de ", m = _, ".") |>
message()

La moyenne est de 6,7.

Le code n’est-il pas plus lisible ?


Pour visualiser chaque étape du code, vous pouvez consulter
le diaporama suivant : [Link]
R/manipulation/ressources/[Link]

7.2 Le pipe du tidyverse : %>%

Ce n’est qu’à partir de la version 4.1 sortie en 2021 que R a


proposé de manière native un pipe, en l’occurence l’opérateur
|>.
En cela, R s’est notamment inspiré d’un opérateur similaire
introduit dès 2014 dans le tidyverse. Le pipe du tidyverse
fonctionne de manière similaire. Il est implémenté dans
le package {magrittr} qui doit donc être chargé en mé-
moire. Le pipe est également disponible lorsque l’on effectue
library(tidyverse).
Cet opérateur s’écrit %>% et il dispose lui aussi d’un placeholder
qui est le .. La syntaxe du placeholder est un peu plus souple
puisqu’il peut être passé à tout type d’argument, y compris un
argument sans nom. Si l’on reprend notre exemple précédent.

80
library(magrittr)
v %>%
mean() %>%
round(digits = 1) %>%
format([Link] = ",") %>%
paste0("La moyenne est de ", ., ".") %>%
message()

La moyenne est de 6,7.

7.3 Vaut-il mieux utiliser |> ou %>% ?

Bonne question. Si vous utilisez une version récente de R (�


4.2), il est préférable d’avoir recours au pipe natif de R dans
la mesure où il est plus efficient en termes de temps de calcul
car il fait partie intégrante du langage. Dans ce guide, nous
privilégions d’ailleurs l’utilisation de |>.
Si votre code nécessite de fonctionner avec différentes versions
de R, par exemple dans le cadre d’un package, il est alors pré-
férable, pour le moment, d’utiliser celui fourni par {magrittr}
(%>%).

81
7.4 Accéder à un élément avec
purrr::pluck() et purrr::chuck()

Il est fréquent d’avoir besoin d’accéder à un élément précis


d’une liste, d’un tableau ou d’un vecteur, ce que l’on fait
d’ordinaire avec la syntaxe [[]] ou $ pour les listes ou [] pour
les vecteurs. Cependant, cette syntaxe se combine souvent mal
avec un enchaînement d’opérations utilisant le pipe.
Le package {purrr}, chargé par défaut avec library(tidyverse),
fournit une fonction purrr::pluck() qui, est l’équivalent de
[[]], et qui permet de récupérer un élément par son nom
ou sa position. Ainsi, si l’on considère le tableau de don-
nées iris, pluck(iris, "[Link]") est équivalent à
iris$[Link]. Voyons un exemple d’utilisation dans le
cadre d’un enchaînement d’opérations.

iris |>
purrr::pluck("[Link]") |>
mean()

[1] 1.199333

Cette écriture est équivalente à :

mean(iris$[Link])

[1] 1.199333

purrr::pluck() fonctionne également sur des vecteurs (et


dans ce cas opère comme []).

v <- c("a", "b", "c", "d")


v |> purrr::pluck(2)

[1] "b"

82
v[2]

[1] "b"

On peut également, dans un même appel à purrr::pluck(),


enchaîner plusieurs niveaux. Les trois syntaxes ci-après sont
ainsi équivalents :

iris |>
purrr::pluck("[Link]", 3)

[1] 3.2

iris |>
purrr::pluck("[Link]") |>
purrr::pluck(3)

[1] 3.2

iris[["[Link]"]][3]

[1] 3.2

Si l’on demande un élément qui n’existe pas, purrr:pluck()


renverra l’élement vide (NULL). Si l’on souhaite plutôt que cela
génère une erreur, on aura alors recours à purrr::chuck().

iris |> purrr::pluck("inconnu")

NULL

iris |> purrr::chuck("inconnu")

Error in `purrr::chuck()`:
! Can't find name `inconnu` in vector.

83
v |> purrr::pluck(10)

NULL

v |> purrr::chuck(10)

Error in `purrr::chuck()`:
! Index 1 exceeds the length of plucked object (10 > 4).

84
8 dplyr

{dplyr} est l’un des packages les plus connus du tidyverse. Il


facilite le traitement et la manipulation des tableaux de données
(qu’il s’agisse de data frame ou de tibble). Il propose une syntaxe
claire et cohérente, sous formes de verbes correspondant à des
fonctions.
{dplyr} part du principe que les données sont tidy (chaque va-
riable est une colonne, chaque observation est une ligne, voir
Chapitre 5). Les verbes de {dplyr} prennent en entrée un ta-
bleau de données10 (data frame ou tibble) et renvoient systéma- 10
Le package {dbplyr} permets
tiquement un tibble. d’étendre les verbes de {dplyr} à
des tables de bases de données SQL,
{dtplyr} à des tableaux de données
library(dplyr) du type {[Link]} et {srvyr}
à des données pondérées du type
Dans ce qui suit on va utiliser le jeu de données {nycflights13}, {survey}.
contenu dans l’extension du même nom (qu’il faut donc avoir
installée). Celui-ci correspond aux données de tous les vols au
départ d’un des trois aéroports de New-York en 2013. Il a la
particularité d’être réparti en trois tables :

• nycflights13::flights contient des informations sur


les vols : date, départ, destination, horaires, retard…
• nycflights13::airports contient des informations sur
les aéroports
• nycflights13::airlines contient des données sur les
compagnies aériennes

On va charger les trois tables du jeu de données :

library(nycflights13)
## Chargement des trois tables du jeu de données
data(flights)
data(airports)

85
data(airlines)

Normalement trois objets correspondant aux trois tables ont


dû apparaître dans votre environnement.

8.1 Opérations sur les lignes

8.1.1 filter()

dplyr::filter() sélectionne des lignes d’un tableau de don-


nées selon une condition. On lui passe en paramètre un test, et
seules les lignes pour lesquelles ce test renvoi TRUE (vrai) sont
conservées11 . 11
Si le test renvoie faux (FALSE) ou
une valeur manquante (NA), les lignes
Par exemple, si on veut sélectionner les vols du mois de janvier, correspondantes ne seront donc pas
on peut filtrer sur la variable month de la manière suivante : sélectionnées.

filter(flights, month == 1)

# A tibble: 27,004 x 19
year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
<int> <int> <int> <int> <int> <dbl> <int> <int>
1 2013 1 1 517 515 2 830 819
2 2013 1 1 533 529 4 850 830
3 2013 1 1 542 540 2 923 850
4 2013 1 1 544 545 -1 1004 1022
5 2013 1 1 554 600 -6 812 837
6 2013 1 1 554 558 -4 740 728
7 2013 1 1 555 600 -5 913 854
8 2013 1 1 557 600 -3 709 723
9 2013 1 1 557 600 -3 838 846
10 2013 1 1 558 600 -2 753 745
# i 26,994 more rows
# i 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
# tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
# hour <dbl>, minute <dbl>, time_hour <dttm>

Cela peut s’écrire plus simplement avec un pipe :

86
flights |> filter(month == 1)

# A tibble: 27,004 x 19
year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
<int> <int> <int> <int> <int> <dbl> <int> <int>
1 2013 1 1 517 515 2 830 819
2 2013 1 1 533 529 4 850 830
3 2013 1 1 542 540 2 923 850
4 2013 1 1 544 545 -1 1004 1022
5 2013 1 1 554 600 -6 812 837
6 2013 1 1 554 558 -4 740 728
7 2013 1 1 555 600 -5 913 854
8 2013 1 1 557 600 -3 709 723
9 2013 1 1 557 600 -3 838 846
10 2013 1 1 558 600 -2 753 745
# i 26,994 more rows
# i 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
# tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
# hour <dbl>, minute <dbl>, time_hour <dttm>

Si l’on veut uniquement les vols avec un retard au départ (va-


riable dep_delay) compris entre 10 et 15 minutes :

flights |>
filter(dep_delay >= 10 & dep_delay <= 15)

# A tibble: 14,919 x 19
year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
<int> <int> <int> <int> <int> <dbl> <int> <int>
1 2013 1 1 611 600 11 945 931
2 2013 1 1 623 610 13 920 915
3 2013 1 1 743 730 13 1107 1100
4 2013 1 1 743 730 13 1059 1056
5 2013 1 1 851 840 11 1215 1206
6 2013 1 1 912 900 12 1241 1220
7 2013 1 1 914 900 14 1058 1043
8 2013 1 1 920 905 15 1039 1025
9 2013 1 1 1011 1001 10 1133 1128
10 2013 1 1 1112 1100 12 1440 1438

87
# i 14,909 more rows
# i 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
# tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
# hour <dbl>, minute <dbl>, time_hour <dttm>

Si l’on passe plusieurs arguments à dplyr::filter(), celui-ci


rajoute automatiquement une condition ET. La ligne ci-dessus
peut donc également être écrite de la manière suivante, avec le
même résultat :

flights |>
filter(dep_delay >= 10, dep_delay <= 15)

Enfin, on peut également placer des fonctions dans les tests,


qui nous permettent par exemple de sélectionner les vols avec
la plus grande distance :

flights |>
filter(distance == max(distance))

# A tibble: 342 x 19
year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
<int> <int> <int> <int> <int> <dbl> <int> <int>
1 2013 1 1 857 900 -3 1516 1530
2 2013 1 2 909 900 9 1525 1530
3 2013 1 3 914 900 14 1504 1530
4 2013 1 4 900 900 0 1516 1530
5 2013 1 5 858 900 -2 1519 1530
6 2013 1 6 1019 900 79 1558 1530
7 2013 1 7 1042 900 102 1620 1530
8 2013 1 8 901 900 1 1504 1530
9 2013 1 9 641 900 1301 1242 1530
10 2013 1 10 859 900 -1 1449 1530
# i 332 more rows
# i 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
# tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
# hour <dbl>, minute <dbl>, time_hour <dttm>

88
Ď Évaluation contextuelle

Il est important de noter que {dplyr} procède à une éva-


luation contextuelle des expressions qui lui sont passées.
Ainsi, on peut indiquer directement le nom d’une variable
et {dplyr} l’interprétera dans le contexte du tableau de
données, c’est-à-dire regardera s’il existe une colonne por-
tant ce nom dans le tableau.
Dans l’expression flights |> filter(month == 1),
month est interprété comme la colonne month du tableau
flights, à savoir flights$month.
Il est également possible d’indiquer des objets extérieurs
au tableau :

m <- 2
flights |>
filter(month == m)

# A tibble: 24,951 x 19
year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
<int> <int> <int> <int> <int> <dbl> <int> <int>
1 2013 2 1 456 500 -4 652 648
2 2013 2 1 520 525 -5 816 820
3 2013 2 1 527 530 -3 837 829
4 2013 2 1 532 540 -8 1007 1017
5 2013 2 1 540 540 0 859 850
6 2013 2 1 552 600 -8 714 715
7 2013 2 1 552 600 -8 919 910
8 2013 2 1 552 600 -8 655 709
9 2013 2 1 553 600 -7 833 815
10 2013 2 1 553 600 -7 821 825
# i 24,941 more rows
# i 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
# tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
# hour <dbl>, minute <dbl>, time_hour <dttm>

Cela fonctionne car il n’y a pas de colonne m dans


flights. Dès lors, {dplyr} regarde s’il existe un objet
m dans l’environnement de travail.
Par contre, si une colonne existe dans le tableau, elle aura

89
priorité sur les objets du même nom dans l’environnement.
Dans l’exemple ci-dessous, le résultat obtenu n’est pas ce-
lui voulu. Il est interprété comme sélectionner toutes les
lignes où la colonne mois est égale à elle-même et donc
cela sélectionne toutes les lignes du tableau.

month <- 3
flights |>
filter(month == month)

# A tibble: 336,776 x 19
year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
<int> <int> <int> <int> <int> <dbl> <int> <int>
1 2013 1 1 517 515 2 830 819
2 2013 1 1 533 529 4 850 830
3 2013 1 1 542 540 2 923 850
4 2013 1 1 544 545 -1 1004 1022
5 2013 1 1 554 600 -6 812 837
6 2013 1 1 554 558 -4 740 728
7 2013 1 1 555 600 -5 913 854
8 2013 1 1 557 600 -3 709 723
9 2013 1 1 557 600 -3 838 846
10 2013 1 1 558 600 -2 753 745
# i 336,766 more rows
# i 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
# tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
# hour <dbl>, minute <dbl>, time_hour <dttm>

Afin de distinguer ce qui correspond à une colonne du


tableau et à un objet de l’environnement, on pourra avoir
recours à .data et .env (voir help(".env", package =
"rlang")).

month <- 3
flights |>
filter(.data$month == .env$month)

# A tibble: 28,834 x 19
year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
<int> <int> <int> <int> <int> <dbl> <int> <int>

90
1 2013 3 1 4 2159 125 318 56
2 2013 3 1 50 2358 52 526 438
3 2013 3 1 117 2245 152 223 2354
4 2013 3 1 454 500 -6 633 648
5 2013 3 1 505 515 -10 746 810
6 2013 3 1 521 530 -9 813 827
7 2013 3 1 537 540 -3 856 850
8 2013 3 1 541 545 -4 1014 1023
9 2013 3 1 549 600 -11 639 703
10 2013 3 1 550 600 -10 747 801
# i 28,824 more rows
# i 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
# tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
# hour <dbl>, minute <dbl>, time_hour <dttm>

8.1.2 slice()

Le verbe dplyr::slice() sélectionne des lignes du tableau


selon leur position. On lui passe un chiffre ou un vecteur de
chiffres.
Si l’on souhaite sélectionner la 345e ligne du tableau
airports :

airports |>
slice(345)

# A tibble: 1 x 8
faa name lat lon alt tz dst tzone
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
1 CYF Chefornak Airport 60.1 -164. 40 -9 A America/Anchorage

Si l’on veut sélectionner les 5 premières lignes :

airports |>
slice(1:5)

# A tibble: 5 x 8

91
faa name lat lon alt tz dst tzone
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
1 04G Lansdowne Airport 41.1 -80.6 1044 -5 A America/New~
2 06A Moton Field Municipal Airport 32.5 -85.7 264 -6 A America/Chi~
3 06C Schaumburg Regional 42.0 -88.1 801 -6 A America/Chi~
4 06N Randall Airport 41.4 -74.4 523 -5 A America/New~
5 09J Jekyll Island Airport 31.1 -81.4 11 -5 A America/New~

8.1.3 arrange()

dplyr::arrange() réordonne les lignes d’un tableau selon une


ou plusieurs colonnes.
Ainsi, si l’on veut trier le tableau flights selon le retard au
départ, dans l’ordre croissant :

flights |>
arrange(dep_delay)

# A tibble: 336,776 x 19
year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
<int> <int> <int> <int> <int> <dbl> <int> <int>
1 2013 12 7 2040 2123 -43 40 2352
2 2013 2 3 2022 2055 -33 2240 2338
3 2013 11 10 1408 1440 -32 1549 1559
4 2013 1 11 1900 1930 -30 2233 2243
5 2013 1 29 1703 1730 -27 1947 1957
6 2013 8 9 729 755 -26 1002 955
7 2013 10 23 1907 1932 -25 2143 2143
8 2013 3 30 2030 2055 -25 2213 2250
9 2013 3 2 1431 1455 -24 1601 1631
10 2013 5 5 934 958 -24 1225 1309
# i 336,766 more rows
# i 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
# tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
# hour <dbl>, minute <dbl>, time_hour <dttm>

On peut trier selon plusieurs colonnes. Par exemple selon le


mois, puis selon le retard au départ :

92
flights |>
arrange(month, dep_delay)

# A tibble: 336,776 x 19
year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
<int> <int> <int> <int> <int> <dbl> <int> <int>
1 2013 1 11 1900 1930 -30 2233 2243
2 2013 1 29 1703 1730 -27 1947 1957
3 2013 1 12 1354 1416 -22 1606 1650
4 2013 1 21 2137 2159 -22 2232 2316
5 2013 1 20 704 725 -21 1025 1035
6 2013 1 12 2050 2110 -20 2310 2355
7 2013 1 12 2134 2154 -20 4 50
8 2013 1 14 2050 2110 -20 2329 2355
9 2013 1 4 2140 2159 -19 2241 2316
10 2013 1 11 1947 2005 -18 2209 2230
# i 336,766 more rows
# i 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
# tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
# hour <dbl>, minute <dbl>, time_hour <dttm>

Si l’on veut trier selon une colonne par ordre décroissant, on


lui applique la fonction dplyr::desc() :

flights |>
arrange(desc(dep_delay))

# A tibble: 336,776 x 19
year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
<int> <int> <int> <int> <int> <dbl> <int> <int>
1 2013 1 9 641 900 1301 1242 1530
2 2013 6 15 1432 1935 1137 1607 2120
3 2013 1 10 1121 1635 1126 1239 1810
4 2013 9 20 1139 1845 1014 1457 2210
5 2013 7 22 845 1600 1005 1044 1815
6 2013 4 10 1100 1900 960 1342 2211
7 2013 3 17 2321 810 911 135 1020
8 2013 6 27 959 1900 899 1236 2226
9 2013 7 22 2257 759 898 121 1026

93
10 2013 12 5 756 1700 896 1058 2020
# i 336,766 more rows
# i 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
# tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
# hour <dbl>, minute <dbl>, time_hour <dttm>

Combiné avec dplyr::slice(), dplyr::arrange() permet


par exemple de sélectionner les trois vols ayant eu le plus de
retard :

flights |>
arrange(desc(dep_delay)) |>
slice(1:3)

# A tibble: 3 x 19
year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
<int> <int> <int> <int> <int> <dbl> <int> <int>
1 2013 1 9 641 900 1301 1242 1530
2 2013 6 15 1432 1935 1137 1607 2120
3 2013 1 10 1121 1635 1126 1239 1810
# i 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
# tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
# hour <dbl>, minute <dbl>, time_hour <dttm>

8.1.4 slice_sample()

dplyr::slice_sample() permet de sélectionner aléatoirement


un nombre de lignes ou une fraction des lignes d’un tableau.
Ainsi si l’on veut choisir 5 lignes au hasard dans le tableau
airports :

airports |>
slice_sample(n = 5)

# A tibble: 5 x 8
faa name lat lon alt tz dst tzone
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
1 EGV Eagle River 45.9 -89.3 1642 -6 A America/Chica~

94
2 RID Richmond Municipal Airport 39.8 -84.8 1140 -5 U America/New_Y~
3 EGA Eagle County Airport 39.6 -107. 6548 -7 U America/Denver
4 YKN Chan Gurney 42.9 -97.4 1200 -6 A America/Chica~
5 FWA Fort Wayne 41.0 -85.2 815 -5 A America/New_Y~

Si l’on veut tirer au hasard 10% des lignes de flights :

flights |>
slice_sample(prop = .1)

# A tibble: 33,677 x 19
year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
<int> <int> <int> <int> <int> <dbl> <int> <int>
1 2013 1 13 1828 1705 83 2037 1905
2 2013 12 9 1013 1000 13 1320 1242
3 2013 6 8 1738 1655 43 2023 2005
4 2013 12 24 1518 1520 -2 1648 1700
5 2013 11 21 625 635 -10 808 812
6 2013 4 24 1858 1844 14 2205 2114
7 2013 7 6 654 659 -5 852 909
8 2013 9 15 1922 1925 -3 2212 2248
9 2013 6 17 935 930 5 1036 1042
10 2013 5 28 1356 1359 -3 1542 1554
# i 33,667 more rows
# i 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
# tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
# hour <dbl>, minute <dbl>, time_hour <dttm>

Ces fonctions sont utiles notamment pour faire de l’“échantillonnage”


en tirant au hasard un certain nombre d’observations du ta-
bleau.

8.1.5 distinct()

dplyr::distinct() filtre les lignes du tableau pour ne conser-


ver que les lignes distinctes, en supprimant toutes les lignes en
double.

95
flights |>
select(day, month) |>
distinct()

# A tibble: 365 x 2
day month
<int> <int>
1 1 1
2 2 1
3 3 1
4 4 1
5 5 1
6 6 1
7 7 1
8 8 1
9 9 1
10 10 1
# i 355 more rows

On peut lui spécifier une liste de variables : dans ce cas, pour


toutes les observations ayant des valeurs identiques pour les
variables en question, dplyr::distinct() ne conservera que
la première d’entre elles.

flights |>
distinct(month, day)

# A tibble: 365 x 2
month day
<int> <int>
1 1 1
2 1 2
3 1 3
4 1 4
5 1 5
6 1 6
7 1 7
8 1 8
9 1 9

96
10 1 10
# i 355 more rows

L’option .keep_all permet, dans l’opération précédente, de


conserver l’ensemble des colonnes du tableau :

flights |>
distinct(month, day, .keep_all = TRUE)

# A tibble: 365 x 19
year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
<int> <int> <int> <int> <int> <dbl> <int> <int>
1 2013 1 1 517 515 2 830 819
2 2013 1 2 42 2359 43 518 442
3 2013 1 3 32 2359 33 504 442
4 2013 1 4 25 2359 26 505 442
5 2013 1 5 14 2359 15 503 445
6 2013 1 6 16 2359 17 451 442
7 2013 1 7 49 2359 50 531 444
8 2013 1 8 454 500 -6 625 648
9 2013 1 9 2 2359 3 432 444
10 2013 1 10 3 2359 4 426 437
# i 355 more rows
# i 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
# tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
# hour <dbl>, minute <dbl>, time_hour <dttm>

8.2 Opérations sur les colonnes

8.2.1 select()

dplyr::select() permet de sélectionner des colonnes d’un ta-


bleau de données. Ainsi, si l’on veut extraire les colonnes lat
et lon du tableau airports :

airports |>
select(lat, lon)

97
# A tibble: 1,458 x 2
lat lon
<dbl> <dbl>
1 41.1 -80.6
2 32.5 -85.7
3 42.0 -88.1
4 41.4 -74.4
5 31.1 -81.4
6 36.4 -82.2
7 41.5 -84.5
8 42.9 -76.8
9 39.8 -76.6
10 48.1 -123.
# i 1,448 more rows

Si on fait précéder le nom d’un -, la colonne est éliminée plutôt


que sélectionnée :

airports |>
select(-lat, -lon)

# A tibble: 1,458 x 6
faa name alt tz dst tzone
<chr> <chr> <dbl> <dbl> <chr> <chr>
1 04G Lansdowne Airport 1044 -5 A America/New_York
2 06A Moton Field Municipal Airport 264 -6 A America/Chicago
3 06C Schaumburg Regional 801 -6 A America/Chicago
4 06N Randall Airport 523 -5 A America/New_York
5 09J Jekyll Island Airport 11 -5 A America/New_York
6 0A9 Elizabethton Municipal Airport 1593 -5 A America/New_York
7 0G6 Williams County Airport 730 -5 A America/New_York
8 0G7 Finger Lakes Regional Airport 492 -5 A America/New_York
9 0P2 Shoestring Aviation Airfield 1000 -5 U America/New_York
10 0S9 Jefferson County Intl 108 -8 A America/Los_Angeles
# i 1,448 more rows

dplyr::select() comprend toute une série de fonc-


tions facilitant la sélection de multiples colonnes. Par
exemple, dplyr::starts_with(), dplyr::ends_width(),

98
dplyr::contains() ou dplyr::matches() permettent
d’exprimer des conditions sur les noms de variables :

flights |>
select(starts_with("dep_"))

# A tibble: 336,776 x 2
dep_time dep_delay
<int> <dbl>
1 517 2
2 533 4
3 542 2
4 544 -1
5 554 -6
6 554 -4
7 555 -5
8 557 -3
9 557 -3
10 558 -2
# i 336,766 more rows

La syntaxe colonne1:colonne2 permet de sélectionner toutes


les colonnes situées entre colonne1 et colonne2 incluses12 : 12
À noter que cette opération est
un peu plus “fragile” que les autres,
car si l’ordre des colonnes change elle
flights |>
peut renvoyer un résultat différent.
select(year:day)

# A tibble: 336,776 x 3
year month day
<int> <int> <int>
1 2013 1 1
2 2013 1 1
3 2013 1 1
4 2013 1 1
5 2013 1 1
6 2013 1 1
7 2013 1 1
8 2013 1 1
9 2013 1 1

99
10 2013 1 1
# i 336,766 more rows

dplyr::all_of() et dplyr::any_of() permettent de fournir


une liste de variables à extraire sous forme de vecteur textuel.
Alors que dplyr::all_of() renverra une erreur si une variable
n’est pas trouvée dans le tableau de départ, dplyr::any_of()
sera moins stricte.

flights |>
select(all_of(c("year", "month", "day")))

# A tibble: 336,776 x 3
year month day
<int> <int> <int>
1 2013 1 1
2 2013 1 1
3 2013 1 1
4 2013 1 1
5 2013 1 1
6 2013 1 1
7 2013 1 1
8 2013 1 1
9 2013 1 1
10 2013 1 1
# i 336,766 more rows

flights |>
select(all_of(c("century", "year", "month", "day")))

Error in `all_of()`:
! Can't subset columns that don't exist.
x Column `century` doesn't exist.

Erreur : Can't subset columns that don't exist.


x Column `century` doesn't exist.

100
flights |>
select(any_of(c("century", "year", "month", "day")))

# A tibble: 336,776 x 3
year month day
<int> <int> <int>
1 2013 1 1
2 2013 1 1
3 2013 1 1
4 2013 1 1
5 2013 1 1
6 2013 1 1
7 2013 1 1
8 2013 1 1
9 2013 1 1
10 2013 1 1
# i 336,766 more rows

dplyr::where() permets de sélectionner des variables à par-


tir d’une fonction qui renvoie une valeur logique. Par exemple,
pour sélectionner seulement les variables textuelles :

flights |>
select(where([Link]))

# A tibble: 336,776 x 4
carrier tailnum origin dest
<chr> <chr> <chr> <chr>
1 UA N14228 EWR IAH
2 UA N24211 LGA IAH
3 AA N619AA JFK MIA
4 B6 N804JB JFK BQN
5 DL N668DN LGA ATL
6 UA N39463 EWR ORD
7 B6 N516JB EWR FLL
8 EV N829AS LGA IAD
9 B6 N593JB JFK MCO
10 AA N3ALAA LGA ORD
# i 336,766 more rows

101
dplyr::select() peut être utilisée pour réordonner les co-
lonnes d’une table en utilisant la fonction dplyr::everything(),
qui sélectionne l’ensemble des colonnes non encore sélection-
nées. Ainsi, si l’on souhaite faire passer la colonne name en
première position de la table airports, on peut faire :

airports |>
select(name, everything())

# A tibble: 1,458 x 8
name faa lat lon alt tz dst tzone
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
1 Lansdowne Airport 04G 41.1 -80.6 1044 -5 A America/~
2 Moton Field Municipal Airport 06A 32.5 -85.7 264 -6 A America/~
3 Schaumburg Regional 06C 42.0 -88.1 801 -6 A America/~
4 Randall Airport 06N 41.4 -74.4 523 -5 A America/~
5 Jekyll Island Airport 09J 31.1 -81.4 11 -5 A America/~
6 Elizabethton Municipal Airport 0A9 36.4 -82.2 1593 -5 A America/~
7 Williams County Airport 0G6 41.5 -84.5 730 -5 A America/~
8 Finger Lakes Regional Airport 0G7 42.9 -76.8 492 -5 A America/~
9 Shoestring Aviation Airfield 0P2 39.8 -76.6 1000 -5 U America/~
10 Jefferson County Intl 0S9 48.1 -123. 108 -8 A America/~
# i 1,448 more rows

8.2.2 relocate()

Pour réordonner des colonnes, on pourra aussi avoir recours


à dplyr::relocate() en indiquant les premières variables.
Il n’est pas nécessaire d’ajouter everything() car avec
dplyr::relocate() toutes les variables sont conservées.

airports |>
relocate(lon, lat, name)

# A tibble: 1,458 x 8
lon lat name faa alt tz dst tzone
<dbl> <dbl> <chr> <chr> <dbl> <dbl> <chr> <chr>
1 -80.6 41.1 Lansdowne Airport 04G 1044 -5 A America/~
2 -85.7 32.5 Moton Field Municipal Airport 06A 264 -6 A America/~

102
3 -88.1 42.0 Schaumburg Regional 06C 801 -6 A America/~
4 -74.4 41.4 Randall Airport 06N 523 -5 A America/~
5 -81.4 31.1 Jekyll Island Airport 09J 11 -5 A America/~
6 -82.2 36.4 Elizabethton Municipal Airport 0A9 1593 -5 A America/~
7 -84.5 41.5 Williams County Airport 0G6 730 -5 A America/~
8 -76.8 42.9 Finger Lakes Regional Airport 0G7 492 -5 A America/~
9 -76.6 39.8 Shoestring Aviation Airfield 0P2 1000 -5 U America/~
10 -123. 48.1 Jefferson County Intl 0S9 108 -8 A America/~
# i 1,448 more rows

8.2.3 rename()

Une variante de dplyr::select() est dplyr::rename()13 , 13


Il est également possible de renom-
qui permet de renommer facilement des colonnes. On l’utilise mer des colonnes directement avec
select(), avec la même syntaxe que
en lui passant des paramètres de la forme nouveau_nom =
pour rename().
ancien_nom. Ainsi, si on veut renommer les colonnes lon et lat
de airports en longitude et latitude :

airports |>
rename(longitude = lon, latitude = lat)

# A tibble: 1,458 x 8
faa name latitude longitude alt tz dst tzone
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
1 04G Lansdowne Airport 41.1 -80.6 1044 -5 A Amer~
2 06A Moton Field Municipal Airpo~ 32.5 -85.7 264 -6 A Amer~
3 06C Schaumburg Regional 42.0 -88.1 801 -6 A Amer~
4 06N Randall Airport 41.4 -74.4 523 -5 A Amer~
5 09J Jekyll Island Airport 31.1 -81.4 11 -5 A Amer~
6 0A9 Elizabethton Municipal Airp~ 36.4 -82.2 1593 -5 A Amer~
7 0G6 Williams County Airport 41.5 -84.5 730 -5 A Amer~
8 0G7 Finger Lakes Regional Airpo~ 42.9 -76.8 492 -5 A Amer~
9 0P2 Shoestring Aviation Airfield 39.8 -76.6 1000 -5 U Amer~
10 0S9 Jefferson County Intl 48.1 -123. 108 -8 A Amer~
# i 1,448 more rows

Si les noms de colonnes comportent des espaces ou des carac-


tères spéciaux, on peut les entourer de guillemets (") ou de
quotes inverses (`) :

103
flights |>
rename(
"retard départ" = dep_delay,
"retard arrivée" = arr_delay
) |>
select(`retard départ`, `retard arrivée`)

# A tibble: 336,776 x 2
`retard départ` `retard arrivée`
<dbl> <dbl>
1 2 11
2 4 20
3 2 33
4 -1 -18
5 -6 -25
6 -4 12
7 -5 19
8 -3 -14
9 -3 -8
10 -2 8
# i 336,766 more rows

8.2.4 rename_with()

La fonction dplyr::rename_with() permets de renommer plu-


sieurs colonnes d’un coup en transmettant une fonction, par
exemple toupper() qui passe tous les caractères en majus-
cule.

airports |>
rename_with(toupper)

# A tibble: 1,458 x 8
FAA NAME LAT LON ALT TZ DST TZONE
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
1 04G Lansdowne Airport 41.1 -80.6 1044 -5 A America/~
2 06A Moton Field Municipal Airport 32.5 -85.7 264 -6 A America/~
3 06C Schaumburg Regional 42.0 -88.1 801 -6 A America/~

104
4 06N Randall Airport 41.4 -74.4 523 -5 A America/~
5 09J Jekyll Island Airport 31.1 -81.4 11 -5 A America/~
6 0A9 Elizabethton Municipal Airport 36.4 -82.2 1593 -5 A America/~
7 0G6 Williams County Airport 41.5 -84.5 730 -5 A America/~
8 0G7 Finger Lakes Regional Airport 42.9 -76.8 492 -5 A America/~
9 0P2 Shoestring Aviation Airfield 39.8 -76.6 1000 -5 U America/~
10 0S9 Jefferson County Intl 48.1 -123. 108 -8 A America/~
# i 1,448 more rows

On pourra notamment utiliser les fonctions du package


snakecase et, en particulier, snakecase::to_snake_case()
que je recommande pour nommer de manière consistante les
variables14 . 14
Le snake case est une conven-
tion typographique en informatique
consistant à écrire des ensembles de
8.2.5 pull() mots, généralement, en minuscules en
les séparant par des tirets bas.

La fonction dplyr::pull() permet d’accéder au contenu d’une


variable. C’est un équivalent aux opérateurs $ ou [[]]. On peut
lui passer un nom de variable ou bien sa position.

airports |>
pull(alt) |>
mean()

[1] 1001.416

Ĺ Note

dplyr::pull() ressemble à la fonction purrr::chuck()


que nous avons déjà abordée (cf. Section 7.4). Cependant,
dplyr::pull() ne fonctionne que sur des tableaux de
données tandis que purrr::chuck() est plus générique
et peut s’appliquer à tous types de listes.

8.2.6 mutate()

dplyr::mutate() permet de créer de nouvelles colonnes dans


le tableau de données, en général à partir de variables exis-
tantes.

105
Par exemple, la table airports contient l’altitude de l’aéroport
en pieds. Si l’on veut créer une nouvelle variable alt_m avec
l’altitude en mètres, on peut faire :

airports <-
airports |>
mutate(alt_m = alt / 3.2808)

On peut créer plusieurs nouvelles colonnes en une seule fois, et


les expressions successives peuvent prendre en compte les résul-
tats des calculs précédents. L’exemple suivant convertit d’abord
la distance en kilomètres dans une variable distance_km, puis
utilise cette nouvelle colonne pour calculer la vitesse en km/h.

flights <-
flights |>
mutate(
distance_km = distance / 0.62137,
vitesse = distance_km / air_time * 60
)

8.3 Opérations groupées

8.3.1 group_by()

Un élément très important de {dplyr} est la fonction


dplyr::group_by(). Elle permet de définir des groupes de
lignes à partir des valeurs d’une ou plusieurs colonnes. Par
exemple, on peut grouper les vols selon leur mois :

flights |>
group_by(month)

# A tibble: 336,776 x 21
# Groups: month [12]
year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
<int> <int> <int> <int> <int> <dbl> <int> <int>
1 2013 1 1 517 515 2 830 819

106
2 2013 1 1 533 529 4 850 830
3 2013 1 1 542 540 2 923 850
4 2013 1 1 544 545 -1 1004 1022
5 2013 1 1 554 600 -6 812 837
6 2013 1 1 554 558 -4 740 728
7 2013 1 1 555 600 -5 913 854
8 2013 1 1 557 600 -3 709 723
9 2013 1 1 557 600 -3 838 846
10 2013 1 1 558 600 -2 753 745
# i 336,766 more rows
# i 13 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
# tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
# hour <dbl>, minute <dbl>, time_hour <dttm>, distance_km <dbl>,
# vitesse <dbl>

Par défaut ceci ne fait rien de visible, à part l’apparition d’une


mention Groups dans l’affichage du résultat. Mais à partir
du moment où des groupes ont été définis, les verbes comme
dplyr::slice() ou dplyr::mutate() vont en tenir compte
lors de leurs opérations.
Par exemple, si on applique dplyr::slice() à un tableau préa-
lablement groupé, il va sélectionner les lignes aux positions indi-
quées pour chaque groupe. Ainsi la commande suivante affiche
le premier vol de chaque mois, selon leur ordre d’apparition
dans le tableau :

flights |>
group_by(month) |>
slice(1)

# A tibble: 12 x 21
# Groups: month [12]
year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
<int> <int> <int> <int> <int> <dbl> <int> <int>
1 2013 1 1 517 515 2 830 819
2 2013 2 1 456 500 -4 652 648
3 2013 3 1 4 2159 125 318 56
4 2013 4 1 454 500 -6 636 640
5 2013 5 1 9 1655 434 308 2020

107
6 2013 6 1 2 2359 3 341 350
7 2013 7 1 1 2029 212 236 2359
8 2013 8 1 12 2130 162 257 14
9 2013 9 1 9 2359 10 343 340
10 2013 10 1 447 500 -13 614 648
11 2013 11 1 5 2359 6 352 345
12 2013 12 1 13 2359 14 446 445
# i 13 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
# tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
# hour <dbl>, minute <dbl>, time_hour <dttm>, distance_km <dbl>,
# vitesse <dbl>

Idem pour dplyr::mutate() : les opérations appliquées lors


du calcul des valeurs des nouvelles colonnes sont appliquée
groupe de lignes par groupe de lignes. Dans l’exemple suivant,
on ajoute une nouvelle colonne qui contient le retard moyen du
mois correspondant :

flights |>
group_by(month) |>
mutate(mean_delay_month = mean(dep_delay, [Link] = TRUE))

# A tibble: 336,776 x 22
# Groups: month [12]
year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
<int> <int> <int> <int> <int> <dbl> <int> <int>
1 2013 1 1 517 515 2 830 819
2 2013 1 1 533 529 4 850 830
3 2013 1 1 542 540 2 923 850
4 2013 1 1 544 545 -1 1004 1022
5 2013 1 1 554 600 -6 812 837
6 2013 1 1 554 558 -4 740 728
7 2013 1 1 555 600 -5 913 854
8 2013 1 1 557 600 -3 709 723
9 2013 1 1 557 600 -3 838 846
10 2013 1 1 558 600 -2 753 745
# i 336,766 more rows
# i 14 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
# tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
# hour <dbl>, minute <dbl>, time_hour <dttm>, distance_km <dbl>,

108
# vitesse <dbl>, mean_delay_month <dbl>

Ceci peut permettre, par exemple, de déterminer si un retard


donné est supérieur ou inférieur au retard moyen du mois en
cours.
dplyr::group_by() peut aussi être utile avec dplyr::filter(),
par exemple pour sélectionner les vols avec le retard au départ
le plus important pour chaque mois :

flights |>
group_by(month) |>
filter(dep_delay == max(dep_delay, [Link] = TRUE))

# A tibble: 12 x 21
# Groups: month [12]
year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
<int> <int> <int> <int> <int> <dbl> <int> <int>
1 2013 1 9 641 900 1301 1242 1530
2 2013 10 14 2042 900 702 2255 1127
3 2013 11 3 603 1645 798 829 1913
4 2013 12 5 756 1700 896 1058 2020
5 2013 2 10 2243 830 853 100 1106
6 2013 3 17 2321 810 911 135 1020
7 2013 4 10 1100 1900 960 1342 2211
8 2013 5 3 1133 2055 878 1250 2215
9 2013 6 15 1432 1935 1137 1607 2120
10 2013 7 22 845 1600 1005 1044 1815
11 2013 8 8 2334 1454 520 120 1710
12 2013 9 20 1139 1845 1014 1457 2210
# i 13 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
# tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
# hour <dbl>, minute <dbl>, time_hour <dttm>, distance_km <dbl>,
# vitesse <dbl>

Attention : la clause dplyr::roup_by() marche pour les


verbes déjà vus précédemment, sauf pour dplyr::arrange(),
qui par défaut trie la table sans tenir compte des groupes.
Pour obtenir un tri par groupe, il faut lui ajouter l’argument
.by_group = TRUE.

109
On peut voir la différence en comparant les deux résultats sui-
vants :

flights |>
group_by(month) |>
arrange(desc(dep_delay))

# A tibble: 336,776 x 21
# Groups: month [12]
year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
<int> <int> <int> <int> <int> <dbl> <int> <int>
1 2013 1 9 641 900 1301 1242 1530
2 2013 6 15 1432 1935 1137 1607 2120
3 2013 1 10 1121 1635 1126 1239 1810
4 2013 9 20 1139 1845 1014 1457 2210
5 2013 7 22 845 1600 1005 1044 1815
6 2013 4 10 1100 1900 960 1342 2211
7 2013 3 17 2321 810 911 135 1020
8 2013 6 27 959 1900 899 1236 2226
9 2013 7 22 2257 759 898 121 1026
10 2013 12 5 756 1700 896 1058 2020
# i 336,766 more rows
# i 13 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
# tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
# hour <dbl>, minute <dbl>, time_hour <dttm>, distance_km <dbl>,
# vitesse <dbl>

flights |>
group_by(month) |>
arrange(desc(dep_delay), .by_group = TRUE)

# A tibble: 336,776 x 21
# Groups: month [12]
year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
<int> <int> <int> <int> <int> <dbl> <int> <int>
1 2013 1 9 641 900 1301 1242 1530
2 2013 1 10 1121 1635 1126 1239 1810
3 2013 1 1 848 1835 853 1001 1950
4 2013 1 13 1809 810 599 2054 1042

110
5 2013 1 16 1622 800 502 1911 1054
6 2013 1 23 1551 753 478 1812 1006
7 2013 1 10 1525 900 385 1713 1039
8 2013 1 1 2343 1724 379 314 1938
9 2013 1 2 2131 1512 379 2340 1741
10 2013 1 7 2021 1415 366 2332 1724
# i 336,766 more rows
# i 13 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
# tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
# hour <dbl>, minute <dbl>, time_hour <dttm>, distance_km <dbl>,
# vitesse <dbl>

8.3.2 summarise()

dplyr::summarise() permet d’agréger les lignes du tableau


en effectuant une opération résumée sur une ou plusieurs co-
lonnes. Il s’agit de toutes les fonctions qui prennent en entrée
un ensemble de valeurs et renvoie une valeur unique, comme la
moyenne (mean()). Par exemple, si l’on souhaite connaître les
retards moyens au départ et à l’arrivée pour l’ensemble des vols
du tableau flights :

flights |>
summarise(
retard_dep = mean(dep_delay, [Link]=TRUE),
retard_arr = mean(arr_delay, [Link]=TRUE)
)

# A tibble: 1 x 2
retard_dep retard_arr
<dbl> <dbl>
1 12.6 6.90

Cette fonction est en général utilisée avec dplyr::group_by(),


puisqu’elle permet du coup d’agréger et de résumer les lignes
du tableau groupe par groupe. Si l’on souhaite calculer le délai
maximum, le délai minimum et le délai moyen au départ pour
chaque mois, on pourra faire :

111
flights |>
group_by(month) |>
summarise(
max_delay = max(dep_delay, [Link]=TRUE),
min_delay = min(dep_delay, [Link]=TRUE),
mean_delay = mean(dep_delay, [Link]=TRUE)
)

# A tibble: 12 x 4
month max_delay min_delay mean_delay
<int> <dbl> <dbl> <dbl>
1 1 1301 -30 10.0
2 2 853 -33 10.8
3 3 911 -25 13.2
4 4 960 -21 13.9
5 5 878 -24 13.0
6 6 1137 -21 20.8
7 7 1005 -22 21.7
8 8 520 -26 12.6
9 9 1014 -24 6.72
10 10 702 -25 6.24
11 11 798 -32 5.44
12 12 896 -43 16.6

dplyr::summarise() dispose d’une fonction spéciale


dplyr::n(), qui retourne le nombre de lignes du groupe.
Ainsi si l’on veut le nombre de vols par destination, on peut
utiliser :

flights |>
group_by(dest) |>
summarise(n = n())

# A tibble: 105 x 2
dest n
<chr> <int>
1 ABQ 254
2 ACK 265
3 ALB 439

112
4 ANC 8
5 ATL 17215
6 AUS 2439
7 AVL 275
8 BDL 443
9 BGR 375
10 BHM 297
# i 95 more rows

dplyr::n() peut aussi être utilisée avec dplyr::filter() et


dplyr::mutate().

8.3.3 count()

À noter que quand l’on veut compter le nombre de lignes


par groupe, on peut utiliser directement la fonction
dplyr::count(). Ainsi le code suivant est identique au
précédent :

flights |>
count(dest)

# A tibble: 105 x 2
dest n
<chr> <int>
1 ABQ 254
2 ACK 265
3 ALB 439
4 ANC 8
5 ATL 17215
6 AUS 2439
7 AVL 275
8 BDL 443
9 BGR 375
10 BHM 297
# i 95 more rows

113
8.3.4 Grouper selon plusieurs variables

On peut grouper selon plusieurs variables à la fois, il suffit de


les indiquer dans la clause du dplyr::group_by() :

flights |>
group_by(month, dest) |>
summarise(nb = n()) |>
arrange(desc(nb))

`summarise()` has grouped output by 'month'. You can override using the
`.groups` argument.

# A tibble: 1,113 x 3
# Groups: month [12]
month dest nb
<int> <chr> <int>
1 8 ORD 1604
2 10 ORD 1604
3 5 ORD 1582
4 9 ORD 1582
5 7 ORD 1573
6 6 ORD 1547
7 7 ATL 1511
8 8 ATL 1507
9 8 LAX 1505
10 7 LAX 1500
# i 1,103 more rows

On peut également compter selon plusieurs variables :

flights |>
count(origin, dest) |>
arrange(desc(n))

# A tibble: 224 x 3
origin dest n
<chr> <chr> <int>

114
1 JFK LAX 11262
2 LGA ATL 10263
3 LGA ORD 8857
4 JFK SFO 8204
5 LGA CLT 6168
6 EWR ORD 6100
7 JFK BOS 5898
8 LGA MIA 5781
9 JFK MCO 5464
10 EWR BOS 5327
# i 214 more rows

On peut utiliser plusieurs opérations de groupage dans le


même pipeline. Ainsi, si l’on souhaite déterminer le couple
origine/destination ayant le plus grand nombre de vols selon le
mois de l’année, on devra procéder en deux étapes :

• d’abord grouper selon mois, origine et destination pour


calculer le nombre de vols
• puis grouper uniquement selon le mois pour sélectionner
la ligne avec la valeur maximale.

Au final, on obtient le code suivant :

flights |>
group_by(month, origin, dest) |>
summarise(nb = n()) |>
group_by(month) |>
filter(nb == max(nb))

`summarise()` has grouped output by 'month', 'origin'. You can override using
the `.groups` argument.

# A tibble: 12 x 4
# Groups: month [12]
month origin dest nb
<int> <chr> <chr> <int>
1 1 JFK LAX 937
2 2 JFK LAX 834
3 3 JFK LAX 960

115
4 4 JFK LAX 935
5 5 JFK LAX 960
6 6 JFK LAX 928
7 7 JFK LAX 985
8 8 JFK LAX 979
9 9 JFK LAX 925
10 10 JFK LAX 965
11 11 JFK LAX 907
12 12 JFK LAX 947

Lorsqu’on effectue un dplyr::group_by() suivi d’un


dplyr::summarise(), le tableau résultat est automati-
quement dégroupé de la dernière variable de regroupement.
Ainsi le tableau généré par le code suivant est groupé par
month et origin15 : 15
Comme expliqué dans le message
affiché dans la console, cela peut être
contrôlé avec l’argument .groups de
flights |>
dplyr::summarise(), dont les op-
group_by(month, origin, dest) |> tions sont décrites dans l’aide de la
summarise(nb = n()) fonction.

`summarise()` has grouped output by 'month', 'origin'. You can override using
the `.groups` argument.

# A tibble: 2,313 x 4
# Groups: month, origin [36]
month origin dest nb
<int> <chr> <chr> <int>
1 1 EWR ALB 64
2 1 EWR ATL 362
3 1 EWR AUS 51
4 1 EWR AVL 2
5 1 EWR BDL 37
6 1 EWR BNA 111
7 1 EWR BOS 430
8 1 EWR BQN 31
9 1 EWR BTV 100
10 1 EWR BUF 119
# i 2,303 more rows

116
Cela peut permettre d’enchaîner les opérations groupées. Dans
l’exemple suivant, on calcule le pourcentage des trajets pour
chaque destination par rapport à tous les trajets du mois :

flights |>
group_by(month, dest) |>
summarise(nb = n()) |>
mutate(pourcentage = nb / sum(nb) * 100)

`summarise()` has grouped output by 'month'. You can override using the
`.groups` argument.

# A tibble: 1,113 x 4
# Groups: month [12]
month dest nb pourcentage
<int> <chr> <int> <dbl>
1 1 ALB 64 0.237
2 1 ATL 1396 5.17
3 1 AUS 169 0.626
4 1 AVL 2 0.00741
5 1 BDL 37 0.137
6 1 BHM 25 0.0926
7 1 BNA 399 1.48
8 1 BOS 1245 4.61
9 1 BQN 93 0.344
10 1 BTV 223 0.826
# i 1,103 more rows

On peut à tout moment dégrouper un tableau à l’aide de


dplyr::ungroup(). Ce serait par exemple nécessaire, dans
l’exemple précédent, si on voulait calculer le pourcentage sur
le nombre total de vols plutôt que sur le nombre de vols par
mois :

flights |>
group_by(month, dest) |>
summarise(nb = n()) |>
ungroup() |>
mutate(pourcentage = nb / sum(nb) * 100)

117
`summarise()` has grouped output by 'month'. You can override using the
`.groups` argument.

# A tibble: 1,113 x 4
month dest nb pourcentage
<int> <chr> <int> <dbl>
1 1 ALB 64 0.0190
2 1 ATL 1396 0.415
3 1 AUS 169 0.0502
4 1 AVL 2 0.000594
5 1 BDL 37 0.0110
6 1 BHM 25 0.00742
7 1 BNA 399 0.118
8 1 BOS 1245 0.370
9 1 BQN 93 0.0276
10 1 BTV 223 0.0662
# i 1,103 more rows

À noter que dplyr::count(), par contre, renvoi un tableau


non groupé :

flights |>
count(month, dest)

# A tibble: 1,113 x 3
month dest n
<int> <chr> <int>
1 1 ALB 64
2 1 ATL 1396
3 1 AUS 169
4 1 AVL 2
5 1 BDL 37
6 1 BHM 25
7 1 BNA 399
8 1 BOS 1245
9 1 BQN 93
10 1 BTV 223
# i 1,103 more rows

118
8.4 Cheatsheet

8.5 webin-R

On pourra également se référer au webin-R #04 (manipuler les


données avec dplyr) sur YouTube.
[Link]

119
9 Facteurs et forcats

Dans R, les facteurs sont utilisés pour représenter des variables


catégorielles, c’est-à-dire des variables qui ont un nombre fixé
et limité de valeurs possibles (par exemple une variable sexe ou
une variable niveau d’éducation).
De telles variables sont parfois représentées sous forme textuelle
(vecteurs de type character). Cependant, cela ne permets pas
d’indiquer un ordre spécifique aux modalités, à la différence des
facteurs.

Ĺ Note

Lorsque l’on importe des données d’enquêtes, il est fré-


quent que les variables catégorielles sont codées sous la
forme d’un code numérique (par exemple 1 pour femme et
2 pour homme) auquel est associé une étiquette de valeur.
C’est notamment le fonctionnement usuel de logiciels tels
que SPSS, Stata ou SAS. Les étiquettes de valeurs se-
ront abordés dans un prochain chapitre (voir Chapitre 12).
Au moment de l’analyse (tableaux statistiques, gra-
phiques, modèles de régression…), il sera nécessaire de
transformer ces vecteurs avec étiquettes en facteurs.

9.1 Création d’un facteur

Le plus simple pour créer un facteur est de partir d’un vecteur


textuel et d’utiliser la fonction factor().

x <- c("nord", "sud", "sud", "est", "est", "est")


x |>
factor()

120
[1] nord sud sud est est est
Levels: est nord sud

Par défaut, les niveaux du facteur obtenu correspondent aux


valeurs uniques du facteur textuel, triés par ordre alphabétique.
Si l’on veut contrôler l’ordre des niveaux, et éventuellement
indiquer un niveau absent des données, on utilisera l’argument
levels de factor().

x |>
factor(levels = c("nord", "est", "sud", "ouest"))

[1] nord sud sud est est est


Levels: nord est sud ouest

Si une valeur observée dans les données n’est pas indiqué dans
levels, elle sera silencieusement convertie en valeur manquante
(NA).

x |>
factor(levels = c("nord", "sud"))

[1] nord sud sud <NA> <NA> <NA>


Levels: nord sud

Si l’on veut être averti par un warning dans ce genre de


situation, on pourra avoir plutôt recours à la fonction
readr::parse_factor() du package {readr}, qui, le cas
échéant, renverra un tableau avec les problèmes rencontrés.

x |>
readr::parse_factor(levels = c("nord", "sud"))

Warning: 3 parsing failures.


row col expected actual
4 -- value in level set est
5 -- value in level set est
6 -- value in level set est

121
[1] nord sud sud <NA> <NA> <NA>
attr(,"problems")
# A tibble: 3 x 4
row col expected actual
<int> <int> <chr> <chr>
1 4 NA value in level set est
2 5 NA value in level set est
3 6 NA value in level set est
Levels: nord sud

Une fois un facteur créé, on peut accéder à la liste de ses éti-


quettes avec levels().

f <- factor(x)
levels(f)

[1] "est" "nord" "sud"

Dans certaines situations (par exemple pour la réalisation


d’une régression logistique ordinale), on peut avoir avoir
besoin d’indiquer que les modalités du facteur sont ordonnées
hiérarchiquement. Dans ce cas là, on aura simplement recours
à ordered() pour créer/convertir notre facteur.

c("supérieur", "primaire", "secondaire", "primaire", "supérieur") |>


ordered(levels = c("primaire", "secondaire", "supérieur"))

[1] supérieur primaire secondaire primaire supérieur


Levels: primaire < secondaire < supérieur

Techniquement, les valeurs d’un facteur sont stockés de ma-


nière interne à l’aide de nombres entiers, dont la valeur repré-
sente la position de l’étiquette correspondante dans l’attribut
levels. Ainsi, un facteur à n modalités sera toujours codé avec
les nombre entiers allant de 1 à n.

class(f)

[1] "factor"

122
typeof(f)

[1] "integer"

[Link](f)

[1] 2 3 3 1 1 1

[Link](f)

[1] "nord" "sud" "sud" "est" "est" "est"

9.2 Changer l’ordre des modalités

Le package {forcats}, chargé par défaut lorsque l’on exécute


la commande library(tidyverse), fournie plusieurs fonc-
tions pour manipuler des facteurs. Pour donner des exemples
d’utilisation de ces différentes fonctions, nous allons utiliser le
jeu de données hdv2003 du package {questionr}.

library(tidyverse)
data("hdv2003", package = "questionr")

Considérons la variable qualif qui indique le niveau de quali-


fication des enquêtés. On peut voir la liste des niveaux de ce
facteur, et leur ordre, avec levels(), ou en effectuant un tri à
plat avec la fonction questionr::freq().

hdv2003$qualif |>
levels()

[1] "Ouvrier specialise" "Ouvrier qualifie"


[3] "Technicien" "Profession intermediaire"
[5] "Cadre" "Employe"
[7] "Autre"

123
hdv2003$qualif |>
questionr::freq()

n % val%
Ouvrier specialise 203 10.2 12.3
Ouvrier qualifie 292 14.6 17.7
Technicien 86 4.3 5.2
Profession intermediaire 160 8.0 9.7
Cadre 260 13.0 15.7
Employe 594 29.7 35.9
Autre 58 2.9 3.5
NA 347 17.3 NA

Parfois, on a simplement besoin d’inverser l’ordre des


facteurs, ce qui peut se faire facilement avec la fonction
forcats::fct_rev(). Elle renvoie le facteur fourni en entrée
en ayant inverser l’ordre des modalités (mais sans modifier
l’ordre des valeurs dans le vecteur).

hdv2003$qualif |>
fct_rev() |>
questionr::freq()

n % val%
Autre 58 2.9 3.5
Employe 594 29.7 35.9
Cadre 260 13.0 15.7
Profession intermediaire 160 8.0 9.7
Technicien 86 4.3 5.2
Ouvrier qualifie 292 14.6 17.7
Ouvrier specialise 203 10.2 12.3
NA 347 17.3 NA

Pour plus de contrôle, on utilisera forcats::fct_relevel()


où l’on indique l’ordre souhaité des modalités. On peut éga-
lement seulement indiquer les premières modalités, les autres
seront ajoutées à la fin sans changer leur ordre.

124
hdv2003$qualif |>
fct_relevel("Cadre", "Autre", "Technicien", "Employe") |>
questionr::freq()

n % val%
Cadre 260 13.0 15.7
Autre 58 2.9 3.5
Technicien 86 4.3 5.2
Employe 594 29.7 35.9
Ouvrier specialise 203 10.2 12.3
Ouvrier qualifie 292 14.6 17.7
Profession intermediaire 160 8.0 9.7
NA 347 17.3 NA

La fonction forcats::fct_infreq() ordonne les modalités


de celle la plus fréquente à celle la moins fréquente (nombre
d’observations) :

hdv2003$qualif |>
fct_infreq() |>
questionr::freq()

n % val%
Employe 594 29.7 35.9
Ouvrier qualifie 292 14.6 17.7
Cadre 260 13.0 15.7
Ouvrier specialise 203 10.2 12.3
Profession intermediaire 160 8.0 9.7
Technicien 86 4.3 5.2
Autre 58 2.9 3.5
NA 347 17.3 NA

Pour inverser l’ordre, on combinera forcats::fct_infreq()


avec forcats::fct_rev().

hdv2003$qualif |>
fct_infreq() |>
fct_rev() |>

125
questionr::freq()

n % val%
Autre 58 2.9 3.5
Technicien 86 4.3 5.2
Profession intermediaire 160 8.0 9.7
Ouvrier specialise 203 10.2 12.3
Cadre 260 13.0 15.7
Ouvrier qualifie 292 14.6 17.7
Employe 594 29.7 35.9
NA 347 17.3 NA

Dans certains cas, on souhaite créer un facteur dont les modali-


tés sont triées selon leur ordre d'apparition dans le jeu de don-
nées. Pour cela, on aura recours à forcats::fct_inorder().

v <- c("c", "a", "d", "b", "a", "c")


factor(v)

[1] c a d b a c
Levels: a b c d

fct_inorder(v)

[1] c a d b a c
Levels: c a d b

La fonction forcats::fct_reorder() permets de trier les mo-


dalités en fonction d’une autre variable. Par exemple, si je sou-
haite trier les modalités de la variable qualif en fonction de l’âge
moyen (dans chaque modalité) :

hdv2003$qualif_tri_age <-
hdv2003$qualif |>
fct_reorder(hdv2003$age, .fun = mean)
hdv2003 |>
dplyr::group_by(qualif_tri_age) |>

126
dplyr::summarise(age_moyen = mean(age))

# A tibble: 8 x 2
qualif_tri_age age_moyen
<fct> <dbl>
1 Technicien 45.9
2 Employe 46.7
3 Autre 47.0
4 Ouvrier specialise 48.9
5 Profession intermediaire 49.1
6 Cadre 49.7
7 Ouvrier qualifie 50.0
8 <NA> 47.9

Ď Astuce

{questionr} propose une interface graphique afin de faci-


liter les opérations de ré-ordonnancement manuel. Pour la
lancer, sélectionner le menu Addins puis Levels ordering,
ou exécuter la fonction questionr::iorder() en lui pas-
sant comme paramètre le facteur à réordonner.

127
Une démonstration en vidéo de cet add-in est disponible
dans le webin-R #05 (recoder des variables) sur [You-
Tube]([Link]
[Link]

9.3 Modifier les modalités

Pour modifier le nom des modalités, on pourra avoir recours


à forcats::fct_recode() avec une syntaxe de la forme
"nouveau nom" = "ancien nom".

hdv2003$sexe |>
questionr::freq()

n % val%
Homme 899 45 45
Femme 1101 55 55

hdv2003$sexe <-
hdv2003$sexe |>
fct_recode(f = "Femme", m = "Homme")
hdv2003$sexe |>
questionr::freq()

n % val%
m 899 45 45
f 1101 55 55

On peut également fusionner des modalités ensemble en leur


attribuant le même nom.

hdv2003$nivetud |>
questionr::freq()

128
n % val%
N'a jamais fait d'etudes 39 2.0 2.1
A arrete ses etudes, avant la derniere annee d'etudes primaires 86 4.3 4.6
Derniere annee d'etudes primaires 341 17.0 18.1
1er cycle 204 10.2 10.8
2eme cycle 183 9.2 9.7
Enseignement technique ou professionnel court 463 23.2 24.5
Enseignement technique ou professionnel long 131 6.6 6.9
Enseignement superieur y compris technique superieur 441 22.0 23.4
NA 112 5.6 NA

hdv2003$instruction <-
hdv2003$nivetud |>
fct_recode(
"primaire" = "N'a jamais fait d'etudes",
"primaire" = "A arrete ses etudes, avant la derniere annee d'etudes primaires",
"primaire" = "Derniere annee d'etudes primaires",
"secondaire" = "1er cycle",
"secondaire" = "2eme cycle",
"technique/professionnel" = "Enseignement technique ou professionnel court",
"technique/professionnel" = "Enseignement technique ou professionnel long",
"supérieur" = "Enseignement superieur y compris technique superieur"
)
hdv2003$instruction |>
questionr::freq()

n % val%
primaire 466 23.3 24.7
secondaire 387 19.4 20.5
technique/professionnel 594 29.7 31.5
supérieur 441 22.0 23.4
NA 112 5.6 NA

Ď Interface graphique

Le package{questionr} propose une interface graphique


facilitant le recodage des modalités d’une variable qualita-
tive. L’objectif est de permettre à la personne qui l’utilise
de saisir les nouvelles valeurs dans un formulaire, et de

129
générer ensuite le code R correspondant au recodage indi-
qué.
Pour utiliser cette interface, sous RStudio vous pouvez
aller dans le menu Addins (présent dans la barre d’outils
principale) puis choisir Levels recoding. Sinon, vous pouvez
lancer dans la console la fonction questionr::irec() en
lui passant comme paramètre la variable à recoder.

Ď Astuce

Une démonstration en vidéo de cet


add-in est disponible dans le webin-R
#05 (recoder des variables) sur [You-
Tube]([Link]
[Link]

La fonction forcats::fct_collapse() est une variante


de forcats::fct_recode() pour indiquer les fusions de
modalités. La même recodification s’écrirait alors :

130
hdv2003$instruction <-
hdv2003$nivetud |>
fct_collapse(
"primaire" = c(
"N'a jamais fait d'etudes",
"A arrete ses etudes, avant la derniere annee d'etudes primaires",
"Derniere annee d'etudes primaires"
),
"secondaire" = c(
"1er cycle",
"2eme cycle"
),
"technique/professionnel" = c(
"Enseignement technique ou professionnel court",
"Enseignement technique ou professionnel long"
),
"supérieur" = "Enseignement superieur y compris technique superieur"
)

Pour transformer les valeurs manquantes (NA) en une modalité


16 16
explicite, on pourra avoir recours à forcats::fct_na_value_to_level() . Cette fonction
s’appelait précédemment
forcats::fct_explicit_na()
hdv2003$instruction <-
et a été renommée depuis la version
hdv2003$instruction |> 1.0.0 de {forcats}.
fct_na_value_to_level(level = "(manquant)")
hdv2003$instruction |>
questionr::freq()

n % val%
primaire 466 23.3 23.3
secondaire 387 19.4 19.4
technique/professionnel 594 29.7 29.7
supérieur 441 22.0 22.0
(manquant) 112 5.6 5.6

Plusieurs fonctions permettent de regrouper plusieurs modali-


tés dans une modalité autres.
Par exemple, avec forcats::fct_other(), on pourra indiquer
les modalités à garder.

131
hdv2003$qualif |>
questionr::freq()

n % val%
Ouvrier specialise 203 10.2 12.3
Ouvrier qualifie 292 14.6 17.7
Technicien 86 4.3 5.2
Profession intermediaire 160 8.0 9.7
Cadre 260 13.0 15.7
Employe 594 29.7 35.9
Autre 58 2.9 3.5
NA 347 17.3 NA

hdv2003$qualif |>
fct_other(keep = c("Technicien", "Cadre", "Employe")) |>
questionr::freq()

n % val%
Technicien 86 4.3 5.2
Cadre 260 13.0 15.7
Employe 594 29.7 35.9
Other 713 35.6 43.1
NA 347 17.3 NA

La fonction forcats::fct_lump_n() permets de ne conserver


que les modalités les plus fréquentes et de regrouper les autres
dans une modalité autres.

hdv2003$qualif |>
fct_lump_n(n = 4, other_level = "Autres") |>
questionr::freq()

n % val%
Ouvrier specialise 203 10.2 12.3
Ouvrier qualifie 292 14.6 17.7
Cadre 260 13.0 15.7
Employe 594 29.7 35.9

132
Autres 304 15.2 18.4
NA 347 17.3 NA

Et forcats::fct_lump_min() celles qui ont un minimum


d’observations.

hdv2003$qualif |>
fct_lump_min(min = 200, other_level = "Autres") |>
questionr::freq()

n % val%
Ouvrier specialise 203 10.2 12.3
Ouvrier qualifie 292 14.6 17.7
Cadre 260 13.0 15.7
Employe 594 29.7 35.9
Autres 304 15.2 18.4
NA 347 17.3 NA

Il peut arriver qu’une des modalités d’un facteur ne soit pas


représentée dans les données.

v <- factor(
c("a", "a", "b", "a"),
levels = c("a", "b", "c")
)
questionr::freq(v)

n % val%
a 3 75 75
b 1 25 25
c 0 0 0

Pour calculer certains tests statistiques ou faire tourner un


modèle, ces modalités sans observation peuvent être probléma-
tiques. forcats::fct_drop() permet de supprimer les moda-
lités qui n’apparaissent pas dans les données.

133
[1] a a b a
Levels: a b c

v |> fct_drop()

[1] a a b a
Levels: a b

À l’inverse, forcats::fct_expand() permet d’ajouter une ou


plusieurs modalités à un facteur.

[1] a a b a
Levels: a b c

v |> fct_expand("d", "e")

[1] a a b a
Levels: a b c d e

9.4 Découper une variable numérique en


classes

Il est fréquent d’avoir besoin de découper une variable numé-


rique en une variable catégorielles (un facteur) à plusieurs mo-
dalités, par exemple pour créer des groupes d’âges à partir
d’une variable age.
On utilise pour cela la fonction cut() qui prend, outre la va-
riable à découper, un certain nombre d’arguments :

• breaks indique soit le nombre de classes souhaité, soit, si


on lui fournit un vecteur, les limites des classes ;
• labels permet de modifier les noms de modalités attri-
bués aux classes ;

134
• [Link] et right influent sur la manière dont
les valeurs situées à la frontière des classes seront inclues
ou exclues ;
• [Link] indique le nombre de chiffres après la virgule à
conserver dans les noms de modalités.

Prenons tout de suite un exemple et tentons de découper la


variable age en cinq classes :

hdv2003 <-
hdv2003 |>
mutate(groupe_ages = cut(age, 5))
hdv2003$groupe_ages |> questionr::freq()

n % val%
(17.9,33.8] 454 22.7 22.7
(33.8,49.6] 628 31.4 31.4
(49.6,65.4] 556 27.8 27.8
(65.4,81.2] 319 16.0 16.0
(81.2,97.1] 43 2.1 2.1

Par défaut R nous a bien créé cinq classes d’amplitudes égales.


La première classe va de 17,9 à 33,8 ans (en fait de 17 à 32),
etc.
Les frontières de classe seraient plus présentables si elles utili-
saient des nombres ronds. On va donc spécifier manuellement
le découpage souhaité, par tranches de 20 ans :

hdv2003 <-
hdv2003 |>
mutate(groupe_ages = cut(age, c(18, 20, 40, 60, 80, 97)))
hdv2003$groupe_ages |> questionr::freq()

n % val%
(18,20] 55 2.8 2.8
(20,40] 660 33.0 33.3
(40,60] 780 39.0 39.3
(60,80] 436 21.8 22.0
(80,97] 52 2.6 2.6

135
NA 17 0.9 NA

Les symboles dans les noms attribués aux classes ont leur im-
portance : ( signifie que la frontière de la classe est exclue,
tandis que [ signifie qu’elle est incluse. Ainsi, (20,40] signifie
« strictement supérieur à 20 et inférieur ou égal à 40 ».
On remarque que du coup, dans notre exemple précédent, la va-
leur minimale, 18, est exclue de notre première classe, et qu’une
observation est donc absente de ce découpage. Pour résoudre
ce problème on peut soit faire commencer la première classe à
17, soit utiliser l’option [Link]=TRUE :

hdv2003 <-
hdv2003 |>
mutate(groupe_ages = cut(
age,
c(18, 20, 40, 60, 80, 97),
[Link] = TRUE
))
hdv2003$groupe_ages |> questionr::freq()

n % val%
[18,20] 72 3.6 3.6
(20,40] 660 33.0 33.0
(40,60] 780 39.0 39.0
(60,80] 436 21.8 21.8
(80,97] 52 2.6 2.6

On peut également modifier le sens des intervalles avec l’option


right=FALSE :

hdv2003 <-
hdv2003 |>
mutate(groupe_ages = cut(
age,
c(18, 20, 40, 60, 80, 97),
[Link] = TRUE,
right = FALSE
))

136
hdv2003$groupe_ages |> questionr::freq()

n % val%
[18,20) 48 2.4 2.4
[20,40) 643 32.1 32.1
[40,60) 793 39.6 39.6
[60,80) 454 22.7 22.7
[80,97] 62 3.1 3.1

Ď Interface graphique

Il n’est pas nécessaire de connaître toutes les options de


cut(). Le package {questionr} propose là encore une in-
terface graphique permettant de visualiser l’effet des diffé-
rents paramètres et de générer le code R correspondant.
Pour utiliser cette interface, sous RStudio vous pou-
vez aller dans le menu Addins (présent dans la barre
d’outils principale) puis choisir Numeric range dividing.
Sinon, vous pouvez lancer dans la console la fonction
questionr::icut() en lui passant comme paramètre la
variable numérique à découper.

137
Une démonstration en vidéo de cet add-in est disponible
dans le webin-R #05 (recoder des variables) sur [You-
Tube]([Link]
[Link]

138
10 Combiner plusieurs
variables

Parfois, on a besoin de créer une nouvelle variable en partant


des valeurs d’une ou plusieurs autres variables. Dans ce cas on
peut utiliser les fonctions dplyr::if_else() pour les cas les
plus simples, ou dplyr::case_when() pour les cas plus com-
plexes.
Une fois encore, nous utiliser le jeu de données hdv2003 pour
illustrer ces différentes fonctions.

library(tidyverse)
data("hdv2003", package = "questionr")

10.1 if_else()

dplyr::if_else() prend trois arguments : un test, les valeurs


à renvoyer si le test est vrai, et les valeurs à renvoyer si le test
est faux.
Voici un exemple simple :

v <- c(12, 14, 8, 16)


if_else(v > 10, "Supérieur à 10", "Inférieur à 10")

[1] "Supérieur à 10" "Supérieur à 10" "Inférieur à 10" "Supérieur à 10"

La fonction devient plus intéressante avec des tests combinant


plusieurs variables. Par exemple, imaginons qu’on souhaite
créer une nouvelle variable indiquant les hommes de plus de
60 ans :

139
hdv2003 <-
hdv2003 |>
mutate(
statut = if_else(
sexe == "Homme" & age > 60,
"Homme de plus de 60 ans",
"Autre"
)
)
hdv2003 |>
pull(statut) |>
questionr::freq()

n % val%
Autre 1778 88.9 88.9
Homme de plus de 60 ans 222 11.1 11.1

Il est possible d’utiliser des variables ou des combinaisons de


variables au sein du dplyr::if_else(). Supposons une petite
enquête menée auprès de femmes et d’hommes. Le question-
naire comportait une question de préférence posée différemment
aux femmes et aux hommes et dont les réponses ont ainsi été
collectées dans deux variables différentes, pref_f et pref_h, que
l’on souhaite combiner en une seule variable. De même, une cer-
taine mesure quantitative a été réalisée, mais une correction est
nécessaire pour normaliser ce score (retirer 0.4 aux scores des
hommes et 0.6 aux scores des femmes). Cela peut être réalisé
avec le code ci-dessous.

df <- tibble(
sexe = c("f", "f", "h", "h"),
pref_f = c("a", "b", NA, NA),
pref_h = c(NA, NA, "c", "d"),
mesure = c(1.2, 4.1, 3.8, 2.7)
)
df

# A tibble: 4 x 4
sexe pref_f pref_h mesure

140
<chr> <chr> <chr> <dbl>
1 f a <NA> 1.2
2 f b <NA> 4.1
3 h <NA> c 3.8
4 h <NA> d 2.7

df <-
df |>
mutate(
pref = if_else(sexe == "f", pref_f, pref_h),
indicateur = if_else(sexe == "h", mesure - 0.4, mesure - 0.6)
)
df

# A tibble: 4 x 6
sexe pref_f pref_h mesure pref indicateur
<chr> <chr> <chr> <dbl> <chr> <dbl>
1 f a <NA> 1.2 a 0.6
2 f b <NA> 4.1 b 3.5
3 h <NA> c 3.8 c 3.4
4 h <NA> d 2.7 d 2.3

ĺ if_else() et ifelse()

La fonction dplyr::if_else() ressemble à la fonction


ifelse() en base R. Il y a néanmoins quelques petites
différences :

• dplyr::if_else() vérifie que les valeurs fournies


pour true et celles pour false sont du même type et
de la même classe et renvoie une erreur dans le cas
contraire, là où ifelse() sera plus permissif ;
• si un vecteur a des attributs (cf. Chapitre 6), ils se-
ront préservés par dplyr::if_else() (et pris dans
le vecteur true), ce que ne fera pas if_else() ;
• dplyr::if_else() propose un argument optionnel
supplémentaire missing pour indiquer les valeurs à
retourner lorsque le test renvoie NA.

141
10.2 case_when()

dplyr::case_when() est une généralisation de dplyr::if_else()


qui permet d’indiquer plusieurs tests et leurs valeurs asso-
ciées.
Imaginons que l’on souhaite créer une nouvelle variable per-
mettant d’identifier les hommes de plus de 60 ans, les femmes
de plus de 60 ans, et les autres. On peut utiliser la syntaxe
suivante :

hdv2003 <-
hdv2003 |>
mutate(
statut = case_when(
age >= 60 & sexe == "Homme" ~ "Homme, 60 et plus",
age >= 60 & sexe == "Femme" ~ "Femme, 60 et plus",
TRUE ~ "Autre"
)
)
hdv2003 |>
pull(statut) |>
questionr::freq()

n % val%
Autre 1484 74.2 74.2
Femme, 60 et plus 278 13.9 13.9
Homme, 60 et plus 238 11.9 11.9

dplyr::case_when() prend en arguments une série


d’instructions sous la forme condition ~ valeur. Il les
exécute une par une, et dès qu’une condition est vraie, il
renvoi la valeur associée.
La clause TRUE ~ "Autre" permet d’assigner une valeur
à toutes les lignes pour lesquelles aucune des conditions
précédentes n’est vraie.

142
ĺ Important

Attention : comme les conditions sont testées l’une après


l’autre et que la valeur renvoyée est celle correspondant à
la première condition vraie, l’ordre de ces conditions est
très important. Il faut absolument aller du plus spécifique
au plus général.
Par exemple le recodage suivant ne fonctionne pas :

hdv2003 <-
hdv2003 |>
mutate(
statut = case_when(
sexe == "Homme" ~ "Homme",
age >= 60 & sexe == "Homme" ~ "Homme, 60 et plus",
TRUE ~ "Autre"
)
)
hdv2003 |>
pull(statut) |>
questionr::freq()

n % val%
Autre 1101 55 55
Homme 899 45 45

Comme la condition sexe == "Homme" est plus géné-


rale que sexe == "Homme" & age > 60, cette deuxième
condition n’est jamais testée ! On n’obtiendra jamais la
valeur correspondante.
Pour que ce recodage fonctionne il faut donc changer
l’ordre des conditions pour aller du plus spécifique au plus
général :

143
hdv2003 <-
hdv2003 |>
mutate(
statut = case_when(
age >= 60 & sexe == "Homme" ~ "Homme, 60 et plus",
sexe == "Homme" ~ "Homme",
TRUE ~ "Autre"
)
)
hdv2003 |>
pull(statut) |>
questionr::freq()

n % val%
Autre 1101 55.0 55.0
Homme 661 33.1 33.1
Homme, 60 et plus 238 11.9 11.9

C’est pour cela que l’on peut utiliser, en toute dernière


condition, la valeur TRUE pour indiquer dans tous les
autres cas.

10.3 recode_if()

Parfois, on n’a besoin de ne modifier une variable que pour


certaines observations. Prenons un petit exemple :

df <- tibble(
pref = factor(c("bleu", "rouge", "autre", "rouge", "autre")),
autre_details = c(NA, NA, "bleu ciel", NA, "jaune")
)
df

# A tibble: 5 x 2
pref autre_details
<fct> <chr>
1 bleu <NA>

144
2 rouge <NA>
3 autre bleu ciel
4 rouge <NA>
5 autre jaune

Nous avons demandé aux enquêtés d’indiquer leur couleur pré-


férée. Ils pouvaient répondre bleu ou rouge et avait également la
possibilité de choisir autre et d’indiquer la valeur de leur choix
dans un champs textuel libre.
Une des personnes enquêtées a choisi autre et a indiqué dans le
champs texte la valeur bleu ciel. Pour les besoins de l’analyse,
on peut considérer que cette valeur bleu ciel pour être tout
simplement recodée en bleu.
En syntaxe R classique, on pourra simplement faire :

df$pref[df$autre_details == "bleu ciel"] <- "bleu"

Avec dplyr::if_else(), on serait tenté d’écrire :

df |>
mutate(pref = if_else(autre_details == "bleu ciel", "bleu", pref))

# A tibble: 5 x 2
pref autre_details
<chr> <chr>
1 <NA> <NA>
2 <NA> <NA>
3 bleu bleu ciel
4 <NA> <NA>
5 autre jaune

On obtient une erreur, car dplyr::if_else() exige les valeurs


fournie pour true et false soient de même type. Essayons
alors :

df |>
mutate(pref = if_else(autre_details == "bleu ciel", factor("bleu"), pref))

145
# A tibble: 5 x 2
pref autre_details
<fct> <chr>
1 <NA> <NA>
2 <NA> <NA>
3 bleu bleu ciel
4 <NA> <NA>
5 autre jaune

Ici nous avons un autre problème, signalé par un message


d’avertissement (warning) : dplyr::if_else() ne préserve
que les attributs du vecteur passé en true et non ceux passés
à false. Or l’ensemble des modalités (niveaux du facteur) de
la variable pref n’ont pas été définis dans factor("bleu")
et sont ainsi perdus, générant une perte de données (valeurs
manquantes NA).
Pour obtenir le bon résultat, il faudrait inverser la condition :

df |>
mutate(pref = if_else(
autre_details != "bleu ciel",
pref,
factor("bleu")
))

# A tibble: 5 x 2
pref autre_details
<fct> <chr>
1 <NA> <NA>
2 <NA> <NA>
3 bleu bleu ciel
4 <NA> <NA>
5 autre jaune

Mais ce n’est toujours pas suffisant. En effet, la variable


autre_details a des valeurs manquantes pour lesquelles le
test autre_details != "bleu ciel" renvoie NA ce qui une
fois encore génère des valeurs manquantes non souhaitées.

146
Dès lors, il nous faut soit définir l’argument missing de
dplyr::if_else(), soit être plus précis dans notre test.

df |>
mutate(pref = if_else(
autre_details != "bleu ciel",
pref,
factor("bleu"),
missing = pref
))

# A tibble: 5 x 2
pref autre_details
<fct> <chr>
1 bleu <NA>
2 rouge <NA>
3 bleu bleu ciel
4 rouge <NA>
5 autre jaune

df |>
mutate(pref = if_else(
autre_details != "bleu ciel" | [Link](autre_details),
pref,
factor("bleu")
))

# A tibble: 5 x 2
pref autre_details
<fct> <chr>
1 bleu <NA>
2 rouge <NA>
3 bleu bleu ciel
4 rouge <NA>
5 autre jaune

Bref, on peut s’en sortir avec dplyr::if_else() mais ce n’est


pas forcément le plus pratique dans le cas présent. La syntaxe

147
en base R fonctionne très bien, mais ne peut pas être intégrée
à un enchaînement d’opérations utilisant le pipe.
Dans ce genre de situation, on pourra être intéressé par la
fonction labelled::recode_if() disponible dans le package
{labelled}. Elle permet de ne modifier que certaines observa-
tions d’un vecteur en fonction d’une condition. Si la condition
vaut FALSE ou NA, les observations concernées restent inchan-
gées. Voyons comment cela s’écrit :

df <-
df |>
mutate(
pref = pref |>
labelled::recode_if(autre_details == "bleu ciel", "bleu")
)
df

# A tibble: 5 x 2
pref autre_details
<fct> <chr>
1 bleu <NA>
2 rouge <NA>
3 bleu bleu ciel
4 rouge <NA>
5 autre jaune

C’est tout de suite plus intuitif !

148
11 Étiquettes de variables

11.1 Principe

Les étiquettes de variable permettent de donner un nom long,


plus explicite, aux différentes colonnes d’un tableau de don-
nées (ou encore directement à un vecteur autonome). Dans le
champs des grandes enquêtes, il est fréquent de nommer les
variables q101, q102, etc. pour refléter le numéro de la ques-
tion et d’indiquer ce qu’elle représente (groupe d’âges, milieu
de résidence…) avec une étiquette.
Un usage, introduit par le package {haven}, et repris depuis
par de nombreux autres packages dont {gtsummary} que nous
aborderons dans de prochains chapitres, consiste à stocker les
étiquettes de variables sous la forme d’un attribut17 "label" 17
Pour plus d’information sur les at-
attaché au vecteur / à la colonne du tableau. tributs, voir Chapitre 6.

Le package {labelled} permet de manipuler aisément ces éti-


quettes de variables.
La visionneuse de données de RStudio sait reconnaître et
afficher ces étiquettes de variable lorsqu’elles existent. Prenons
pour exemple le jeu de données gtsummary::trial dont
les colonnes ont des étiquettes de variable. La commande
View(gtsummary::trial) permet d’ouvrir la visionneuse de
données de RStudio. Comme on peut le constater, une éti-
quette de variable est bien présente sous le nom des différentes
colonnes.
La fonction labelled::look_for() du package {labelled}
permet de lister l’ensemble des variables d’un tableau de don-
nées et affiche notamment les étiquettes de variable associées.

149
Figure 11.1: Présentation du tableau gtsummary::trial dans
la visionneuse de RStudio

library(labelled)
gtsummary::trial |>
look_for()

pos variable label col_type missing values


1 trt Chemotherapy Treatment chr 0
2 age Age dbl 11
3 marker Marker Level (ng/mL) dbl 10
4 stage T Stage fct 0 T1
T2
T3
T4
5 grade Grade fct 0 I
II
III
6 response Tumor Response int 7
7 death Patient Died int 0
8 ttdeath Months to Death/Censor dbl 0

La fonction labelled::look_for() permet également de re-


chercher des variables en tenant compte à la fois de leur nom
et de leur étiquette.

gtsummary::trial |>
look_for("months")

150
pos variable label col_type missing values
8 ttdeath Months to Death/Censor dbl 0

Ď Astuce

Comme on le voit, la fonction labelled::look_for()


est tout à fait adaptée pour générer un dictionnaire
de codification. Ses différentes options sont détaillées
dans une vignette dédiée. Les résultats renvoyés par
labelled::look_for() sont récupérables dans un ta-
bleau de données que l’on pourra ainsi manipuler à sa
guise.

gtsummary::trial |>
look_for() |>
dplyr::as_tibble()

# A tibble: 8 x 7
pos variable label col_type missing levels value_labels
<int> <chr> <chr> <chr> <int> <named li> <named list>
1 1 trt Chemotherapy Treatment chr 0 <NULL> <NULL>
2 2 age Age dbl 11 <NULL> <NULL>
3 3 marker Marker Level (ng/mL) dbl 10 <NULL> <NULL>
4 4 stage T Stage fct 0 <chr [4]> <NULL>
5 5 grade Grade fct 0 <chr [3]> <NULL>
6 6 response Tumor Response int 7 <NULL> <NULL>
7 7 death Patient Died int 0 <NULL> <NULL>
8 8 ttdeath Months to Death/Censor dbl 0 <NULL> <NULL>

11.2 Manipulation sur un vecteur / une


colonne

La fonction labelled::var_label() permets de voir


l’étiquette de variable attachée à un vecteur (renvoie NULL
s’il n’y en a pas) mais également d’ajouter/modifier une
étiquette.
Le fait d’ajouter une étiquette de variable à un vecteur ne modi-
fie en rien son type ni sa classe. On peut associer une étiquette

151
de variable à n’importe quel type de variable, qu’elle soit nu-
mérique, textuelle, un facteur ou encore des dates.

v <- c(1, 5, 2, 4, 1)
v |> var_label()

NULL

var_label(v) <- "Mon étiquette"


var_label(v)

[1] "Mon étiquette"

str(v)

num [1:5] 1 5 2 4 1
- attr(*, "label")= chr "Mon étiquette"

var_label(v) <- "Une autre étiquette"


var_label(v)

[1] "Une autre étiquette"

str(v)

num [1:5] 1 5 2 4 1
- attr(*, "label")= chr "Une autre étiquette"

Pour supprimer une étiquette, il suffit d’attribuer la valeur


NULL.

var_label(v) <- NULL


str(v)

152
num [1:5] 1 5 2 4 1

On peut appliquer labelled::var_label() directement sur


une colonne de tableau.

var_label(iris$[Link]) <- "Longueur du pétale"


var_label(iris$[Link]) <- "Largeur du pétale"
var_label(iris$Species) <- "Espèce"
iris |>
look_for()

pos variable label col_type missing values


1 [Link] — dbl 0
2 [Link] — dbl 0
3 [Link] Longueur du pétale dbl 0
4 [Link] Largeur du pétale dbl 0
5 Species Espèce fct 0 setosa
versicolor
virginica

11.3 Manipulation sur un tableau de données

La fonction labelled::set_variable_labels() permets de


manipuler les étiquettes de variable d’un tableau de données
avec une syntaxe du type {dplyr}.

iris <-
iris |>
set_variable_labels(
Species = NULL,
[Link] = "Longeur du sépale"
)
iris |>
look_for()

pos variable label col_type missing values


1 [Link] Longeur du sépale dbl 0
2 [Link] — dbl 0

153
3 [Link] Longueur du pétale dbl 0
4 [Link] Largeur du pétale dbl 0
5 Species — fct 0 setosa
versicolor
virginica

11.4 Préserver les étiquettes

Certaines fonctions de R ne préservent pas les attributs et


risquent donc d’effacer les étiquettes de variables que l’on a
définit. Un exemple est la fonction générique subset() qui per-
met de sélectionner certaines lignes remplissant une certaines
conditions.

iris |>
look_for()

pos variable label col_type missing values


1 [Link] Longeur du sépale dbl 0
2 [Link] — dbl 0
3 [Link] Longueur du pétale dbl 0
4 [Link] Largeur du pétale dbl 0
5 Species — fct 0 setosa
versicolor
virginica

iris |>
subset(Species == "setosa") |>
look_for()

pos variable label col_type missing values


1 [Link] — dbl 0
2 [Link] — dbl 0
3 [Link] — dbl 0
4 [Link] — dbl 0
5 Species — fct 0 setosa
versicolor
virginica

154
On pourra, dans ce cas précis, préférer la fonction
dplyr::filter() qui préserve les attributs et donc les
étiquettes de variables.

iris |>
dplyr::filter(Species == "setosa") |>
look_for()

pos variable label col_type missing values


1 [Link] Longeur du sépale dbl 0
2 [Link] — dbl 0
3 [Link] Longueur du pétale dbl 0
4 [Link] Largeur du pétale dbl 0
5 Species — fct 0 setosa
versicolor
virginica

On pourra également tirer parti de la fonction labelled::copy_labels_from()


qui permet de copier les étiquettes d’un tableau à un autre.

iris |>
subset(Species == "setosa") |>
copy_labels_from(iris) |>
look_for()

pos variable label col_type missing values


1 [Link] Longeur du sépale dbl 0
2 [Link] — dbl 0
3 [Link] Longueur du pétale dbl 0
4 [Link] Largeur du pétale dbl 0
5 Species — fct 0 setosa
versicolor
virginica

155
12 Étiquettes de valeurs

Dans le domaine des grandes enquêtes, il est fréquent de coder


les variables catégorielles avec des codes numériques auxquels
on associé une certaines valeurs. Par exemple, une variable mi-
lieu de résidence pourrait être codée 1 pour urbain, 2 pour
semi-urbain, 3 pour rural et 9 pour indiquer une donnée man-
quante. Une variable binaire pourrait quant à elle être codée
0 pour non et 1 pour oui. Souvent, chaque enquête définit ses
propres conventions.
Les logiciels statistiques propriétaires SPSS, Stata et SAS ont
tous les trois un système d’étiquettes de valeurs pour représen-
ter ce type de variables catégorielles.
R n’a pas, de manière native, de système d’étiquettes de valeurs.
Le format utilisé en interne pour représenter les variables ca-
tégorielles est celui des facteurs (cf. Chapitre 9). Cependant,
ce dernier ne permet de contrôler comment sont associées une
étiquette avec une valeur numérique précise.

12.1 La classe haven_labelled

Afin d’assurer une importation complète des données depuis


SPSS, Stata et SAS, le package {haven} a introduit un nou-
veau type de vecteurs, la classe haven_labelled, qui permet
justement de rendre compte de ces vecteurs labellisés (i.e. avec
des étiquettes de valeurs). Le package {labelled} fournie un
jeu de fonctions pour faciliter la manipulation des vecteurs la-
bellisés.

156
ĺ Important

Les vecteurs labellisés sont un format intermédiaire qui


permets d’importer les données telles qu’elles ont été défi-
nies dans le fichier source. Il n’est pas destiné à être utilisé
pour l’analyse statistique.
Pour la réalisation de tableaux, graphiques, modèles, R at-
tend que les variables catégorielles soit codées sous formes
de facteurs, et que les variables continues soient numé-
riques. On aura donc besoin, à un moment ou à un autre,
de convertir les vecteurs labellisés en facteurs ou en va-
riables numériques classiques.

12.2 Manipulation sur un vecteur / une


colonne

Pour définir des étiquettes, la fonction de base est


labelled::val_labels(). Il est possible de définir des
étiquettes de valeurs pour des vecteurs numériques, d’entiers et
textuels. On indiquera les étiquettes sous la forme étiquette
= valeur. Cette fonction s’utilise de la même manière que
labelled::var_label() abordée au chapitre précédent
(cf. Chapitre 11). Un appel simple renvoie les étiquettes de
valeur associées au vecteur, NULL s’il n’y en n’a pas. Combiner
avec l’opérateur d’assignation (<-), on peut ajouter/modifier
les étiquettes de valeurs associées au vecteur.

library(labelled)
v <- c(1, 2, 1, 9)
v

[1] 1 2 1 9

class(v)

[1] "numeric"

157
val_labels(v)

NULL

val_labels(v) <- c(non = 1, oui = 2)


val_labels(v)

non oui
1 2

<labelled<double>[4]>
[1] 1 2 1 9

Labels:
value label
1 non
2 oui

class(v)

[1] "haven_labelled" "vctrs_vctr" "double"

Comme on peut le voir avec cet exemple simple :

• l’ajout d’étiquettes de valeurs modifie la classe de


l’objet (qui est maintenant un vecteur de la classe
haven_labelled) ;
• l’objet obtenu est multi-classes, la classe double
indiquant ici qu’il s’agit d’un vecteur numérique ;
• il n’est pas obligatoire d’associer une étiquette de valeurs
à toutes les valeurs observées dans le vecteur (ici, nous
n’avons pas défini d’étiquettes pour la valeur 9).

La fonction labelled::val_label() (notez l’absence d’un s à


la fin du nom de la fonction) permet d’accéder / de modifier
l’étiquette associée à une valeur spécifique.

158
val_label(v, 1)

[1] "non"

val_label(v, 9)

NULL

val_label(v, 9) <- "(manquant)"


val_label(v, 2) <- NULL
v

<labelled<double>[4]>
[1] 1 2 1 9

Labels:
value label
1 non
9 (manquant)

Pour supprimer, toutes les étiquettes de valeurs, on attribuera


NULL avec labelled::val_labels().

val_labels(v) <- NULL


v

[1] 1 2 1 9

class(v)

[1] "numeric"

On remarquera que, lorsque toutes les étiquettes de valeurs sont


supprimées, la nature de l’objet change à nouveau et il redevient
un simple vecteur numérique.

159
¾ Mise en garde

Il est essentiel de bien comprendre que l’ajout d’étiquettes


de valeurs ne change pas fondamentalement la nature du
vecteur. Cela ne le transforme pas en variable caté-
gorielle. À ce stade, le vecteur n’a pas été transformé en
facteur. Cela reste un vecteur numérique qui est considéré
comme tel par R. On peut ainsi en calculer une moyenne,
ce qui serait impossible avec un facteur.

v <- c(1, 2, 1, 2)
val_labels(v) <- c(non = 1, oui = 2)
mean(v)

[1] 1.5

f <- factor(v, levels = c(1, 2), labels = c("non", "oui"))


mean(f)

Warning in [Link](f): l'argument n'est ni numérique, ni logique : renvoi


de NA

[1] NA

Les fonctions labelled::val_labels() et labelled::val_label()


peuvent également être utilisées sur les colonnes d’un ta-
bleau.

df <- dplyr::tibble(
x = c(1, 2, 1, 2),
y = c(3, 9, 9, 3)
)
val_labels(df$x) <- c(non = 1, oui = 2)
val_label(df$y, 9) <- "(manquant)"
df

# A tibble: 4 x 2
x y
<dbl+lbl> <dbl+lbl>
1 1 [non] 3

160
2 2 [oui] 9 [(manquant)]
3 1 [non] 9 [(manquant)]
4 2 [oui] 3

On pourra noter, que si notre tableau est un tibble, les étiquettes


sont rendues dans la console quand on affiche le tableau.
La fonction labelled::look_for() est également un bon
moyen d’afficher les étiquettes de valeurs.

df |>
look_for()

pos variable label col_type missing values


1 x — dbl+lbl 0 [1] non
[2] oui
2 y — dbl+lbl 0 [9] (manquant)

12.3 Manipulation sur un tableau de données

{labelled} fournie 3 fonctions directement applicables sur


un tableau de données : labelled::set_value_labels(),
labelled::add_value_labels() et labelled::remove_value_labels().
La première remplace l’ensemble des étiquettes de valeurs
associées à une variable, la seconde ajoute des étiquettes de
valeurs (et conserve celles déjà définies), la troisième supprime
les étiquettes associées à certaines valeurs spécifiques (et laisse
les autres inchangées).

df |>
look_for()

pos variable label col_type missing values


1 x — dbl+lbl 0 [1] non
[2] oui
2 y — dbl+lbl 0 [9] (manquant)

161
df <- df |>
set_value_labels(
x = c(yes = 2),
y = c("a répondu" = 3, "refus de répondre" = 9)
)
df |>
look_for()

pos variable label col_type missing values


1 x — dbl+lbl 0 [2] yes
2 y — dbl+lbl 0 [3] a répondu
[9] refus de répondre

df <- df |>
add_value_labels(
x = c(no = 1)
) |>
remove_value_labels(
y = 9
)
df |>
look_for()

pos variable label col_type missing values


1 x — dbl+lbl 0 [2] yes
[1] no
2 y — dbl+lbl 0 [3] a répondu

12.4 Conversion

12.4.1 Quand convertir les vecteurs labellisés ?

La classe haven_labelled permets d’ajouter des métadonnées


aux variables sous la forme d’étiquettes de valeurs. Lorsque
les données sont importées depuis SAS, SPSS ou Stata, cela
permet notamment de conserver le codage original du fichier
importé.

162
Mais il faut noter que ces étiquettes de valeur n’indique pas
pour autant de manière systématique le type de variable (ca-
tégorielle ou continue). Les vecteurs labellisés n’ont donc pas
vocation à être utilisés pour l’analyse, notamment le calcul de
modèles statistiques. Ils doivent être convertis en facteurs (pour
les variables catégorielles) ou en vecteurs numériques (pour les
variables continues).
La question qui peut se poser est donc de choisir à quel moment
cette conversion doit avoir lieu dans un processus d’analyse. On
peut considérer deux approches principales.

Figure 12.1: Deux approches possibles pour la conversion des


étiquettes de valeurs

Dans l’approche A, les vecteurs labellisés sont convertis


juste après l’import des données, en utilisant les fonctions
labelled::unlabelled(), labelled::to_factor() ou
base::unclass() qui sont présentées ci-après. Dès lors, toute
la partie de nettoyage et de recodage des données se fera en
utilisant les fonctions classiques de R. Si l’on n’a pas besoin
de conserver le codage original, cette approche a l’avantage de
s’inscrire dans le fonctionnement usuel de R.
Dans l’approche B, les vecteurs labellisés sont conservés
pour l’étape de nettoyage et de recodage des données. Dans
ce cas là, on pourra avoir recours aux fonctions de l’extension
{labelled} qui facilitent la gestion des données labellisées.
Cette approche est particulièrement intéressante quand (i) on
veut pouvoir se référer au dictionnaire de codification fourni
avec les données sources et donc on veut conserver le codage
original et/ou (ii) quand les données devront faire l’objet
d’un ré-export après transformation. Par contre, comme dans

163
l’approche A, il faudra prévoir une conversion des variables
labellisées au moment de l’analyse.

Á Avertissement

Dans tous les cas, il est recommandé d’adopter l’une ou


l’autre approche, mais d’éviter de mélanger les différents
types de vecteur. Une organisation rigoureuse de ses don-
nées et de son code est essentielle !

12.4.2 Convertir un vecteur labellisé en facteur

Il est très facile de convertir un vecteur labellisé en facteur


à l’aide la fonction labelled::to_factor() du package
{labelled}18 . 18
On privilégiera la fonction
labelled::to_factor() à la
fonction haven::as_factor() de
v <- c(1,2,9,3,3,2,NA)
l’extension {haven}, la première
val_labels(v) <- c( ayant plus de possibilités et un
oui = 1, "peut-être" = 2, comportement plus consistent.
non = 3, "ne sait pas" = 9
)
v

<labelled<double>[7]>
[1] 1 2 9 3 3 2 NA

Labels:
value label
1 oui
2 peut-être
3 non
9 ne sait pas

to_factor(v)

[1] oui peut-être ne sait pas non non peut-être


[7] <NA>
Levels: oui peut-être non ne sait pas

164
Il possible d’indiquer si l’on souhaite, comme étiquettes du fac-
teur, utiliser les étiquettes de valeur (par défaut), les valeurs
elles-mêmes, ou bien les étiquettes de valeurs préfixées par la
valeur d’origine indiquée entre crochets.

to_factor(v, 'l')

[1] oui peut-être ne sait pas non non peut-être


[7] <NA>
Levels: oui peut-être non ne sait pas

to_factor(v, 'v')

[1] 1 2 9 3 3 2 <NA>
Levels: 1 2 3 9

to_factor(v, 'p')

[1] [1] oui [2] peut-être [9] ne sait pas [3] non
[5] [3] non [2] peut-être <NA>
Levels: [1] oui [2] peut-être [3] non [9] ne sait pas

Par défaut, les modalités du facteur seront triées selon l’ordre


des étiquettes de valeur. Mais cela peut être modifié avec
l’argument sort_levels si l’on préfère trier selon les valeurs
ou selon l’ordre alphabétique des étiquettes.

to_factor(v, sort_levels = 'v')

[1] oui peut-être ne sait pas non non peut-être


[7] <NA>
Levels: oui peut-être non ne sait pas

to_factor(v, sort_levels = 'l')

[1] oui peut-être ne sait pas non non peut-être


[7] <NA>
Levels: ne sait pas non oui peut-être

165
12.4.3 Convertir un vecteur labellisé en numérique ou
en texte

Pour rappel, il existe deux types de vecteurs labellisés : des


vecteurs numériques labellisés (x dans l’exemple ci-dessous) et
des vecteurs textuels labellisés (y dans l’exemple ci-dessous).

x <- c(1, 2, 9, 3, 3, 2, NA)


val_labels(x) <- c(
oui = 1, "peut-être" = 2,
non = 3, "ne sait pas" = 9
)

y <- c("f", "f", "h", "f")


val_labels(y) <- c(femme = "f", homme = "h")

Pour leur retirer leur caractère labellisé et revenir à leur classe


d’origine, on peut utiliser la fonction unclass().

unclass(x)

[1] 1 2 9 3 3 2 NA
attr(,"labels")
oui peut-être non ne sait pas
1 2 3 9

unclass(y)

[1] "f" "f" "h" "f"


attr(,"labels")
femme homme
"f" "h"

À noter que dans ce cas-là, les étiquettes sont conservées comme


attributs du vecteur.

166
Une alternative est d’utiliser labelled::remove_labels() qui
supprimera toutes les étiquettes, y compris les étiquettes de va-
riable. Pour conserver les étiquettes de variables et ne suppri-
mer que les étiquettes de valeurs, on indiquera keep_var_label
= TRUE.

var_label(x) <- "Etiquette de variable"


remove_labels(x)

[1] 1 2 9 3 3 2 NA

remove_labels(x, keep_var_label = TRUE)

[1] 1 2 9 3 3 2 NA
attr(,"label")
[1] "Etiquette de variable"

remove_labels(y)

[1] "f" "f" "h" "f"

Dans le cas d’un vecteur numérique labellisé que l’on sou-


haiterait convertir en variable textuelle, on pourra utiliser
labelled::to_character() à la place de labelled::to_factor()
qui, comme sa grande sœur, utilisera les étiquettes de valeurs.

to_character(x)

[1] "oui" "peut-être" "ne sait pas" "non" "non"


[6] "peut-être" NA
attr(,"label")
[1] "Etiquette de variable"

167
12.4.4 Conversion conditionnelle en facteurs

Il n’est pas toujours possible de déterminer la nature d’une


variable (continue ou catégorielle) juste à partir de la présence
ou l’absence d’étiquettes de valeur. En effet, on peut utiliser
des étiquettes de valeur dans le cadre d’une variable continue
pour indiquer certaines valeurs spécifiques.
Une bonne pratique est de vérifier chaque variable inclue dans
une analyse, une à une.
Cependant, une règle qui fonctionne dans 90% des cas est
de convertir un vecteur labellisé en facteur si et seulement si
toutes les valeurs observées dans le vecteur disposent d’une
étiquette de valeur correspondante. C’est ce que propose la
fonction labelled::unlabelled() qui peut même être appli-
qué à tout un tableau de données. Par défaut, elle fonctionne
ainsi :

1. les variables non labellisées restent inchangées (variables


f et g dans l’exemple ci-dessous);
2. si toutes les valeurs observées d’une variable labellisées
ont une étiquette, elles sont converties en facteurs (va-
riables b et c);
3. sinon, on leur applique base::unclass() (variables a, d
et e).

df <- dplyr::tibble(
a = c(1, 1, 2, 3),
b = c(1, 1, 2, 3),
c = c(1, 1, 2, 2),
d = c("a", "a", "b", "c"),
e = c(1, 9, 1, 2),
f = 1:4,
g = [Link](c(
"2020-01-01", "2020-02-01",
"2020-03-01", "2020-04-01"
))
) |>
set_value_labels(
a = c(No = 1, Yes = 2),

168
b = c(No = 1, Yes = 2, DK = 3),
c = c(No = 1, Yes = 2, DK = 3),
d = c(No = "a", Yes = "b"),
e = c(No = 1, Yes = 2)
)
df |> look_for()

pos variable label col_type missing values


1 a — dbl+lbl 0 [1] No
[2] Yes
2 b — dbl+lbl 0 [1] No
[2] Yes
[3] DK
3 c — dbl+lbl 0 [1] No
[2] Yes
[3] DK
4 d — chr+lbl 0 [a] No
[b] Yes
5 e — dbl+lbl 0 [1] No
[2] Yes
6 f — int 0
7 g — date 0

to_factor(df) |> look_for()

pos variable label col_type missing values


1 a — fct 0 No
Yes
3
2 b — fct 0 No
Yes
DK
3 c — fct 0 No
Yes
DK
4 d — fct 0 No
Yes
c

169
5 e — fct 0 No
Yes
9
6 f — int 0
7 g — date 0

unlabelled(df) |> look_for()

pos variable label col_type missing values


1 a — dbl 0
2 b — fct 0 No
Yes
DK
3 c — fct 0 No
Yes
DK
4 d — chr 0
5 e — dbl 0
6 f — int 0
7 g — date 0

On peut indiquer certaines options, par exemple drop_unused_labels


= TRUE pour supprimer des facteurs créés les niveaux non
observées dans les données (voir la variable c).

unlabelled(df, drop_unused_labels = TRUE) |>


look_for()

pos variable label col_type missing values


1 a — dbl 0
2 b — fct 0 No
Yes
DK
3 c — fct 0 No
Yes
4 d — chr 0
5 e — dbl 0
6 f — int 0
7 g — date 0

170
unlabelled(df, levels = "prefixed") |>
look_for()

pos variable label col_type missing values


1 a — dbl 0
2 b — fct 0 [1] No
[2] Yes
[3] DK
3 c — fct 0 [1] No
[2] Yes
[3] DK
4 d — chr 0
5 e — dbl 0
6 f — int 0
7 g — date 0

171
13 Valeurs manquantes

Dans R base, les valeurs manquantes sont indiquées par la va-


leurs logiques NA que l’on peut utiliser dans tous types de vec-
teurs.
Dans certains cas, par exemple dans la fonction dplyr::if_else()
qui vérifie que les deux options sont du même type, on peut
avoir besoin de spécifier une valeur manquante d’un certains
types précis (numérique, entier, textuel…) ce que l’on peut
faire avec les constantes NA_real_, NA_integer_ ou encore
NA_character_.
De base, il n’existe qu’un seul type de valeurs manquantes dans
R. D’autres logiciels statistiques ont mis en place des systèmes
pour distinguer plusieurs types de valeurs manquantes, ce qui
peut avoir une importance dans le domaine des grandes en-
quêtes, par exemple pour distinguer des ne sait pas d’un refus
de répondre ou d’un oubli enquêteur.
Ainsi, Stata et SAS ont un système de valeurs manquantes
étiquetées ou tagged NAs, où les valeurs manquantes peuvent
recevoir une étiquette (une lettre entre a et z). De son côté,
SPSS permet d’indiquer, sous la forme de métadonnées, que
certaines valeurs devraient être traitées comme des valeurs man-
quantes (par exemple que la valeur 8 correspond à des refus et
que la valeur 9 correspond à des ne sait pas). Il s’agit alors de
valeurs manquantes définies par l’utilisateur ou user NAs.
Dans tous les cas, il appartient à l’analyste de décider au
cas par cas comment ces valeurs manquantes doivent être
traitées. Dans le cadre d’une variable numérique, il est es-
sentiel d’exclure ces valeurs manquantes pour le calcul de
statistiques telles que la moyenne ou l’écart-type. Pour des
variables catégorielles, les pourcentages peuvent être calculées
sur l’ensemble de l’échantillon (les valeurs manquantes étant
alors traitées comme des modalités à part entière) ou bien

172
uniquement sur les réponses valides, en fonction du besoin de
l’analyse et de ce que l’on cherche à montrer.
Afin d’éviter toute perte d’informations lors d’un import de
données depuis Stata, SAS et SPSS, le package {haven} pro-
pose une implémentation sous R des tagged NAs et des user
NAs. Le package {labelled} fournit quant à lui différentes
fonctions pour les manipuler aisément.

library(labelled)

13.1 Valeurs manquantes étiquetées (tagged


NAs)

13.1.1 Création et test

Les tagged NAs sont de véritables valeurs manquantes (NA) au


sens de R, auxquelles a été attachées sur étiquette, une lettre
unique minuscule (a-z) ou majuscule (A-Z). On peut les créer
avec labelled::tagged_na().

x <- c(1:3, tagged_na("a"), tagged_na("z"), NA)

Pour la plupart des fonctions de R, les tagged NAs sont juste


considérées comme des valeurs manquantes régulières (regu-
lar NAs). Dès lors, par défaut, elles sont justes affichées à
l’écran comme n’importe quelle valeur manquante et la fonc-
tion [Link]() renvoie TRUE.

[1] 1 2 3 NA NA NA

[Link](x)

[1] FALSE FALSE FALSE TRUE TRUE TRUE

173
Pour afficher les étiquettes associées à ces valeurs man-
quantes, il faut avoir recours à labelled::na_tag(),
labelled::print_tagged_na() ou encore labelled::format_tagged_na().

na_tag(x)

[1] NA NA NA "a" "z" NA

print_tagged_na(x)

[1] 1 2 3 NA(a) NA(z) NA

format_tagged_na(x)

[1] " 1" " 2" " 3" "NA(a)" "NA(z)" " NA"

Pour tester si une certaine valeur manquante est une regular NA


ou une tagged NA, on aura recours à labelled::is_regular_na()
et à labelled::is_tagged_na().

[Link](x)

[1] FALSE FALSE FALSE TRUE TRUE TRUE

is_regular_na(x)

[1] FALSE FALSE FALSE FALSE FALSE TRUE

is_tagged_na(x)

[1] FALSE FALSE FALSE TRUE TRUE FALSE

Il est possible de tester une étiquette particulière en passant un


deuxième argument à labelled::is_tagged_na().

174
is_tagged_na(x, "a")

[1] FALSE FALSE FALSE TRUE FALSE FALSE

Ĺ Note

Il n’est possible de définir des tagged NAs seulement pour


des vecteurs numériques (double). Si l’on ajoute une tag-
ged NA à un vecteur d’entiers, ce vecteur sera converti en
vecteur numérique. Si on l’ajoute à un vecteur textuel, la
valeur manquante sera convertie en regular NA.

y <- c("a", "b", tagged_na("z"))


y

[1] "a" "b" NA

is_tagged_na(y)

[1] FALSE FALSE FALSE

format_tagged_na(y)

Error: `x` must be a double vector

z <- c(1L, 2L, tagged_na("a"))


typeof(z)

[1] "double"

format_tagged_na(z)

[1] " 1" " 2" "NA(a)"

175
13.1.2 Valeurs uniques, doublons et tris

Par défaut, les fonctions classiques de R unique(),


duplicated(), ordered() ou encore sort() traiteront
les tagged NAs comme des valeurs manquantes tout ce qu’il y
a de plus classique, et ne feront pas de différences entre des
tagged NAs ayant des étiquettes différentes.
Pour traiter des tagged NAs ayant des étiquettes différentes
comme des valeurs différentes, on aura recours aux fonctions
labelled::unique_tagged_na(), labelled::duplicated_tagged_na(),
labelled::order_tagged_na() ou encore labelled::sort_tagged_na().

x <- c(1, 2, tagged_na("a"), 1, tagged_na("z"), 2, tagged_na("a"), NA)


x |>
print_tagged_na()

[1] 1 2 NA(a) 1 NA(z) 2 NA(a) NA

x |>
unique() |>
print_tagged_na()

[1] 1 2 NA(a)

x |>
unique_tagged_na() |>
print_tagged_na()

[1] 1 2 NA(a) NA(z) NA

x |>
duplicated()

[1] FALSE FALSE FALSE TRUE TRUE TRUE TRUE TRUE

176
x |>
duplicated_tagged_na()

[1] FALSE FALSE FALSE TRUE FALSE TRUE TRUE FALSE

x |>
sort([Link] = TRUE) |>
print_tagged_na()

[1] 1 1 2 2 NA(a) NA(z) NA(a) NA

x |>
sort_tagged_na() |>
print_tagged_na()

[1] 1 1 2 2 NA(a) NA(a) NA(z) NA

13.1.3 Tagged NAs et étiquettes de valeurs

Il est tout à fait possible d’associer une étiquette de valeurs


(cf. Chapitre 12) à des tagged NAs.

x <- c(
1, 0,
1, tagged_na("r"),
0, tagged_na("d"),
tagged_na("z"), NA
)
val_labels(x) <- c(
no = 0,
yes = 1,
"don't know" = tagged_na("d"),
refusal = tagged_na("r")
)
x

177
<labelled<double>[8]>
[1] 1 0 1 NA(r) 0 NA(d) NA(z) NA

Labels:
value label
0 no
1 yes
NA(d) don't know
NA(r) refusal

Lorsqu’un vecteur labellisé est converti en facteur avec


labelled::to_factor(), les tagged NAs sont, par défaut
convertis en en valeurs manquantes classiques (regular NAs).
Il n’est pas possible de définir des tagged NAs pour des
facteurs.

x |> to_factor()

[1] yes no yes <NA> no <NA> <NA> <NA>


Levels: no yes

L’option explicit_tagged_na de labelled::to_factor()


permets de convertir les tagged NAs en modalités explicites du
facteur.

x |>
to_factor(explicit_tagged_na = TRUE)

[1] yes no yes refusal no don't know NA(z)


[8] <NA>
Levels: no yes don't know refusal NA(z)

x |>
to_factor(
levels = "prefixed",
explicit_tagged_na = TRUE
)

178
[1] [1] yes [0] no [1] yes [NA(r)] refusal
[5] [0] no [NA(d)] don't know [NA(z)] NA(z) <NA>
Levels: [0] no [1] yes [NA(d)] don't know [NA(r)] refusal [NA(z)] NA(z)

13.1.4 Conversion en user NAs

La fonction labelled::tagged_na_to_user_na() permets de


convertir des tagged NAs en user NAs.

x |>
tagged_na_to_user_na()

<labelled_spss<double>[8]>
[1] 1 0 1 3 0 2 4 NA
Missing range: [2, 4]

Labels:
value label
0 no
1 yes
2 don't know
3 refusal
4 NA(z)

x |>
tagged_na_to_user_na(user_na_start = 10)

<labelled_spss<double>[8]>
[1] 1 0 1 11 0 10 12 NA
Missing range: [10, 12]

Labels:
value label
0 no
1 yes
10 don't know
11 refusal
12 NA(z)

179
La fonction labelled::tagged_na_to_regular_na() conver-
tit les tagged NAs en valeurs manquantes classiques (regular
NAs).

x |>
tagged_na_to_regular_na()

<labelled<double>[8]>
[1] 1 0 1 NA 0 NA NA NA

Labels:
value label
0 no
1 yes

x |>
tagged_na_to_regular_na() |>
is_tagged_na()

[1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE

13.2 Valeurs manquantes définies par


l’utilisateurs (user NAs)

Le package {haven} a introduit la classe haven_labelled_spss,


une extension de la classe haven_labelled permettant
d’indiquer des valeurs à considérer comme manquantes à la
manière de SPSS.

ĺ Important

Cela revient à associer à un vecteur des attributs


(cf. Chapitre 6) additionnels pour indiquer des valeurs
que l’utilisateur pourrait/devrait considérer comme man-
quante. Cependant, il ne s’agit que de métadonnées et en
interne ces valeurs ne sont pas stockées sous forme de NA
mais restent des valeurs valides.

180
Il convient de garder en mémoire que la très grande ma-
jorité des fonctions de R ne prendront pas en compte ces
métadonnées et traiteront donc ces valeurs comme des va-
leurs valides. C’est donc à l’utilisateur de convertir, au
besoin, ces les valeurs indiquées comme manquantes en
réelles valeurs manquantes (NA).

13.2.1 Création

Il est possible d’indiquer des valeurs à considérer comme man-


quantes (user NAs) de deux manières :

• soit en indiquant une liste de valeurs individuelles avec


labelled::na_values() (on peut indiquer NULL pour
supprimer les déclarations existantes) ;
• soit en indiquant deux valeurs représentant une plage
de valeurs à considérées comme manquantes avec
labelled::na_range() (seront considérées comme man-
quantes toutes les valeurs supérieures ou égale au premier
chiffre et inférieures ou égales au second chiffre19 ). 19
On peut utiler -Inf et Inf qui
représentent respectivement moins
l’infini et l’infini.
v <- c(1, 2, 3, 9, 1, 3, 2, NA)
val_labels(v) <- c(
faible = 1,
fort = 3,
"ne sait pas" = 9
)
na_values(v) <- 9
v

<labelled_spss<double>[8]>
[1] 1 2 3 9 1 3 2 NA
Missing values: 9

Labels:
value label
1 faible
3 fort
9 ne sait pas

181
na_values(v) <- NULL
v

<labelled<double>[8]>
[1] 1 2 3 9 1 3 2 NA

Labels:
value label
1 faible
3 fort
9 ne sait pas

na_range(v) <- c(5, Inf)


v

<labelled_spss<double>[8]>
[1] 1 2 3 9 1 3 2 NA
Missing range: [5, Inf]

Labels:
value label
1 faible
3 fort
9 ne sait pas

On peut noter que les user NAs peuvent cohabiter avec des
regular NAs ainsi qu’avec des étiquettes de valeurs (value labels,
cf. Chapitre 12).
Pour manipuler les variables d’un tableau de données, on peut
également avoir recours à labelled::set_na_values() et
labelled::set_na_range().

df <-
dplyr::tibble(
s1 = c("M", "M", "F", "F"),
s2 = c(1, 1, 2, 9)
) |>

182
set_na_values(s2 = 9)
df$s2

<labelled_spss<double>[4]>
[1] 1 1 2 9
Missing values: 9

df <-
df |>
set_na_values(s2 = NULL)
df$s2

<labelled<double>[4]>
[1] 1 1 2 9

13.2.2 Tests

La fonction [Link]() est l’une des rares fonctions de base


R à reconnaître les user NAs et donc à renvoyer TRUE
dans ce cas. Pour des tests plus spécifiques, on aura recours à
labelled::is_user_na() et labelled::is_regular_na().

<labelled_spss<double>[8]>
[1] 1 2 3 9 1 3 2 NA
Missing range: [5, Inf]

Labels:
value label
1 faible
3 fort
9 ne sait pas

v |> [Link]()

[1] FALSE FALSE FALSE TRUE FALSE FALSE FALSE TRUE

183
v |> is_user_na()

[1] FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE

v |> is_regular_na()

[1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE

13.2.3 Conversion

Comme dit précédemment, pour la plupart des fonctions de R,


les users NAs sont toujours des valeurs valides.

x <- c(1:5, 11:15)


na_range(x) <- c(10, Inf)
x

<labelled_spss<integer>[10]>
[1] 1 2 3 4 5 11 12 13 14 15
Missing range: [10, Inf]

mean(x)

[1] 8

On aura alors recours à labelled::user_na_to_regular_na()


pour convertir les users NAs en véritables valeurs manquantes
avant de procéder à un calcul statistique.

x |>
user_na_to_na()

<labelled<integer>[10]>
[1] 1 2 3 4 5 NA NA NA NA NA

184
x |>
user_na_to_na() |>
mean([Link] = TRUE)

[1] 3

Une alternative consiste à transformer les user NAs en tagged


NAs avec labelled::user_na_to_tagged_na().

x |>
user_na_to_tagged_na() |>
print_tagged_na()

'x' has been converted into a double vector.

[1] 1 2 3 4 5 NA(a) NA(b) NA(c) NA(d) NA(e)

x |>
user_na_to_tagged_na() |>
mean([Link] = TRUE)

'x' has been converted into a double vector.

[1] 3

Pour supprimer les métadonnées relatives aux user NAs


sans les convertir en valeurs manquantes, on aura recours à
labelled::remove_user_na().

x |>
remove_user_na()

<labelled<integer>[10]>
[1] 1 2 3 4 5 11 12 13 14 15

185
x |>
remove_user_na() |>
mean()

[1] 8

Enfin, lorsque l’on convertit un vecteur labellisé en facteur


avec labelled::to_factor(), on pourra utiliser l’argument
user_na_to_na pour indiquer si les users NAs doivent être
convertis ou non en valeurs manquantes classiques (NA).

x <- c(1, 2, 9, 2)
val_labels(x) <- c(oui = 1, non = 2, refus = 9)
na_values(x) <- 9
x |>
to_factor(user_na_to_na = TRUE)

[1] oui non <NA> non


Levels: oui non

x |>
to_factor(user_na_to_na = FALSE)

[1] oui non refus non


Levels: oui non refus

186
14 Import & Export de
données

14.1 Importer un fichier texte

Les fichiers texte constituent un des formats les plus largement


supportés par la majorité des logiciels statistiques. Presque tous
permettent d’exporter des données dans un format texte, y com-
pris les tableurs comme Libre Office, Open Office ou Ex-
cel.
Cependant, il existe une grande variétés de format texte, qui
peuvent prendre différents noms selon les outils, tels que texte
tabulé ou texte (séparateur : tabulation), CSV (pour comma-
separated value, sachant que suivant les logiciels le séparateur
peut être une virgule ou un point-virgule).

14.1.1 Structure d’un fichier texte

Dès lors, avant d’importer un fichier texte dans R, il est in-


dispensable de regarder comment ce dernier est structuré. Il
importe de prendre note des éléments suivants :

• La première ligne contient-elle le nom des variables ?


• Quel est le caractère séparateur entre les différentes va-
riables (encore appelé séparateur de champs) ? Dans le
cadre d’un fichier CSV, il aurait pu s’agir d’une virgule
ou d’un point-virgule.
• Quel est le caractère utilisé pour indiquer les décimales
(le séparateur décimal) ? Il s’agit en général d’un point (à
l’anglo-saxonne) ou d’une virgule (à la française).
• Les valeurs textuelles sont-elles encadrées par des guille-
mets et, si oui, s’agit-il de guillemets simple (') ou de
guillemets doubles (") ?

187
• Pour les variables textuelles, y a-t-il des valeurs man-
quantes et si oui comment sont-elles indiquées ? Par
exemple, le texte NA est parfois utilisé.

Il ne faut pas hésitez à ouvrir le fichier avec un éditeur de texte


pour le regarder de plus près.

14.1.2 Interface graphique avec RStudio

RStudio fournit une interface graphique pour faciliter l’import


d’un fichier texte. Pour cela, il suffit d’aller dans le menu File
> Import Dataset et de choisir l’option From CSV 20 . Cette 20
L’option CSV fonctionne pour
option est également disponible via l’onglet Environment dans tous les fichiers de type texte, même
si votre fichier a une autre extension,
le quadrant haut-droite.
.txt par exemple
Pour la suite, nous allons utiliser ce fichier texte à titre
d’exemple.

Figure 14.1: Importer un fichier texte avec RStudio

L’interface de RStudio vous présente sous Import Options


les différentes options d’import disponible. La section Data
Preview vous permet de voir en temps réel comment les don-
nées sont importées. La section Code Preview vous indique le
code R correspondant à vos choix. Il n’y a plus qu’à le co-
pier/coller dans un de vos scripts ou à cliquer sur Import pour
l’exécuter.

188
Vous pourrez remarquer que RStudio fait appel à l’extension
{readr} du tidyverse pour l’import des données via la fonction
readr::read_csv().
{readr} essaie de deviner le type de chacune des colonnes, en
se basant sur les premières observations. En cliquant sur le nom
d’une colonne, il est possible de modifier le type de la variable
importée. Il est également possible d’exclure une colonne de
l’import (skip).

14.1.3 Dans un script

L’interface graphique de RStudio fournit le code d’import.


On peut également l’adapter à ces besoins en consultant la
page d’aide de readr::read_csv() pour plus de détails. Par
exemple :

library(readr)
d <- read_delim(
"[Link]
delim = "\t",
quote = "'"
)

On peut indiquer le chemin local vers un fichier (le plus courant)


ou bien directement l’URL d’un fichier sur Internet.
{readr} propose plusieurs fonctions proches : readr::read_delim(),
readr::read_csv(), readr::read_csv2() et readr::read_tsv().
Elles fonctionnent toutes de manière identique et ont les mêmes
arguments. Seule différence, les valeurs par défaut de certains
paramètres.

Ď Fichiers de très grande taille

Si vous travaillez sur des données de grandes dimensions,


les formats texte peuvent être lents à exporter et impor-
ter. Dans ce cas là, on pourra jeter un œil au package
{vroom} et/ou aux fonctions [Link]::fread() et
[Link]::fwrite().

189
Dans des manuels ou des exemples en ligne, vous trou-
verez parfois mention des fonctions utils::[Link](),
utils::[Link](), utils::read.csv2(), utils::[Link]()
ou encore utils::read.delim2(). Il s’agit des fonctions na-
tives et historiques de R (extension {utils}) dédiées à
l’import de fichiers textes. Elles sont similaires à celles de
{readr} dans l’idée générale mais diffèrent dans leurs détails
et les traitements effectués sur les données (pas de détection
des dates par exemple). Pour plus d’information, vous pouvez
vous référer à la page d’aide de ces fonctions.

14.2 Importer un fichier Excel

Une première approche pour importer des données Excel


dans R consiste à les exporter depuis Excel dans un fichier
texte (texte tabulé ou CSV) puis de suivre la procédure
d’importation d’un fichier texte.
Une feuille Excel peut également être importée directement
avec l’extension {readxl} du tidyverse.
La fonction readxl::read_excel() permet d’importer à la fois
des fichiers .xls (Excel 2003 et précédents) et .xlsx (Excel
2007 et suivants).

library(readxl)
donnees <- read_excel("data/[Link]")

Une seule feuille de calculs peut être importée à la fois. On


pourra préciser la feuille désirée avec sheet en indiquant soit
le nom de la feuille, soit sa position (première, seconde, …).

donnees <- read_excel("data/[Link]", sheet = 3)


donnees <- read_excel("data/[Link]", sheet = "mes_donnees")

On pourra préciser avec col_names si la première ligne contient


le nom des variables.
Par défaut, readxl::read_excel() va essayer de deviner le
type (numérique, textuelle, date) de chaque colonne. Au besoin,

190
on pourra indiquer le type souhaité de chaque colonne avec
col_types.
RStudio propose également pour les fichiers Excel un assis-
tant d’importation, similaire à celui pour les fichiers texte, per-
mettant de faciliter l’import.

14.3 Importer depuis des logiciels de


statistique

Le package {haven} du tidyverse a été développé spécifique-


ment pour permettre l’importation de données depuis les for-
mats des logiciels Stata, SAS et SPSS.
Il vise à offrir une importation unifiée depuis ces trois logi-
ciels (là où le package {foreign} distribué en standard avec R
adopte des conventions différentes selon le logiciel source).
Afin de ne pas perdre d’information lors de l’import, {haven}
a introduit la notion d’étiquettes de variables (cf. Chapitre 11),
une classe de vecteurs pour la gestion des étiquettes de valeurs
(cf. Chapitre 12), des mécanismes pour reproduire la gestion des
valeurs manquantes de ces trois logiciels (cf. Chapitre 13), mais
également une gestion et un import correct des dates, dates-
heures et des variables horaires (cf. le package {hms}).
À noter que RStudio intègre également une interface
graphique pour l’import des fichiers Stata, SAS et SPSS.

14.3.1 SPSS

Les fichiers générés par SPSS sont de deux types : les fichiers
SPSS natifs (extension .sav) et les fichiers au format SPSS
export (extension .por).
Dans les deux cas, on aura recours à la fonction haven::read_spss() :

library(haven)
donnees <- read_spss("data/[Link]", user_na = TRUE)

191
Ď Valeurs manquantes

Dans SPSS, il est possible de définir des valeurs à consi-


dérées comme manquantes ou user NAs, voir Chapitre 13.
Par défaut, haven::read_spss() convertir toutes ces va-
leurs en NA lors de l’import.
Or, il est parfois important de garder les diffé-
rentes valeurs originelles. Dans ce cas, on appellera
haven::read_spss() avec l’option user_na = TRUE.

14.3.2 SAS

Les fichiers SAS se présentent en général sous deux format :


format SAS export (extension .xport ou .xpt) ou format
SAS natif (extension .sas7bdat).
Les fichiers SAS natifs peuvent être importées directement
avec haven::read_sas() de l’extension {haven} :

library(haven)
donnees <- read_sas("data/fichier.sas7bdat")

Au besoin, on pourra préciser en deuxième argument le nom


d’un fichier SAS catalogue (extension .sas7bcat) contenant
les métadonnées du fichier de données.

library(haven)
donnees <- read_sas(
"data/fichier.sas7bdat",
catalog_file = "data/fichier.sas7bcat"
)

Ĺ Note

Les fichiers au format SAS export peuvent être impor-


tés via la fonction foreign::[Link]() de l’extension
{foreign}. Celle-ci s’utilise très simplement, en lui pas-
sant le nom du fichier en argument :

192
library(foreign)
donnees <- [Link]("data/[Link]")

14.3.3 Stata

Pour les fichiers Stata (extension .dta), on aura recours aux


fonctions haven::read_dta() et haven::read_stata() de
l’extension {haven}. Ces deux fonctions sont identiques.

library(haven)
donnees <- read_dta("data/[Link]")

ĺ Important

Gestion des valeurs manquantes


Dans Stata, il est possible de définir plusieurs types de
valeurs manquantes, qui sont notées sous la forme .a à .z.
Elles sont importées par {haven} sous formes de tagged
NAs, cf. Chapitre 13.

14.3.4 dBase

L’Insee et d’autres producteur de données diffusent leurs fi-


chiers au format dBase (extension .dbf). Ceux-ci sont direc-
tement lisibles dans R avec la fonction foreign::[Link]()
de l’extension {foreign}.

library(foreign)
donnees <- [Link]("data/[Link]")

14.4 Sauver ses données

R dispose également de son propre format pour sauvegarder


et échanger des données. On peut sauver n’importe quel objet
créé avec R et il est possible de sauver plusieurs objets dans

193
un même fichier. L’usage est d’utiliser l’extension .RData pour
les fichiers de données R. La fonction à utiliser s’appelle tout
simplement save().
Par exemple, si l’on souhaite sauvegarder son tableau de don-
nées d ainsi que les objets tailles et poids dans un fichier
[Link] :

save(d, tailles, poids, file = "[Link]")

À tout moment, il sera toujours possible de recharger ces don-


nées en mémoire à l’aide de la fonction load() :

load("[Link]")

¾ Mise en garde

Si entre temps vous aviez modifié votre tableau d, vos mo-


difications seront perdues. En effet, si lors du chargement
de données, un objet du même nom existe en mémoire, ce
dernier sera remplacé par l’objet importé.

La fonction [Link]() est un raccourci pour sauvegarder


tous les objets de la session de travail dans le fichier .RData (un
fichier un peu étrange car il n’a pas de nom mais juste une exten-
sion). Lors de la fermeture de RStudio, il vous sera demandé si
vous souhaitez enregistrer votre session. Si vous répondez Oui,
c’est cette fonction [Link]() qui sera appliquée.

[Link]()

Un autre mécanisme possible est le format RDS de R. La fonc-


tion saveRDS() permet de sauvegarder un et un seul objet R
dans un fichier.

saveRDS(d, file = "mes_donnees.rds")

Cet objet pourra ensuite être lu avec la fonction readRDS().


Mais au lieu d’être directement chargé dans la mémoire de
l’environnement de travail, l’objet lu sera retourné par la fonc-
tion readRDS() et ce sera à l’utilisateur de le sauvegarder.

194
donnees <- readRDS("mes_donnees.rds")

14.5 Export de tableaux de données

On peut avoir besoin d’exporter un tableau de données R vers


différents formats. La plupart des fonctions d’import disposent
d’un équivalent permettant l’export de données. On citera no-
tamment :

• readr::write_csv() et readr::write_tsv() per-


mettent d’exporter au format CSV et texte tabulé
respectivement, readr::write_delim() offrant de
multiples options pour l'export au format texte ;
• haven::write_sas() permet d’exporter au format
SAS ;
• haven::write_sav() au format SPSS ;
• haven::write_dta() au format Stata ;
• foreign::[Link]() au format dBase.

L’extension readxl ne fournit pas de fonction pour exporter


au format Excel. Par contre, on pourra passer par la fonction
openxlsx::[Link]() du package {openxlsx} ou la fonc-
tion xlsx::[Link]() de l’extension {xlsx}. L’intérêt de
{openxlsx} est de ne pas dépendre de Java à la différence de
{xlsx}.

195
15 Mettre en forme des
nombres

Dans les chapitres suivants, nous aurons régulièrement besoin,


pour produire des tableaux ou des figures propres, de fonctions
de formatage qui permettent de transformer des valeurs nu-
mériques en chaînes de texte.
La fonction R de base est format() mais de nombreux autres
packages proposent des variations pour faciliter cette opération.
Le plus complet est probablement {scales} et, notamment, ses
fonctions scales::label_number() et scales::number().
Elles ont l’air très similaires et partagent un grand
nombre de paramètres en commun. La différence est
que scales::number() a besoin d’un vecteur numérique
en entrée qu’elle va mettre en forme, tandis que que
scales::label_number() renvoie une fonction que l’on
pourra ensuite appliquer à un vecteur numérique.

library(scales)
x <- c(0.0023, .123, 4.567, 874.44, 8957845)
number(x)

[1] "0.00" "0.12" "4.57" "874.44" "8 957 845.00"

f <- label_number()
f(x)

[1] "0.00" "0.12" "4.57" "874.44" "8 957 845.00"

label_number()(x)

196
[1] "0.00" "0.12" "4.57" "874.44" "8 957 845.00"

Dans de nombreux cas de figure (par exemple pour un


graphique {ggplot2} ou un tableau {gtsummary}), il sera
demandé de fournir une fonction de formatage, auquel cas
on aura recours aux fonctions de {scales} préfixées par
label_*() qui permettent donc de générer une fonction
personnalisée.

15.1 label_number()

scales::label_number() est la fonction de base de mise en


forme de nombres dans {scales}, une majorité des autres fonc-
tions faisant appel à scales::label_number() et partageant
les mêmes arguments.
Le paramètre accurary permets de définir le niveau d’arrondi
à utiliser. Par exemple, .1 pour afficher une seule décimale.
Il est aussi possible d’indiquer un nombre qui n’est pas une
puissance de 10 (par exemple .25). Si on n’indique rien (NULL),
alors scales::label_number() essaiera de deviner un nombre
de décimales pertinent en fonction des valeurs du vecteur de
nombres à mettre en forme.

label_number(accuracy = NULL)(x)

[1] "0.00" "0.12" "4.57" "874.44" "8 957 845.00"

label_number(accuracy = .1)(x)

[1] "0.0" "0.1" "4.6" "874.4" "8 957 845.0"

label_number(accuracy = .25)(x)

[1] "0.0" "0.0" "4.5" "874.5" "8 957 845.0"

197
label_number(accuracy = 10)(x)

[1] "0" "0" "0" "870" "8 957 840"

L’option scale permets d’indiquer un facteur multiplicatif à


appliquer avant de mettre en forme. On utilisera le plus souvent
les options prefix et suffix en même temps pour indiquer les
unités.

label_number(scale = 100, suffix = "%")(x) # pour cent

[1] "0%" "12%" "457%" "87 444%" "895 784 500%"

label_number(scale = 1000, suffix = "\u2030")(x) # pour mille

[1] "2‰" "123‰" "4 567‰" "874 440‰"


[5] "8 957 845 000‰"

label_number(scale = .001, suffix = " milliers", accuracy = .1)(x)

[1] "0.0 milliers" "0.0 milliers" "0.0 milliers" "0.9 milliers"


[5] "8 957.8 milliers"

Les arguments [Link] et [Link] permettent de défi-


nir, respectivement, le séparateur de décimale et le séparateur
de milliers. Ainsi, pour afficher des nombres à la française (vir-
gule pour les décimales, espace pour les milliers) :

label_number([Link] = ",", [Link] = " ")(x)

[1] "0,00" "0,12" "4,57" "874,44" "8 957 845,00"

Note : il est possible d’utiliser [Link] et [Link]


pour ajouter des séparateurs parmi les décimales.

198
label_number(accuracy = 10^-9, [Link] = "|", [Link] = 3)(x)

[1] "0.002|300|000" "0.123|000|000" "4.567|000|000"


[4] "874.440|000|000" "8 957 845.000|000|000"

Les options style_positive et style_negative permettent


de personnaliser la manière dont les valeurs positives et néga-
tives sont mises en forme.

y <- c(-1.2, -0.3, 0, 2.4, 7.2)


label_number(style_positive = "plus")(y)

[1] "-1.2" "-0.3" "0.0" "+2.4" "+7.2"

label_number(style_negative = "parens")(y)

[1] "(1.2)" "(0.3)" "0.0" "2.4" "7.2"

L’option scale_cut permet d’utiliser, entre autres, les préfixes


du Système international d’unités les plus proches et arrondi
chaque valeur en fonction, en ajoutant la précision correspon-
dante. Par exemple, pour des données en grammes :

y <- c(.000004536, .01245, 2.3456, 47589.14, 789456244)


label_number(scale_cut = cut_si("g"), accuracy = .1)(y)

[1] "4.5 µg" "12.4 mg" "2.3 g" "47.6 kg" "789.5 Mg"

15.2 Les autres fonctions de {scales}

15.2.1 label_comma()

scales::label_comma() (et scales::comma()) est une


variante de scales::label_number() qui, par défaut, affiche
les nombres à l’américaine, avec une virgule comme séparateur
de milliers.

199
label_comma()(x)

[1] "0.00" "0.12" "4.57" "874.44" "8,957,845.00"

15.2.2 label_percent()

scales::label_percent() (et scales::percent()) est une


variante de scales::label_number() qui affiche les nombres
sous formes de pourcentages (les options par défaut sont scale
= 100, suffix = "%").

label_percent()(x)

[1] "0%" "12%" "457%" "87 444%" "895 784 500%"

On peut utiliser cette fonction pour afficher des résultats en


pour mille (le code Unicode du symbole ‰ étant u2030) :

label_percent(scale = 1000, suffix = "\u2030")(x)

[1] "2‰" "123‰" "4 567‰" "874 440‰"


[5] "8 957 845 000‰"

15.2.3 label_dollar()

scales::label_dollar() est adapté à l’affichage des valeurs


monétaires.

label_dollar()(x)

[1] "$0" "$0" "$5" "$874" "$8,957,845"

label_dollar(prefix = "", suffix = " €", accuracy = .01, [Link] = " ")(x)

200
[1] "0.00 €" "0.12 €" "4.57 €" "874.44 €"
[5] "8 957 845.00 €"

L’option style_negative permet d’afficher les valeurs néga-


tives avec des parenthèses, convention utilisée dans certaines
disciplines.

label_dollar()(c(12.5, -4, 21, -56.36))

[1] "$12.50" "-$4.00" "$21.00" "-$56.36"

label_dollar(style_negative = "parens")(c(12.5, -4, 21, -56.36))

[1] "$12.50" "($4.00)" "$21.00" "($56.36)"

15.2.4 label_pvalue()

scales::label_pvalue() est adapté pour la mise en forme de


p-valeurs.

label_pvalue()(c(0.000001, 0.023, 0.098, 0.60, 0.9998))

[1] "<0.001" "0.023" "0.098" "0.600" ">0.999"

label_pvalue(accuracy = .01, add_p = TRUE)(c(0.000001, 0.023, 0.098, 0.60))

[1] "p<0.01" "p=0.02" "p=0.10" "p=0.60"

15.2.5 label_scientific()

scales::label_scientific() affiche les nombres dans un for-


mat scientifique (avec des puissances de 10).

label_scientific(unit = "g")(c(.00000145, .0034, 5, 12478, 14569787))

[1] "1.45e-06" "3.40e-03" "5.00e+00" "1.25e+04" "1.46e+07"

201
15.2.6 label_bytes()

scales::label_bytes() mets en forme des tailles exprimées


en octets, utilisant au besoin des multiples de 1024.

b <- c(478, 1235468, 546578944897)


label_bytes()(b)

[1] "478 B" "1 MB" "547 GB"

label_bytes(units = "auto_binary")(b)

[1] "478 iB" "1 MiB" "509 GiB"

15.2.7 label_ordinal()

scales::label_ordinal() permets d’afficher des rangs ou


nombres ordinaux. Plusieurs langues sont disponibles.

label_ordinal()(1:5)

[1] "1st" "2nd" "3rd" "4th" "5th"

label_ordinal(rules = ordinal_french())(1:5)

[1] "1er" "2e" "3e" "4e" "5e"

label_ordinal(rules = ordinal_french(gender = "f", plural = TRUE))(1:5)

[1] "1res" "2es" "3es" "4es" "5es"

202
15.2.8 label_date(), label_date_short() &
label_time()

scales::label_date(), scales::label_date_short() et
scales::label_time() peuvent être utilisées pour la mise en
forme de dates.

label_date()([Link]("2020-02-14"))

[1] "2020-02-14"

label_date(format = "%d/%m/%Y")([Link]("2020-02-14"))

[1] "14/02/2020"

label_date_short()([Link]("2020-02-14"))

[1] "14\nfévr.\n2020"

La mise en forme des dates est un peu complexe. Ne pas hésiter


à consulter le fichier d’aide de la fonction base::strptime()
pour plus d’informations.

15.2.9 label_wrap()

La fonction scales::label_wrap() est un peu différente. Elle


permets d’insérer des retours à la ligne (\n) dans des chaines
de caractères. Elle tient compte des espaces pour identifier les
mots et éviter ainsi des coupures au milieu d’un mot.

x <- "Ceci est un texte assez long et que l'on souhaiterait afficher sur plusieurs lignes. C
label_wrap(80)(x)

[1] "Ceci est un texte assez long et que l'on souhaiterait afficher sur plusieurs\nlignes. Cepe

203
label_wrap(80)(x) |> message()

Ceci est un texte assez long et que l'on souhaiterait afficher sur plusieurs
lignes. Cependant, on souhaite éviter que des coupures apparaissent au milieu
d'un mot.

label_wrap(40)(x) |> message()

Ceci est un texte assez long et que


l'on souhaiterait afficher sur
plusieurs lignes. Cependant, on
souhaite éviter que des coupures
apparaissent au milieu d'un mot.

15.3 Les fonctions de formatage de


{gtsummary}

Véritable couteau-suisse du statisticien, le package {gtsummary}


sera largement utilisé dans les prochains chapitres pour pro-
duire des tableaux statistiques prêts à être publiés.
Ce package utilise par défaut ses propres fonctions de formatage
mais, au besoin, il sera toujours possible de lui transmettre des
fonctions de formatage créées avec {scales}.

15.3.1 style_number()

Fonction de base, gtsummary::style_number() accepte les pa-


ramètres [Link] (séparateur de milliers), [Link] (sé-
parateur de décimales) et scale (facteur d’échelle). Le nombre
de décimales se précisera quant à lui avec digits où l’on indi-
quera le nombre de décimales souhaité.

library(gtsummary)
x <- c(0.123, 0.9, 1.1234, 12.345, -0.123, -0.9, -1.1234, -132.345)
style_number(x, digits = 1)

204
[1] "0.1" "0.9" "1.1" "12.3" "-0.1" "-0.9" "-1.1" "-132.3"

Ď Astuce

Nous verrons dans le chapitre sur les statis-


tiques univariées (cf. Section 18.2.1) la fonction
gtsummary::theme_gtsummary_language() qui per-
met de fixer globalement le séparateur de milliers et celui
des décimales, afin de changer les valeurs par défaut de
l’ensemble des fonctions de formatage de {gtsummary}.
Il est important de noter que cela n’a aucun effet sur les
fonctions de formatage de {scales}.

¾ Mise en garde

gtsummary::style_number() est directement une fonc-


tion de formatage (comme scales::number()) et non une
fonction qui génère une fonction de formatage (comme
scales::label::number()).
Pour créer une fonction de formatage personnalisée, on
pourra avoir recours à purrr::partial() qui permet
d’appeler partiellement une fonction et qui renvoie une
nouvelle fonction avec des paramètres par défaut person-
nalisés.

fr <- style_number |>


purrr::partial([Link] = ",", digits = 1)
fr(x)

[1] "0,1" "0,9" "1,1" "12,3" "-0,1" "-0,9" "-1,1" "-132,3"

15.3.2 style_sigfig()

Variante de gtsummary::style_number(), gstummary::style_sigfig()


arrondi les valeurs transmises pour n’afficher qu’un nombre
choisi de chiffres significatifs. Le nombre de décimales peut
ainsi varier.

205
style_sigfig(x)

[1] "0.12" "0.90" "1.1" "12" "-0.12" "-0.90" "-1.1" "-132"

style_sigfig(x, digits = 3)

[1] "0.123" "0.900" "1.12" "12.3" "-0.123" "-0.900" "-1.12" "-132"

15.3.3 style_percent()

La fonction gtsummary::style_percent() a un fonctionne-


ment un peu différent de celui de scales::label_percent().
Par défaut, le symbole % n’est pas affiché (mais paramétrable
avec symbol = TRUE. Par défaut, une décimale est affichée pour
les valeurs inférieures à 10% et aucune pour celles supérieures
à 10%. Un symbole < est ajouté devant les valeurs strictement
positives inférieures à 0,1%.

v <- c(0, 0.0001, 0.005, 0.01, 0.10, 0.45356, 0.99, 1.45)


label_percent(accuracy = .1)(v)

[1] "0.0%" "0.0%" "0.5%" "1.0%" "10.0%" "45.4%" "99.0%" "145.0%"

style_percent(v)

[1] "0" "<0.1" "0.5" "1.0" "10" "45" "99" "145"

style_percent(v, symbol = TRUE)

[1] "0%" "<0.1%" "0.5%" "1.0%" "10%" "45%" "99%" "145%"

style_percent(v, digits = 1)

[1] "0" "0.01" "0.50" "1.00" "10.0" "45.4" "99.0" "145.0"

206
15.3.4 style_pvalue()

La fonction gtsummary::style_pvalue() est similaire à


scales::label_pvalue() mais adapte le nombre de déci-
males affichées,

p <- c(0.000001, 0.023, 0.098, 0.60, 0.9998)


label_pvalue()(p)

[1] "<0.001" "0.023" "0.098" "0.600" ">0.999"

style_pvalue(p)

[1] "<0.001" "0.023" "0.10" "0.6" ">0.9"

style_pvalue(p, prepend_p = TRUE)

[1] "p<0.001" "p=0.023" "p=0.10" "p=0.6" "p>0.9"

15.3.5 style_ratio()

Enfin, gtsummary::style_ratio() est adaptée à l’affichage de


ratios.

r <- c(0.123, 0.9, 1.1234, 12.345, 101.234, -0.123, -0.9, -1.1234, -12.345, -101.234)
style_ratio(r)

[1] "0.12" "0.90" "1.12" "12.3" "101" "-0.12" "-0.90" "-1.12" "-12.3"
[10] "-101"

207
15.4 Bonus : signif_stars() de {ggstats}

La fonction ggstats::signif_stars() de {ggstats} permet


d’afficher des p-valeurs sous forme d’étoiles de significativité.
Par défaut, trois astérisques si p < 0,001, deux si p < 0,01, une
si p < 0,05 et un point si p < 0,10. Les valeurs sont bien sur
paramétrables.

p <- c(0.5, 0.1, 0.05, 0.01, 0.001)


ggstats::signif_stars(p)

[1] "" "." "*" "**" "***"

ggstats::signif_stars(p, one = .15, point = NULL)

[1] "" "*" "*" "**" "***"

15.5

208
16 Couleurs & Palettes

Dans les prochains chapitres, notamment lorsque nous ferons


des graphiques, nous aurons besoin de spécifier à R les couleurs
souhaitées.
Le choix d’une palette de couleurs adaptée à sa représentation
graphique est également un élément essentiel avec quelques
règles de base : un dégradé est adapté pour représentée une
variable continue tandis que pour une variable catégorielle non
ordonnée on aura recours à une palette contrastée.

16.1 Noms de couleur

Lorsque l’on doit indiquer à R une couleur, notamment dans


les fonctions graphiques, on peut mentionner certaines couleurs
en toutes lettres (en anglais) comme "red" ou "blue". La liste
des couleurs reconnues par R est disponible sur [Link]
[Link]/~tzheng/files/[Link].

library(tidyverse)
ggplot(iris) +
aes(x = [Link]) +
geom_histogram(colour = "red", fill = "blue")

209
20
count

10

0
2 4 6
[Link]

16.2 Couleurs RVB et code hexadécimal

En informatique, les couleurs sont usuellement codées en


Rouge/Vert/Bleu (voir [Link]
e_vert_bleu) et représentées par un code hexadécimal à 6
caractères (chiffres 0 à 9 et/ou lettres A à F), précédés du
symbole #. Ce code est reconnu par R. On pourra par exemple
indiquer "#FF0000" pour la couleur rouge ou "#666666" pour
un gris foncé. Le code hexadécimal des différentes couleurs
peut s’obtenir aisément sur internet, de nombreux sites étant
consacrés aux palettes de couleurs.

ggplot(iris) +
aes(x = [Link]) +
geom_histogram(colour = "#666666", fill = "#FF0000")

210
20
count

10

0
2 4 6
[Link]

Parfois, au lieu du code hexadécimal, les couleurs RVB


sont indiquées avec trois chiffres entiers compris entre 0 et
255. La conversion en hexadécimal se fait avec la fonction
grDevices::rgb().

rgb(255, 0, 0, maxColorValue = 255)

[1] "#FF0000"

16.3 Palettes de couleurs

16.3.1 Color Brewer

Le projet Color Brewer a développé des palettes cartogra-


phiques, à la fois séquentielles, divergentes et catégorielles, pré-
sentées en détail sur [Link] Pour chaque
type de palette, et en fonction du nombre de classes, est indi-
qué sur ce site si la palette est adaptée aux personnes souffrant
de daltonisme, si elle est rendra correctement sur écran, en cas
d’impression couleur et en cas d’impression en noir et blanc.
Voici un aperçu des différentes palettes disponibles :

211
YlOrRd
YlOrBr
YlGnBu
YlGn
Reds
RdPu
Purples
PuRd
PuBuGn
PuBu
OrRd
Oranges
Greys
Greens
GnBu
BuPu
BuGn
Blues
Set3
Set2
Set1
Pastel2
Pastel1
Paired
Dark2
Accent
Spectral
RdYlGn
RdYlBu
RdGy
RdBu
PuOr
PRGn
PiYG
BrBG

L’extension {RColorBrewer} permets d’accéder à ces palettes


sous R.
Si on utilise {ggplot2}, les palettes Color Brewer sont directe-
ment disponibles via les fonctions ggplot2::scale_fill_brewer()
et ggplot2::scale_colour_brewer().

¾ Mise en garde

Les palettes Color Brewer sont seulement implémentées


pour des variables catégorielles. Il est cependant pos-
sible de les utiliser avec des variables continues en les
combinant avec ggplot2::scale_fill_gradientn() ou
ggplot2::scale_coulour_gradientn() (en remplaçant
"Set1" par le nom de la palette désirée) :

scale_fill_gradientn(values = RColorBrewer::[Link](6, "Set1"))

212
16.3.2 Palettes de Paul Tol

Le physicien Paul Tol a développé plusieurs palettes de couleurs


adaptées aux personnes souffrant de déficit de perception des
couleurs (daltonisme). À titre personnel, il s’agit des palettes
de couleurs que j’utilise le plus fréquemment.
Le détail de ses travaux est présenté sur [Link]
nl/~pault/.
Le package {khroma} implémente ces palettes de couleurs pro-
posées par Paul Tol afin de pouvoir les utilisées directement
dans R et avec {ggplot}.

library(khroma)
plot_scheme(colour("bright")(7), colours = TRUE)

#4477AA #228833 #66CCEE #BBBBBB

#EE6677 #CCBB44 #AA3377

ggplot(mpg) +
aes(x = displ, y = hwy, colour = class) +
geom_point() +
khroma::scale_colour_bright()

213
40
class
2seater
compact
30 midsize
hwy

minivan
pickup
subcompact
20
suv

2 3 4 5 6 7
displ

plot_scheme(colour("muted")(9), colours = TRUE)

#CC6677 #DDCC77 #88CCEE #44AA99 #AA4499


#332288 #117733 #882255 #999933 #DDDDDD

plot_scheme(colour("PRGn")(9), colours = TRUE, size = 0.9)

214
#762A83 #C2A5CF #F7F7F7 #ACD39E #1B7837
#9970AB #E7D4E8 #D9F0D3 #5AAE61 #FFEE99

Pour la liste complète des palettes disponibles, voir [Link]


[Link]/khroma/articles/[Link].

16.3.3 Interface unifiée avec {paletteer}

L’extension {paletteer} vise à proposer une interface unifiée


pour l’utilisation de palettes de couleurs fournies par d’autres
packages (dont {khroma}, mais aussi par exemple {ggsci} qui
fournit les palettes utilisées par certaines revues scientifiques).
Plus de 2 500 palettes sont ainsi disponibles.
On peut afficher un aperçu des principales palettes disponibles
dans {paletteer} avec la commande suivante :

gt::info_paletteer()

Pour afficher la liste complète des palettes discrètes et continues,


on utilisera les commandes suivantes :

palettes_d_names |> View()


palettes_c_names |> View()

La fonction paletteer::paletteer_d() permet d’obtenir


les codes hexadécimaux d’une palette discrète en pré-
cisant le nombre de couleurs attendues. Les fonctions

215
paletteer::scale_color_paletteer_d() et paletteer::scale_fill_paletteer_d()
permettront d’utiliser une palette donnée avec {ggplot2}.

library(paletteer)
paletteer_d("khroma::bright", n = 5)

<colors>
#4477AAFF #EE6677FF #228833FF #CCBB44FF #66CCEEFF

ggplot(mpg) +
aes(x = displ, y = hwy, colour = class) +
geom_point() +
scale_color_paletteer_d("khroma::bright")

40
class
2seater
compact
30 midsize
hwy

minivan
pickup
subcompact
20
suv

2 3 4 5 6 7
displ

L’équivalent existe pour les palettes continues, avec


paletteer::paletteer_c(), paletteer::scale_color_paletteer_c()
et paletteer::scale_fill_paletteer_c() .

paletteer_c("viridis::viridis", n = 6)

<colors>
#440154FF #414487FF #2A788EFF #22A884FF #7AD151FF #FDE725FF

216
ggplot(iris) +
aes(x = [Link], y = [Link], colour = [Link]) +
geom_point() +
scale_colour_paletteer_c("viridis::viridis", direction = -1)

4.5

4.0

[Link]
[Link]

3.5 6
5
4
3.0
3
2
1
2.5

2.0
5 6 7 8
[Link]

217
partie III

Analyses

218
17 Graphiques avec ggplot2

Le package {ggplot2} fait partie intégrante du tidyverse. Déve-


loppé par Hadley Wickham, ce package met en œuvre la gram-
maire graphique théorisée par Leland Wilkinson. Il devient vite
indispensable lorsque l’on souhaite réaliser des graphiques un
peu complexe.

17.1 Ressources

Il existe de très nombreuses ressources traitant de {ggplot2}.


Pour une introduction en français, on pourra se référer au cha-
pitre Visualiser avec ggplot2 de l’Introduction à R et au tidy-
verse de Julien Barnier, au chapitre Introduction à ggplot2, la
grammaire des graphiques du site analyse-R et adapté d’une
séance de cours de François Briatte, ou encore au chapitre Gra-
phiques du cours Logiciel R et programmation d’Ewen Gallic.
Pour les anglophones, la référence reste encore l’ouvrage gg-
plot2: Elegant Graphics for Data Analysis d’Hadley Wickham
lui-même, dont la troisième édition est librement accessible en
ligne ([Link] D’un point de vue pra-
tique, l’ouvrage R Graphics Cookbook: practical recipes for vi-
sualizing data de Winston Chang est une mine d’informations,
ouvrage là encore librement accessible en ligne ([Link]
[Link]/).

17.2 Les bases de ggplot2

{ggplot2} nécessite que les données du graphique soient sous la


forme d’un tableau de données ([Link] ou tibble) au format

219
tidy, c’est-à-dire avec une ligne par observation et les différentes
valeurs à représenter sous forme de variables du tableau.

Figure 17.1: La grammaire des graphiques

Tous les graphiques avec {ggplot2} suivent une même logique.


En premier lieu, on appellera la fonction ggplot2::ggplot()
en lui passant en paramètre le fichier de données.
{ggplot2} nomme esthétiques les différentes propriétés vi-
suelles d’un graphique, à savoir l’axe des x (x), celui des y
(y), la couleur des lignes (colour), celle de remplissage des
polygones (fill), le type de lignes (linetype), la forme des
points (shape), etc. Une représentation graphique consiste
donc à représenter chacune de nos variables d’intérêt selon
une esthétique donnée. En second lieu, on appellera donc
la fonction ggplot2::aes() pour indiquer la correspondance
entre les variables de notre fichier de données et les esthétiques
du graphique.
A minima, il est nécessaire d’indiquer en troisième lieu
une géométrie, autrement dit la manière dont les élé-
ments seront représentés visuellement. À chaque géomé-
trie corresponds une fonction commençant par geom_,
par exemple ggplot2::geom_point() pour dessiner

220
des points, ggplot2::geom_line() pour des lignes,
ggplot2::geom_bar() pour des barres ou encore ggplot2::geom_area()
pour des aires. Il existe de nombreuses géométries différentes21 , 21
On trouvera une liste dans la cheat
chacune prenant en compte certaines esthétiques, certaines sheet de {ggplot2}, voir Section 17.3.
étant requises pour cette géométrie et d’autres optionnelles.
La liste des esthétiques prises en compte par chaque géométrie
est indiquée dans l’aide en ligne de cette dernière.
Voici un exemple minimal de graphique avec {ggplot2} :

library(ggplot2)
p <-
ggplot(iris) +
aes(
x = [Link],
y = [Link],
colour = Species
) +
geom_point()
p

2.5

2.0

Species
[Link]

1.5
setosa
versicolor
1.0 virginica

0.5

0.0
2 4 6
[Link]

Figure 17.2: Un exemple simple de nuage de points avec


ggplot2

221
ĺ Syntaxe additive

Le développement de {ggplot2} a débuté avant celui du


tidyverse et la généralisation du pipe. Dès lors, on ne
sera pas étonné que la syntaxe de {ggplot2} n’ait pas
recours à ce dernier mais repose sur une approche addi-
tive. Un graphique est dès lors initialisé avec la fonction
ggplot2::ggplot() et l’on ajoutera successivement des
éléments au graphique en appelant différentes fonctions
et en utilisant l’opérateur +.

Il est ensuite possible de personnaliser de nombreux éléments


d’un graphique et notamment :

• les étiquettes ou labs (titre, axes, légendes) avec


ggplot2::ggtitle(), ggplot2::xlab(), ggplot2::ylab()
ou encore la fonction plus générique ggplot2::labs() ;
• les échelles (scales) des différentes esthétiques avec les
fonctions commençant par scale_ ;
• le système de coordonnées avec les fonctions commençant
par coord_ ;
• les facettes (facets) avec les fonctions commençant par
facet_ ;
• la légende (guides) avec les fonctions commençant par
guide_ ;
• le thème du graphiques (mise en forme des différents élé-
ments) avec ggplot2::theme().

p +
labs(
x = "Longueur du pétale",
y = "Largeur du pétale",
colour = "Espèce"
) +
ggtitle(
"Relation entre longueur et largeur des pétales",
subtitle = "Jeu de données Iris"
) +
scale_x_continuous(breaks = 1:7) +
scale_y_continuous(

222
labels = scales::label_number([Link] = ",")
) +
coord_equal() +
facet_grid(cols = vars(Species)) +
guides(
color = guide_legend(nrow = 2)
) +
theme_light() +
theme(
[Link] = "bottom",
[Link] = element_text(face = "bold")
)

Relation entre longueur et largeur des pétales


Jeu de données Iris
Largeur du pétale

setosa versicolor virginica


2,5
2,0
1,5
1,0
0,5
0,0
1 2 3 4 5 6 7 1 2 3 4 5 6 7 1 2 3 4 5 6 7
Longueur du pétale

setosa virginica
Espèce
versicolor

Figure 17.3: Un exemple avancé de nuage de points avec


ggplot2

Pour visualiser chaque étape du code, vous pouvez consulter


le diaporama suivant : [Link]
R/analyses/ressources/[Link]

223
Figure 17.4: Cheatsheet ggplot2

17.3 Cheatsheet

17.4 Exploration visuelle avec esquisse

Le package {esquisse} propose un addin offrant une interface


visuelle pour la création de graphiques {ggplot2}. Après ins-
tallation du package, on pourra lancer {esquisse} directement
à partir du menu addins de RStudio.

Figure 17.5: Lancement d’esquisse à partir du menu Addins


de RStudio

Au lancement de l’addin, une interface permettra de choisir le


tableau de données à partir duquel générer le graphique. Le plus
simple est de choisir un tableau présent dans l’environnement.
Mais {esquisse} offre aussi la possibilité d’importer des fi-
chiers externes, voir de procéder à quelques modifications des
données.
Le principe général d’{esquisse} consiste à associer des va-
riables à des esthétiques par glisser/déposer22 . L’outil détermi- 22
Si une esthétique n’est pas visible
nera automatiquement une géométrie adaptée en fonction de la à l’écran, on pourra cliquer en haut
à droite sur l’icône en forme de roue
nature des variables (continues ou catégorielles). Un clic sur le
dentée afin de choisir d’afficher plus
nom de la géométrie en haut à gauche permet de sélectionner d’esthétiques.
une autre géométrie.
Les menus situés en bas de l’écran permettent d’ajouter/modifier
des étiquettes, de modifier certaines options du graphiques, de

224
Figure 17.6: Import de données au lancement d’esquisse

Figure 17.7: Choix d’une géométrie dans esquisse

225
modifier les échelles de couleurs et l’apparence du graphique,
et de filtrer les observations inclues dans le graphique.
Le menu Code permet de récupérer le code correspondant au
graphique afin de pouvoir le copier/coller dans un script.

Figure 17.8: Obtenir le code du graphique obtenu avec


esquisse

{esquisse} offre également la possibilité d’exporter le


graphique obtenu dans différents formats.

17.5 webin-R

L’utilisation d’{esquisse} est présentée dans le webin-R #03


(statistiques descriptives avec gtsummary et esquisse) sur You-
Tube.
[Link]
{ggplot2} est abordé plus en détails dans le webin-R #08 (gg-
plot2 et la grammaire des graphiques) sur YouTube.
[Link]

226
17.6 Combiner plusieurs graphiques

Plusieurs packages proposent des fonctions pour combiner


ensemble des graphiques {ggplot2}, comme {patchwork},
{ggpubr}, {egg} ou {cowplot}. Ici, nous privilégierons le
package {patchwork} car, bien qu’il ne fasse pas partie du
tidyverse, est développé et maintenant par les mêmes auteurs
que {ggplot2}.
Commençons par créer quelques graphiques avec {ggplot2}.

p1 <- ggplot(mtcars) +
aes(x = wt, y = mpg) +
geom_point()
p2 <- ggplot(mtcars) +
aes(x = factor(cyl)) +
geom_bar()
p3 <- ggplot(mtcars) +
aes(x = factor(cyl), y = mpg) +
geom_violin() +
theme([Link] = element_text(size = 20))
p4 <- ggplot(mtcars) +
aes(x = factor(cyl), y = mpg) +
geom_boxplot() +
ylab(NULL)

Le symbole + permet de combiner des graphiques entre eux.


Le package {patchwork} déterminera le nombre de lignes et de
colonnes en fonction du nombre de graphiques. On pourra noter
que les axes des graphiques sont alignés les uns par rapports
aux autres.

library(patchwork)
p1 + p2 + p3 + p4

227
35
30
10

count
25
mpg

20 5
15
10 0
2 3 4 5 4 6 8
wt factor(cyl)
35 35
30 30
mpg

25 25
20 20
15 15
10 10
4 6 8 4 6 8

factor(cyl) factor(cyl)

Les symboles | et / permettent d’indiquer une disposition côte


à côte ou les uns au-dessus des autres.

p1 | p2 | p3

35 35

30 30

10
25 25
mpg
count
mpg

20 20
5

15 15

10 0 10
2 3 4 5 4 6 8 4 6 8
wt factor(cyl) factor(cyl)

p1 / p2

228
35
30
25
mpg

20
15
10
2 3 4 5
wt

10
count

0
4 6 8
factor(cyl)

On peut utiliser les parenthèses pour indiquer des arrangements


plus complexes.

(p1 + p2) / p3

35
30
10
count

25
mpg

20 5
15
10 0
2 3 4 5 4 6 8
wt factor(cyl)
35
30
mpg

25
20
15
10
4 6 8

factor(cyl)

(p1 + p2) | p3

229
35 35

30 30

10
25 25

mpg
count
mpg

20 20
5

15 15

10 0 10
2 3 4 5 4 6 8 4 6 8
wt factor(cyl) factor(cyl)

Si l’on a une liste de graphiques, on pourra appeler


patchwork::wrap_plots().

list(p1, p2, p3, p4) |>


wrap_plots()

35
30
10
count

25
mpg

20 5
15
10 0
2 3 4 5 4 6 8
wt factor(cyl)
35 35
30 30
mpg

25 25
20 20
15 15
10 10
4 6 8 4 6 8

factor(cyl) factor(cyl)

La fonction patchwork::plot_layout() permet de contrôler


les hauteurs / largeurs relatives des lignes / colonnes.

230
p1 + p2 + p3 + p4 + plot_layout(widths = c(2, 1))

35
30
10

count
25
mpg

20 5
15
10 0
2 3 4 5 4 6 8
wt factor(cyl)
35 35
30 30
mpg

25 25
20 20
15 15
10 10
4 6 8 4 6 8

factor(cyl) factor(cyl)

On peut également ajouter un titre ou des étiquettes avec


patchwork::plot_annotation().

p1 + p2 + p3 + p4 +
plot_annotation(
title = "Titre du graphique",
subtitle = "sous-titre",
caption = "notes additionelles",
tag_levels = "a",
tag_suffix = "."
)

231
Titre du graphique
sous−titre

a. 35
b.
30

count
10
mpg

25
20 5
15
10 0
2 3 4 5 4 6 8
wt factor(cyl)

c. 35
d. 35
mpg

30 30
25 25
20 20
15 15
10 10
4 6 8 4 6 8

factor(cyl) factor(cyl)

notes additionelles

232
18 Statistique univariée &
Intervalles de confiance

On entend par statistique univariée l’étude d’une seule variable,


que celle-ci soit continue (quantitative) ou catégorielle (quali-
tative). La statistique univariée fait partie de la statistique des-
criptive.

18.1 Exploration graphique

Une première approche consiste à explorer visuelle la variable


d’intérêt, notamment à l’aide de l’interface proposée par
{esquisse} (cf Section 17.4).
Nous indiquons ci-après le code correspond aux graphiques
{ggplot2} les plus courants.

library(ggplot2)

18.1.1 Variable continue

Un histogramme est la représentation graphique la plus


commune pour représenter la distribution d’une variable, par
exemple ici la longueur des pétales (variable [Link])
du fichier de données datasets::iris. Il s’obtient avec la
géométrie ggplot2::geom_histogram().

ggplot(iris) +
aes(x = [Link]) +
geom_histogram()

233
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

20
count

10

0
2 4 6
[Link]

Figure 18.1: un histogramme simple

Ď Astuce

Il faut noter qu’il nous a suffit d’associer simplement la va-


riable [Link] à l’esthétique x, sans avoir eu besoin
d’indiquer une variable pour l’esthétique y.
En fait, {ggplot2} associe par défaut à toute géo-
métrie une certaine statistique. Dans le cas de
ggplot2::geom_histogram(), il s’agit de la statistique
ggplot2::stat_bin() qui divise la variable continue
en classes de même largeur et compte le nombre
d’observation dans chacune. ggplot2::stat_bin() ren-
voie un certain nombre de variables calculées (la liste
complète est indiquée dans la documentation dans la sec-
tion Compute variables), dont la variable count qui cor-
respond au nombre d’observations la classe. On peut as-
socier cette variable calculée à une esthétique grâce à la
fonction ggplot2::after_stat(), par exemple aes(y =
after_stat(count)). Dans le cas présent, ce n’est pas
nécessaire car {ggplot2} fait cette association automa-
tiquement si l’on n’a pas déjà attribué une variable à
l’esthétique y.

234
On peut personnaliser la couleur de remplissage des rectangles
en indiquant une valeur fixe pour l’esthétique fill dans
l’appel de ggplot2::geom_histogram() (et non via la fonc-
tion ggplot2::aes() puisqu’il ne s’agit pas d’une variable du
tableau de données). L’esthétique colour permet de spécifier la
couleur du trait des rectangles. Enfin, le paramètre binwidth
permet de spécifier la largeur des barres.

ggplot(iris) +
aes(x = [Link]) +
geom_histogram(
fill ="lightblue",
colour = "black",
binwidth = 1
) +
xlab("Longeur du pétale") +
ylab("Effectifs")

30
Effectifs

20

10

0
2 4 6
Longeur du pétale

Figure 18.2: un histogramme personnalisé

On peut alternativement indiquer un nombre de classes avec


bins.

235
ggplot(iris) +
aes(x = [Link]) +
geom_histogram(bins = 10, colour = "black")

40

30
count

20

10

0
2 4 6
[Link]

Figure 18.3: un histogramme en 10 classes

Une représentation alternative de la distribution d’une variable


peut être obtenue avec une courbe de densité, dont la particu-
larité est d’avoir une surface sous la courbe égale à 1. Une
telle courbe s’obtient avec ggplot2::geom_density(). Le pa-
ramètre adjust permet d’ajuster le niveau de lissage de la
courbe.

ggplot(iris) +
aes(x = [Link]) +
geom_density(adjust = .5)

236
0.4

0.3
density

0.2

0.1

0.0
2 4 6
[Link]

Figure 18.4: une courbe de densité

18.1.2 Variable catégorielle

Pour représenter la répartition des effectifs parmi les modalités


d’une variable catégorielle, on a souvent tendance à utiliser des
diagrammes en secteurs (camemberts). Or, ce type de représen-
tation graphique est très rarement appropriée : l’œil humain
préfère comparer des longueurs plutôt que des surfaces23 . 23
Voir en particulier [Link]
data- to- [Link]/caveat/[Link]
Dans certains contextes ou pour certaines présentations, on pour un exemple concret.
pourra éventuellement considérer un diagramme en donut,
mais le plus souvent, rien ne vaut un bon vieux diagramme
en barres avec ggplot2::geom_bar(). Prenons pour l’exemple
la variable occup du jeu de données hdv2003 du package
{questionr}.

data("hdv2003", package = "questionr")


ggplot(hdv2003) +
aes(x = occup) +
geom_bar()

237
1000

750
count

500

250

0
Exerce une profession
ChomeurEtudiant, eleve RetraiteRetire des affairesAu foyer Autre inactif
occup

Figure 18.5: un diagramme en barres simple

Ď Astuce

Là encore, {ggplot2} a calculé de lui-même le nombre


d’observations de chaque modalité, en utilisant cette fois
la statistique ggplot2::stat_count().

Si l’on souhaite représenter des pourcentages plutôt que des


effectifs, le plus simple est d’avoir recours à la statistique
ggstats::stat_prop() du package {ggstats}24 . Pour appe- 24
Cette statistique est également dis-
ler cette statistique, on utilisera simplement stat = "prop" ponible via le package {GGally}.
dans les géométries concernées.
Cette statistique, qui sera également bien utile pour des gra-
phiques plus complexes, nécessite qu’on lui indique une esthé-
tique by pour dans quels sous-groupes calculés des proportions.
Ici, nous avons un seul groupe considéré et nous souhaitons des
pourcentages du total. On indiquera simplement by = 1.
Pour formater l’axe vertical avec des pourcentages, on pourra
avoir recours à la fonction scales::label_percent() que l’on
appellera via ggplot2::scale_y_continuous().

library(ggstats)
ggplot(hdv2003) +

238
aes(x = occup, y = after_stat(prop), by = 1) +
geom_bar(stat = "prop") +
scale_y_continuous(labels = scales::label_percent())

50%

40%

30%
prop

20%

10%

0%
Exerce une profession
ChomeurEtudiant, eleve RetraiteRetire des affairesAu foyer Autre inactif
occup

Figure 18.6: un diagramme en barres épuré

Pour une publication ou une communication, il ne faut


surtout pas hésiter à épurer vos graphiques (less is bet-
ter!), voire à trier les modalités en fonction de leur fré-
quence pour faciliter la lecture (ce qui se fait aisément avec
forcats::fct_infreq()).

ggplot(hdv2003) +
aes(x = forcats::fct_infreq(occup),
y = after_stat(prop), by = 1) +
geom_bar(stat = "prop",
fill = "#4477AA", colour = "black") +
geom_text(
aes(label = after_stat(prop) |>
scales::percent(accuracy = .1)),
stat = "prop",
nudge_y = .02
) +
theme_minimal() +

239
theme(
[Link] = element_blank(),
[Link].y = element_blank()
) +
xlab(NULL) + ylab(NULL) +
ggtitle("Occupation des personnes enquêtées")

Occupation des personnes enquêtées


52.4%

19.6%

8.6%
6.7%
4.7% 4.2% 3.8%

Exerce une profession


Retraite Au foyer Chomeur Etudiant, eleveAutre inactif
Retire des affaires

Figure 18.7: un diagramme en barres épuré

Pour visualiser chaque étape du code, vous pouvez consulter


le diaporama suivant : [Link]
R/analyses/ressources/flipbook-geom_bar-[Link]

18.2 Tableaux et tris à plat

Le package {gtsummary} constitue l’une des boites à ou-


tils de l’analyste quantitativiste, car il permet de réaliser
très facilement des tableaux quasiment publiables en l’état.
En matière de statistique univariées, la fonction clé est
gtsummary::tbl_summary().
Commençons avec un premier exemple rapide. On part d’un
tableau de données et on indique, avec l’argument include,
les variables à afficher dans le tableau statistique (si on

240
n’indique rien, toutes les variables du tableau de données
sont considérées). Il faut noter que l’argument include de
gtsummary::tbl_summary() utilise la même syntaxe dite
tidy select que dplyr::select() (cf. Section 8.2.1). On
peut indiquer tout autant des variables catégorielles que des
variables continues.

library(gtsummary)

#Uighur

hdv2003 |>
tbl_summary(include = c(age, occup))

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Table 18.1: un tableau simple

Characteristic N = 2,000
age 48 (35, 60)
occup
Exerce une profession 1,049 (52%)
Chomeur 134 (6.7%)
Etudiant, eleve 94 (4.7%)
Retraite 392 (20%)
Retire des affaires 77 (3.9%)
Au foyer 171 (8.6%)
Autre inactif 83 (4.2%)

ĺ Remarque sur les types de variables et les sélecteurs


associés

{gtsummary} permets de réaliser des tableaux statistiques


combinant plusieurs variables, l’affichage des résultats
pouvant dépendre du type de variables.

241
Par défaut, {gtsummary} considère qu’une variable est ca-
tégorielle s’il s’agit d’un facteur, d’une variable textuelle
ou d’une variable numérique ayant moins de 10 valeurs
différentes.
Une variable sera considérée comme dichotomique (va-
riable catégorielle à seulement deux modalités) s’il s’agit
d’un vecteur logique (TRUE/FALSE), d’une variable tex-
tuelle codée yes/no ou d’une variable numérique codée
0/1.
Dans les autres cas, une variable numérique sera considé-
rée comme continue.
Si vous utilisez des vecteurs labellisés (cf. Cha-
pitre 12), vous devez les convertir, en amont, en
facteurs ou en variables numériques. Voir l’extension
{labelled} et les fonctions labelled::to_factor(),
labelled::unlabelled() et unclass().
Au besoin, il est possible de forcer le type d’une variable
avec l’argument type de gtsummary::tbl_summary().
{gtsummary} fournit des sélecteurs qui peuvent être utili-
sés dans les options des différentes fonctions, en particulier
gtsummary::all_continuous() pour les variables conti-
nues, gtsummary::all_dichotolous() pour les variables
dichotomiques et gtsummary::all_categorical() pour
les variables catégorielles. Cela inclue les variables dicho-
tomiques. Il faut utiliser all_categorical(dichotomous
= FALSE) pour sélectionner les variables catégorielles en
excluant les variables dichotomiques.

18.2.1 Thème du tableau

{gtsummary} fournit plusieurs fonctions préfixées theme_gtsummary_*()


permettant de modifier l’affichage par défaut des tableaux.
Vous aurez notez que, par défaut, {gtsummary} est anglo-
phone.
La fonction gtsummary::theme_gtsummary_journal() per-
mets d’adopter les standards de certaines grandes revues
scientifiques telles que JAMA (Journal of the American
Medical Association), The Lancet ou encore le NEJM (New
England Journal of Medicine).

242
La fonction gtsummary::theme_gtsummary_language() per-
met de modifier la langue utilisée par défaut dans les tableaux.
Les options [Link] et [Link] permettent de définir
respectivement le séparateur de décimales et le séparateur des
milliers. Ainsi, pour présenter un tableau en français, on appli-
quera en début de script :

theme_gtsummary_language(
language = "fr",
[Link] = ",",
[Link] = " "
)

Setting theme `language: fr`

Ce thème sera appliqué à tous les tableaux ultérieurs.

hdv2003 |>
tbl_summary(include = c(age, occup))

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Table 18.2: un tableau simple en français

Caractéristique N = 2 000
age 48 (35 – 60)
occup
Exerce une profession 1 049 (52%)
Chomeur 134 (6,7%)
Etudiant, eleve 94 (4,7%)
Retraite 392 (20%)
Retire des affaires 77 (3,9%)
Au foyer 171 (8,6%)
Autre inactif 83 (4,2%)

243
18.2.2 Étiquettes des variables

gtsummary, par défaut, prends en compte les étiquettes de


variables (cf. Chapitre 11), si elles existent, et sinon utilisera
le nom de chaque variable dans le tableau. Pour rappel, les éti-
quettes de variables peuvent être manipulées avec l’extension
{labelled} et les fonctions labelled::var_label() et
labelled::set_variable_labels().
Il est aussi possible d’utiliser l’option label de gtsummary::tbl_summary()
pour indiquer des étiquettes personnalisées.

hdv2003 |>
labelled::set_variable_labels(
occup = "Occupation actuelle"
) |>
tbl_summary(
include = c(age, occup, [Link]),
label = list(age ~ "Âge médian")
)

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Table 18.3: un tableau étiquetté

Caractéristique N = 2 000
Âge médian 48 (35 – 60)
Occupation actuelle
Exerce une profession 1 049 (52%)
Chomeur 134 (6,7%)
Etudiant, eleve 94 (4,7%)
Retraite 392 (20%)
Retire des affaires 77 (3,9%)
Au foyer 171 (8,6%)
Autre inactif 83 (4,2%)
[Link] 2,00 (1,00 – 3,00)
Manquant 5

244
Pour modifier les modalités d’une variable catégorielle, il faut
modifier en amont les niveaux du facteur correspondant.

ĺ Remarque sur la syntaxe des options

De nombreuses options des fonctions de {gtsummary}


peuvent s’appliquer seulement à une ou certaines va-
riables. Pour ces options-là, {gtsummary} attends une for-
mule de la forme variables concernées ~ valeur de
l'option ou bien une liste de formules ayant cette forme.
Par exemple, pour modifier l’étiquette associée à une
certaine variable, on peut utiliser l’option label de
gtsummary::tbl_summary().

trial |>
tbl_summary(label = age ~ "Âge")

Lorsque l’on souhaite passer plusieurs options pour plu-


sieurs variables différentes, on utilisera une list().

trial |>
tbl_summary(label = list(age ~ "Âge", trt ~ "Traitement"))

{gtsummary} est très flexible sur la manière d’indiquer


la ou les variables concernées. Il peut s’agir du nom de
la variable, d’une chaîne de caractères contenant le nom
de la variable, ou d’un vecteur contenant le nom de la
variable. Les syntaxes ci-dessous sont ainsi équivalentes.

trial |>
tbl_summary(label = age ~ "Âge")
trial |>
tbl_summary(label = "age" ~ "Âge")
v <- "age"
trial |>
tbl_summary(label = v ~ "Âge")

Pour appliquer le même changement à plusieurs variables,


plusieurs syntaxes sont acceptées pour lister plusieurs va-
riables.

245
trial |>
tbl_summary(label = c("age", "trt") ~ "Une même étiquette")
trial |>
tbl_summary(label = c(age, trt) ~ "Une même étiquette")

Il est également possible d’utiliser la syn-


taxe {tidyselect} et les sélecteurs de
{tidyselect} comme tidyselect::everything(),
tidyselect::starts_with(),
tidyselect::contains() ou tidyselect::all_of().
Ces différents sélecteurs peuvent être combinés au sein
d’un c().

trial |>
tbl_summary(
label = everything() ~ "Une même étiquette"
)
trial |>
tbl_summary(
label = starts_with("a") ~ "Une même étiquette"
)
trial |>
tbl_summary(
label = c(everything(), -age, -trt) ~ "Une même étiquette"
)
trial |>
tbl_summary(
label = age:trt ~ "Une même étiquette"
)

Bien sûr, il est possible d’utiliser les sélecteurs propres à


{gtsummary}.

246
trial |>
tbl_summary(
label = all_continuous() ~ "Une même étiquette"
)
trial |>
tbl_summary(
label = list(
all_continuous() ~ "Variable continue",
all_dichotomous() ~ "Variable dichotomique",
all_categorical(dichotomous = FALSE) ~ "Variable catégorielle"
)
)

Enfin, si l’on ne précise rien à gauche du ~, ce sera consi-


déré comme équivalent à everything(). Les deux syn-
taxes ci-dessous sont donc équivalentes.

trial |>
tbl_summary(label = ~ "Une même étiquette")
trial |>
tbl_summary(
label = everything() ~ "Une même étiquette"
)

18.2.3 Statistiques affichées

Le paramètre statistic permets de sélectionner les statis-


tiques à afficher pour chaque variable. On indiquera une chaîne
de caractères dont les différentes statistiques seront indiquées
entre accolades ({}).
Pour une variable continue, on pourra utiliser {median} pour
la médiane, {mean} pour la moyenne, {sd} pour l’écart type,
{var} pour la variance, {min} pour le minimum, {max} pour le
maximum, ou encore {p##} (en remplaçant ## par un nombre
entier entre 00 et 100) pour le percentile correspondant (par
exemple p25 et p75 pour le premier et le troisième quartile). Uti-
lisez gtsummary::all_continous() pour sélectionner toutes
les variables continues.

247
hdv2003 |>
tbl_summary(
include = c(age, [Link]),
statistic =
all_continuous() ~ "Moy. : {mean} [min-max : {min} - {max}]"
)

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Table 18.4: statistiques personnalisées pour une variable conti-


nue

Caractéristique N = 2 000
age Moy. : 48 [min-max : 18 - 97]
[Link] Moy. : 2,25 [min-max : 0,00 - 12,00]
Manquant 5

Il est possible d’afficher des statistiques différentes pour chaque


variable.

hdv2003 |>
tbl_summary(
include = c(age, [Link]),
statistic = list(
age ~ "Méd. : {median} [{p25} - {p75}]",
[Link] ~ "Moy. : {mean} ({sd})"
)
)

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

248
Table 18.5: statistiques personnalisées pour une variable conti-
nue (2)

Caractéristique N = 2 000
age Méd. : 48 [35 - 60]
[Link] Moy. : 2,25 (1,78)
Manquant 5

Pour les variables continues, il est également possible d’indiquer


le nom d’une fonction personnalisée qui prends un vecteur
et renvoie une valeur résumée. Par exemple, pour afficher la
moyenne des carrés :

moy_carres <- function(x) {


mean(x^2, [Link] = TRUE)
}
hdv2003 |>
tbl_summary(
include = [Link],
statistic = ~ "MC : {moy_carres}"
)

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Table 18.6: statiques personnalisées pour une variable continue


(3)

Caractéristique N = 2 000
[Link] MC : 8,20
Manquant 5

Pour une variable catégorielle, les statistiques possibles


sont {n} le nombre d’observations, {N} le nombre total
d’observations, et {p} le pourcentage correspondant. Utilisez
gtsummary::all_categorical() pour sélectionner toutes les
variables catégorielles.

249
hdv2003 |>
tbl_summary(
include = occup,
statistic = all_categorical() ~ "{p} % ({n}/{N})"
)

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Table 18.7: statiques personnalisées pour une variable catégo-


rielle

Caractéristique N = 2 000
occup
Exerce une profession 52 % (1 049/2 000)
Chomeur 6,7 % (134/2 000)
Etudiant, eleve 4,7 % (94/2 000)
Retraite 20 % (392/2 000)
Retire des affaires 3,9 % (77/2 000)
Au foyer 8,6 % (171/2 000)
Autre inactif 4,2 % (83/2 000)

Il est possible, pour une variable catégorielle, de trier les moda-


lités de la plus fréquente à la moins fréquente avec le paramètre
sort.

hdv2003 |>
tbl_summary(
include = occup,
sort = all_categorical() ~ "frequency"
)

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

250
Table 18.8: variable catégorielle triée par fréquence

Caractéristique N = 2 000
occup
Exerce une profession 1 049 (52%)
Retraite 392 (20%)
Au foyer 171 (8,6%)
Chomeur 134 (6,7%)
Etudiant, eleve 94 (4,7%)
Autre inactif 83 (4,2%)
Retire des affaires 77 (3,9%)

Pour toutes les variables (catégorielles et continues), les statis-


tiques suivantes sont également disponibles :

• {N_obs} le nombre total d’observations,


• {N_miss} le nombre d’observations manquantes (NA),
• {N_nonmiss} le nombre d’observations non manquantes,
• {p_miss} le pourcentage d’observations manquantes
(i.e. N_miss / N_obs) et
• {p_nonmiss} le pourcentage d’observations non man-
quantes (i.e. N_nonmiss / N_obs).

18.2.4 Affichage du nom des statistiques

Lorsque l’on affiche de multiples statistiques, la liste des sta-


tistiques est regroupée dans une note de tableau qui peut vite
devenir un peu confuse.

tbl <- hdv2003 |>


tbl_summary(
include = c(age, [Link], occup),
statistic = list(
age ~ "{mean} ({sd})",
[Link] ~ "{median} [{p25} - {p75}]"
)
)
tbl

251
Table printed with `knitr::kable()`, not {gt}. Learn why at
[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Table 18.9: tableau par défaut

Caractéristique N = 2 000
age 48 (17)
[Link] 2,00 [1,00 - 3,00]
Manquant 5
occup
Exerce une profession 1 049 (52%)
Chomeur 134 (6,7%)
Etudiant, eleve 94 (4,7%)
Retraite 392 (20%)
Retire des affaires 77 (3,9%)
Au foyer 171 (8,6%)
Autre inactif 83 (4,2%)

La fonction gtsummary::add_stat_label() permets


d’indiquer le type de statistique à côté du nom des va-
riables ou bien dans une colonne dédiée, plutôt qu’en note de
tableau.

tbl |>
add_stat_label()

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Table 18.10: ajout du nom des statistiques

Caractéristique N = 2 000
age, Moyenne (ET) 48 (17)
[Link], Médiane [EI] 2,00 [1,00 - 3,00]
Manquant 5

252
Caractéristique N = 2 000
occup, n (%)
Exerce une profession 1 049 (52%)
Chomeur 134 (6,7%)
Etudiant, eleve 94 (4,7%)
Retraite 392 (20%)
Retire des affaires 77 (3,9%)
Au foyer 171 (8,6%)
Autre inactif 83 (4,2%)

tbl |>
add_stat_label(location = "column")

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Table 18.11: ajout du nom des statistiques dans une colonne


séparée

Caractéristique Statistique N = 2 000


age Moyenne (ET) 48 (17)
[Link] Médiane [EI] 2,00 [1,00 - 3,00]
Manquant n 5
occup
Exerce une profession n (%) 1 049 (52%)
Chomeur n (%) 134 (6,7%)
Etudiant, eleve n (%) 94 (4,7%)
Retraite n (%) 392 (20%)
Retire des affaires n (%) 77 (3,9%)
Au foyer n (%) 171 (8,6%)
Autre inactif n (%) 83 (4,2%)

18.2.5 Forcer le type de variable

Comme évoqué plus haut, {gtsummary} détermine automati-


quement le type de chaque variable. Par défaut, la variable age

253
du tableau de données trial est traitée comme variable conti-
nue, death comme dichotomique (seule la valeur 1 est affichée)
et grade comme variable catégorielle.

trial |>
tbl_summary(
include = c(grade, age, death)
)

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Table 18.12: types de variable par défaut

Caractéristique N = 200
Grade
I 68 (34%)
II 68 (34%)
III 64 (32%)
Age 47 (38 – 57)
Manquant 11
Patient Died 112 (56%)

Il est cependant possible de forcer un certain type avec


l’argument type. Précision : lorsque l’on force une variable en
dichotomique, il faut indiquer avec value la valeur à afficher
(les autres sont alors masquées).

trial |>
tbl_summary(
include = c(grade, death),
type = list(
grade ~ "dichotomous",
death ~ "categorical"
),
value = grade ~ "III",
label = grade ~ "Grade III"

254
)

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Table 18.13: types de variable personnalisés

Caractéristique N = 200
Grade III 64 (32%)
Patient Died
0 88 (44%)
1 112 (56%)

Il ne faut pas oublier que, par défaut, {gtsummary} traite les


variables quantitatives avec moins de 10 valeurs comme des
variables catégorielles. Prenons un exemple :

trial$alea <- sample(1:4, size = nrow(trial), replace = TRUE)


#| label: tbl-types-defaut-alea
#| tbl-cap: traitement par défaut d'une variable numérique à 4 valeurs uniques
trial |>
tbl_summary(
include = alea
)

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Caractéristique N = 200
alea
1 50 (25%)
2 45 (23%)
3 42 (21%)

255
Caractéristique N = 200
4 63 (32%)

On pourra forcer le traitement de cette variable comme conti-


nue.

trial |>
tbl_summary(
include = alea,
type = alea ~ "continuous"
)

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Table 18.15: forcer le traitement continu d’une variable numé-


rique à 4 valeurs uniques

Caractéristique N = 200
alea 3 (2 – 4)

18.2.6 Afficher des statistiques sur plusieurs lignes


(variables continues)

Pour les variables continues, {gtsummary} a introduit un


type de variable "continuous2", qui doit être attribué
manuellement via type, et qui permets d’afficher plusieurs
lignes de statistiques (en indiquant plusieurs chaînes de
caractères dans statistic). À noter le sélecteur dédié
gtsummary::all_continuous2().

hdv2003 |>
tbl_summary(
include = c(age, [Link]),
type = age ~ "continuous2",

256
statistic =
all_continuous2() ~ c(
"{median} ({p25} - {p75})",
"{mean} ({sd})",
"{min} - {max}"
)
)

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Table 18.16: des statistiques sur plusieurs lignes (variables


continues)

Caractéristique N = 2 000
age
Médiane (EI) 48 (35 - 60)
Moyenne (ET) 48 (17)
Étendue 18 - 97
[Link] 2,00 (1,00 – 3,00)
Manquant 5

18.2.7 Mise en forme des statistiques

L’argument digits permet de spécifier comment mettre en


forme les différentes statistiques. Le plus simple est d’indiquer
le nombre de décimales à afficher. Il est important de tenir
compte que plusieurs statistiques peuvent être affichées pour
une même variable. On peut alors indiquer une valeur diffé-
rente pour chaque statistique.

hdv2003 |>
tbl_summary(
include = c(age, occup),
digits = list(
all_continuous() ~ 1,

257
all_categorical() ~ c(0, 1)
)
)

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Table 18.17: personnalisation du nombre de décimales

Caractéristique N = 2 000
age 48,0 (35,0 – 60,0)
occup
Exerce une profession 1 049 (52,5%)
Chomeur 134 (6,7%)
Etudiant, eleve 94 (4,7%)
Retraite 392 (19,6%)
Retire des affaires 77 (3,9%)
Au foyer 171 (8,6%)
Autre inactif 83 (4,2%)

Au lieu d’un nombre de décimales, on peut indiquer plu-


tôt une fonction à appliquer pour mettre en forme le
résultat. Par exemple, {gtsummary} fournit les fonctions
suivantes : gtsummary::style_number() pour les nombres
de manière générale, gtsummary::style_percent() pour les
pourcentages (les valeurs sont multipliées par 100, mais le sym-
bole % n’est pas ajouté), gtsummary::style_pvalue()
pour les p-valeurs, gtsummary::style_sigfig() qui
n’affiche, par défaut, que deux chiffres significatifs, ou en-
core gtsummary::style_ratio() qui est une variante de
gtsummary::``style_sigfig() pour les ratios (comme les
odds ratios) que l’on compare à 1.
Il faut bien noter que ce qui est attendu par digits, c’est une
fonction et non le résultat d’une fonction. On indiquera donc
le nom de la fonction sans parenthèse, comme dans l’exemple
ci-dessous (même si pas forcément pertinent ;-)).

258
hdv2003 |>
tbl_summary(
include = age,
digits =
all_continuous() ~ c(style_percent, style_sigfig, style_ratio)
)

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Table 18.18: personnalisation de la mise en forme des nombres

Caractéristique N = 2 000
age 4 800 (35 – 60,0)

Comme digits s’attend à recevoir une fonction (et non le ré-


sultat) d’une fonction, on ne peut pas passer directement des
arguments aux fonctions style_*() de {gtsummary}. Pour cela
il faut créer une fonction à la levée :

trial |>
tbl_summary(
include = marker,
statistic = ~ "{mean} pour 100",
digits = ~ function(x){style_percent(x, digits = 1)}
)

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

259
Table 18.19: passer une fonction personnalisée à digits (syntaxe
1)

Caractéristique N = 200
Marker Level (ng/mL) 91,6 pour 100
Manquant 10

Depuis R 4.1, il existe une syntaxe raccourcie équivalente, avec


le symbole \ à la place de function.

trial |>
tbl_summary(
include = marker,
statistic = ~ "{mean} pour 100",
digits = ~ \(x){style_percent(x, digits = 1)}
)

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Table 18.20: passer une fonction personnalisée à digits (syntaxe


2)

Caractéristique N = 200
Marker Level (ng/mL) 91,6 pour 100
Manquant 10

Une syntaxe alternative consiste à avoir recours à la fonction


purrr::partial() qui permet d’appeler partiellement une
fonction et de renvoyer une nouvelle fonction.

trial |>
tbl_summary(
include = marker,
statistic = ~ "{mean} pour 100",
digits = ~ purrr::partial(style_percent, digits = 1)

260
)

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Table 18.21: passer une fonction personnalisée à digits (syntaxe


3)

Caractéristique N = 200
Marker Level (ng/mL) 91,6 pour 100
Manquant 10

À noter dans l’exemple précédent que les fonctions style_*()


de {gtsummary} tiennent compte du thème défini (ici la virgule
comme séparateur de décimale).
Pour une mise en forme plus avancée des nombres, il faut
se tourner vers l’extension {scales} et ses diverses fonc-
tions de mise en forme comme scales::label_number() ou
scales::label_percent().
ATTENTION : les fonctions de {scales} n’héritent pas des
paramètres du thème {gtsummary} actif. Il faut donc person-
naliser le séparateur de décimal dans l’appel à la fonction.

trial |>
tbl_summary(
include = marker,
statistic = ~ "{mean}",
digits = ~ scales::label_number(
accuracy = .01,
suffix = " ng/mL",
[Link] = ","
)
)

Table printed with `knitr::kable()`, not {gt}. Learn why at

261
[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Table 18.22: passer une fonction personnalisée à digits (syntaxe


4)

Caractéristique N = 200
Marker Level (ng/mL) 0,92 ng/mL
Manquant 10

18.2.8 Données manquantes

Le paramètre missing permets d’indiquer s’il faut afficher le


nombre d’observations manquantes (c’est-à-dire égales à NA) :
"ifany" (valeur par défaut) affiche ce nombre seulement s’il y
en a, "no" masque ce nombre et "always" force l’affichage de ce
nombre même s’il n’y pas de valeur manquante. Le paramètre
missing_text permets de personnaliser le texte affiché.

hdv2003 |>
tbl_summary(
include = c(age, [Link]),
missing = "always",
missing_text = "Nbre observations manquantes"
)

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Table 18.23: forcer l’affichage des valeurs manquantes

Caractéristique N = 2 000
age 48 (35 – 60)
Nbre observations manquantes 0
[Link] 2,00 (1,00 – 3,00)
Nbre observations manquantes 5

262
Caractéristique N = 2 000

Il est à noter, pour les variables catégorielles, que les va-


leurs manquantes ne sont jamais pris en compte pour le
calcul des pourcentages. Pour les inclure dans le calcul, il
faut les transformer en valeurs explicites, par exemple avec
forcats::fct_na_value_to_level() de {forcats}.

hdv2003 |>
dplyr::mutate(
[Link] = [Link] |>
forcats::fct_na_value_to_level("(non renseigné)")
) |>
tbl_summary(
include = c([Link], [Link])
)

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Table 18.24: valeurs manquantes explicites (variable catégo-


rielle)

Caractéristique N = 2 000
[Link]
Le plus important 29 (2,8%)
Aussi important que le reste 259 (25%)
Moins important que le reste 708 (68%)
Peu important 52 (5,0%)
Manquant 952
[Link]
Le plus important 29 (1,5%)
Aussi important que le reste 259 (13%)
Moins important que le reste 708 (35%)
Peu important 52 (2,6%)
(non renseigné) 952 (48%)

263
18.2.9 Ajouter les effectifs observés

Lorsque l’on masque les manquants, il peut être pertinent


d’ajouter une colonne avec les effectifs observés pour chaque
variable à l’aide de la fonction gtsummary::add_n().

hdv2003 |>
tbl_summary(
include = c([Link], [Link]),
missing = "no"
) |>
add_n()

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Table 18.25: ajouter une colonne avec les effectifs observés

Caractéristique N N = 2 000
[Link] 1 995 2,00 (1,00 – 3,00)
[Link] 1 048
Le plus important 29 (2,8%)
Aussi important que le reste 259 (25%)
Moins important que le reste 708 (68%)
Peu important 52 (5,0%)

18.3 Calcul manuel

18.3.1 Variable continue

R fournit de base toutes les fonctions nécessaires pour le calcul


des différentes statistiques descriptives :

• mean() pour la moyenne


• sd() pour l’écart-type
• min() et max() pour le minimum et le maximum

264
• range() pour l’étendue
• median() pour la médiane

Si la variable contient des valeurs manquantes (NA), ces fonc-


tions renverront une valeur manquante, sauf si on leur précise
[Link] = TRUE.

hdv2003$[Link] |> mean()

[1] NA

hdv2003$[Link] |> mean([Link] = TRUE)

[1] 2.246566

hdv2003$[Link] |> sd([Link] = TRUE)

[1] 1.775853

hdv2003$[Link] |> min([Link] = TRUE)

[1] 0

hdv2003$[Link] |> max([Link] = TRUE)

[1] 12

hdv2003$[Link] |> range([Link] = TRUE)

[1] 0 12

hdv2003$[Link] |> median([Link] = TRUE)

[1] 2

La fonction quantile() permets de calculer tous types de quan-


tiles.

265
hdv2003$[Link] |> quantile([Link] = TRUE)

0% 25% 50% 75% 100%


0 1 2 3 12

hdv2003$[Link] |>
quantile(
probs = c(.2, .4, .6, .8),
[Link] = TRUE
)

20% 40% 60% 80%


1 2 2 3

La fonction summary() renvoie la plupart de ces indicateurs en


une seule fois, ainsi que le nombre de valeurs manquantes.

hdv2003$[Link] |> summary()

Min. 1st Qu. Median Mean 3rd Qu. Max. NA's


0.000 1.000 2.000 2.247 3.000 12.000 5

18.3.2 Variable catégorielle

Les fonctions de base pour le calcul d’un tri à plat sont les
fonctions table() et xtabs(). Leur syntaxe est quelque peu
différente. On passe un vecteur entier à table() alors que la
syntaxe de xtabs() se rapproche de celle d’un modèle linéaire :
on décrit le tableau attendu à l’aide d’une formule et on indique
le tableau de données. Les deux fonctions renvoient le même
résultat.

tbl <- hdv2003$[Link] |> table()


tbl <- xtabs(~ [Link], data = hdv2003)
tbl <- hdv2003 |> xtabs(~ [Link], data = _)
tbl

266
[Link]
Le plus important Aussi important que le reste
29 259
Moins important que le reste Peu important
708 52

Comme on le voit, il s’agit du tableau brut des effectifs, sans


les valeurs manquantes, et pas vraiment lisible dans la console
de R.
Pour calculer les proportions, on appliquera [Link]() sur
la table des effectifs bruts.

[Link](tbl)

[Link]
Le plus important Aussi important que le reste
0.02767176 0.24713740
Moins important que le reste Peu important
0.67557252 0.04961832

Pour la réalisation rapide d’un tri à plat, on pourra donc pré-


férer la fonction questionr::freq() qui affiche également le
nombre de valeurs manquantes et les pourcentages, en un seul
appel.

hdv2003$[Link] |>
questionr::freq(total = TRUE)

n % val%
Le plus important 29 1.5 2.8
Aussi important que le reste 259 13.0 24.7
Moins important que le reste 708 35.4 67.6
Peu important 52 2.6 5.0
NA 952 47.6 NA
Total 2000 100.0 100.0

267
18.4 Intervalles de confiance

18.4.1 Avec gtsummary

La fonction gtsummary::add_ci() permet d’ajouter


des intervalles de confiance à un tableau créé avec
gtsummary::tbl_summary().

Á Avertissement

Par défaut, pour les variables continues,


gtsummary::tbl_summary() affiche la médiane tan-
dis que gtsummary::add_ci() calcule l’intervalle de
confiance d’une moyenne !
Il faut donc :

• soit afficher la moyenne dans


gtsummary::tbl_summary() à l’aide du para-
mètre statistic ;
• soit calculer les intervalles de confiance d’une mé-
diane (méthode "[Link]") via le paramètre
method de gtsummary::add_ci().

hdv2003 |>
tbl_summary(
include = c(age, [Link], [Link]),
statistic = age ~ "{mean} ({sd})"
) |>
add_ci(
method = [Link] ~ "[Link]"
)

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

268
Table 18.26: ajouter les intervalles de confiance

Caractéristique N = 2 000 95% CI


age 48 (17) 47, 49
[Link] 2,00 (1,00 – 3,00) 2,5, 2,5
Manquant 5
[Link]
Le plus important 29 (2,8%) 1,9%, 4,0%
Aussi important que le reste 259 (25%) 22%, 27%
Moins important que le reste 708 (68%) 65%, 70%
Peu important 52 (5,0%) 3,8%, 6,5%
Manquant 952

L’argument statistic permet de personnaliser la présentation


de l’intervalle ; [Link] de changer le niveau de confiance
et style_fun de modifier la mise en forme des nombres de
l’intervalle.

hdv2003 |>
tbl_summary(
include = c(age, [Link]),
statistic = ~ "{mean}"
) |>
add_ci(
statistic = ~ "entre {[Link]} et {[Link]}",
[Link] = .9,
style_fun = ~ purrr::partial(style_number, digits = 1)
)

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Table 18.27: des intervalles de confiance personnalisés

Caractéristique N = 2 000 90% CI


age 48 entre 47,5 et 48,8
[Link] 2,25 entre 2,2 et 2,3

269
Caractéristique N = 2 000 90% CI
Manquant 5

18.4.2 Calcul manuel

Le calcul de l’intervalle de confiance d’une moyenne s’effectue


avec la fonction [Link]().

hdv2003$age |> [Link]()

One Sample t-test

data: hdv2003$age
t = 127.12, df = 1999, p-value < 2.2e-16
alternative hypothesis: true mean is not equal to 0
95 percent confidence interval:
47.41406 48.89994
sample estimates:
mean of x
48.157

Le résultat renvoyé est une liste contenant de multiples infor-


mations.

hdv2003$age |> [Link]() |> str()

List of 10
$ statistic : Named num 127
..- attr(*, "names")= chr "t"
$ parameter : Named num 1999
..- attr(*, "names")= chr "df"
$ [Link] : num 0
$ [Link] : num [1:2] 47.4 48.9
..- attr(*, "[Link]")= num 0.95
$ estimate : Named num 48.2
..- attr(*, "names")= chr "mean of x"

270
$ [Link] : Named num 0
..- attr(*, "names")= chr "mean"
$ stderr : num 0.379
$ alternative: chr "[Link]"
$ method : chr "One Sample t-test"
$ [Link] : chr "hdv2003$age"
- attr(*, "class")= chr "htest"

Si l’on a besoin d’accéder spécifiquement à l’intervalle de


confiance calculé :

hdv2003$age |> [Link]() |> purrr::pluck("[Link]")

[1] 47.41406 48.89994


attr(,"[Link]")
[1] 0.95

Pour celui d’une médiane, on utilisera [Link]() en pré-


cisant [Link] = TRUE.

hdv2003$age |> [Link]([Link] = TRUE)

Wilcoxon signed rank test with continuity correction

data: hdv2003$age
V = 2001000, p-value < 2.2e-16
alternative hypothesis: true location is not equal to 0
95 percent confidence interval:
47.00001 48.50007
sample estimates:
(pseudo)median
47.99996

hdv2003$age |>
[Link]([Link] = TRUE) |>
purrr::pluck("[Link]")

271
[1] 47.00001 48.50007
attr(,"[Link]")
[1] 0.95

Pour une proportion, on utilisera [Link]() en lui trans-


mettant le nombre de succès et le nombre d’observations, qu’il
faudra donc avoir calculé au préalable. On peut également pas-
ser une table à deux entrées avec le nombre de succès puis le
nombre d’échecs.
Ainsi, pour obtenir l’intervalle de confiance de la proportion
des enquêtés qui considèrent leur travail comme peu important,
en tenant compte des valeurs manquantes, le plus simple est
d’effectuer le code suivant25 : 25
Notez l’utilisation de rev() pour
inverser le tableau créé avec xtabs()
xtabs(~ I(hdv2003$[Link] == "Peu important"), data =afinhdv2003)
que le nombre de succès (TRUE)
|>
soit indiqués avant le nombre d’échecs
rev() |> (FALSE).
[Link]()

1-sample proportions test with continuity correction

data: rev(xtabs(~I(hdv2003$[Link] == "Peu important"), data = hdv2003)), null probability 0.


X-squared = 848.52, df = 1, p-value < 2.2e-16
alternative hypothesis: true p is not equal to 0.5
95 percent confidence interval:
0.03762112 0.06502346
sample estimates:
p
0.04961832

Par défaut, [Link]() produit un intervalle de confiance bi-


latéral en utilisant la méthode de Wilson avec correction de
continuité. Pour plus d’information sur les différentes manières
de calculer l’intervalle de confiance d’une proportion, on pourra
se référer à ce billet de blog.

272
Ď Astuce

Comme on le voit, il n’est pas aisé, avec les fonctions de R


base de calculer les intervalles de confiance pour toutes
les modalités d’une variable catégorielle.
On pourra éventuellement avoir recours à la petite fonc-
tion suivante qui réalise le tri à plat d’une variable ca-
tégorielle, calcule les proportions et leurs intervalles de
confiance.

prop_ci <- function(x, [Link] = .95, correct = TRUE) {


tbl <- [Link](table(x), responseName = "n")
tbl$N <- sum(tbl$n)
tbl$prop <- tbl$n / tbl$N
tbl$[Link] <- NA_real_
tbl$[Link] <- NA_real_
for (i in 1:nrow(tbl)) {
test <- [Link](
x = tbl$n[i],
n = tbl$N[i],
[Link] = [Link],
correct = correct
)
tbl$[Link][i] <- test$[Link][1]
tbl$[Link][i] <- test$[Link][2]
}
tbl
}
prop_ci(hdv2003$[Link])

x n N prop [Link] [Link]


1 Le plus important 29 1048 0.02767176 0.01894147 0.04001505
2 Aussi important que le reste 259 1048 0.24713740 0.22151849 0.27463695
3 Moins important que le reste 708 1048 0.67557252 0.64614566 0.70369541
4 Peu important 52 1048 0.04961832 0.03762112 0.06502346

273
18.5 webin-R

La statistique univariée est présentée dans le webin-R #03


(statistiques descriptives avec gtsummary et esquisse) sur You-
Tube.
[Link]

274
19 Statistique bivariée & Tests
de comparaison

19.1 Deux variables catégorielles

19.1.1 Tableau croisé avec gtsummary

Pour regarder le lien entre deux variables catégorielles,


l’approche la plus fréquente consiste à réaliser un tableau
croisé, ce qui s’obtient très facilement avec l’argument by
de la fonction gtsummary::tbl_summary() que nous avons
déjà abordée dans le chapitre sur la statistique univariée (cf.
Section 18.2).
Prenons pour exemple le jeu de données gtsummary::trial et
croisons les variables stage et grade. On indique à by la variable
à représenter en colonnes et à include celle à représenter en
lignes.

library(gtsummary)
theme_gtsummary_language("fr", [Link] = ',')

Setting theme `language: fr`

trial |>
tbl_summary(
include = stage,
by = grade
)

275
Table printed with `knitr::kable()`, not {gt}. Learn why at
[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Table 19.1: un tableau croisé avec des pourcentages en colonne

Caractéristique I, N = 68 II, N = 68 III, N = 64


T Stage
T1 17 (25%) 23 (34%) 13 (20%)
T2 18 (26%) 17 (25%) 19 (30%)
T3 18 (26%) 11 (16%) 14 (22%)
T4 15 (22%) 17 (25%) 18 (28%)

Par défaut, les pourcentages affichés correspondent à des pour-


centages en colonne. On peut demander des pourcentages en
ligne avec percent = "row" ou des pourcentages du total avec
percent = "cell".
Il est possible de passer plusieurs variables à include mais
une seule variable peut être transmise à by. La fonction
gtsummary::add_overall() permet d’ajouter une colonne
totale. Comme pour un tri à plat, on peut personnaliser les
statistiques affichées avec statistic.

library(gtsummary)
trial |>
tbl_summary(
include = c(stage, trt),
by = grade,
statistic = ~ "{p}% ({n}/{N})",
percent = "row"
) |>
add_overall(last = TRUE)

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

276
Table 19.2: un tableau croisé avec des pourcentages en ligne

I, N = II, N = III, N = Total, N


Caractéristique 68 68 64 = 200
T Stage
T1 32% 43% 25% 100%
(17/53) (23/53) (13/53) (53/53)
T2 33% 31% 35% 100%
(18/54) (17/54) (19/54) (54/54)
T3 42% 26% 33% 100%
(18/43) (11/43) (14/43) (43/43)
T4 30% 34% 36% 100%
(15/50) (17/50) (18/50) (50/50)
Chemotherapy
Treatment
Drug A 36% 33% 32% 100%
(35/98) (32/98) (31/98) (98/98)
Drug B 32% 35% 32% 100%
(33/102) (36/102) (33/102) (102/102)

ĺ Important

Choisissez bien votre type de pourcentages (en lignes ou


en colonnes). Si d’un point de vue purement statistique, ils
permettent tous deux de décrire la relation entre les deux
variables, ils ne correspondent au même story telling. Tout
dépend donc du message que vous souhaitez faire passer,
de l’histoire que vous souhaitez raconter.

gtsummary::tbl_summary() est bien adaptée dans le cadre


d’une analyse de facteurs afin de représenter un outcome donné
avec by et une liste de facteurs avec include.
Lorsque l’on ne croise que deux variables et que l’on souhaite
un affichage un peu plus traditionnel d’un tableau croisé, on
peut utiliser gtsummary::tbl_cross() à laquelle on transmet-
tra une et une seule variable à row et une et une seule variable
à col. Pour afficher des pourcentages, il faudra indiquer le type
de pourcentages voulus avec percent.

277
trial |>
tbl_cross(
row = stage,
col = grade,
percent = "row"
)

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Table 19.3: un tableau croisé avec tbl_cross()

I II III Total
T Stage
T1 17 (32%) 23 (43%) 13 (25%) 53 (100%)
T2 18 (33%) 17 (31%) 19 (35%) 54 (100%)
T3 18 (42%) 11 (26%) 14 (33%) 43 (100%)
T4 15 (30%) 17 (34%) 18 (36%) 50 (100%)
Total 68 (34%) 68 (34%) 64 (32%) 200 (100%)

19.1.2 Représentations graphiques

La représentation graphique la plus commune pour le croise-


ment de deux variables catégorielles est le diagramme en barres,
que l’on réalise avec la géométrie ggplot2::geom_bar() et en
utilisant les esthétiques x et fill pour représenter les deux va-
riables.

library(ggplot2)
ggplot(trial) +
aes(x = stage, fill = grade) +
geom_bar() +
labs(x = "T Stage", fill = "Grade", y = "Effectifs")

278
40

Grade
Effectifs

I
II
20 III

0
T1 T2 T3 T4
T Stage

Figure 19.1: un graphique en barres croisant deux variables

On peut modifier la position des barres avec le paramètre


position.

library(ggplot2)
ggplot(trial) +
aes(x = stage, fill = grade) +
geom_bar(position = "dodge") +
labs(x = "T Stage", fill = "Grade", y = "Effectifs")

279
20

15 Grade
Effectifs

I
II
10
III

0
T1 T2 T3 T4
T Stage

Figure 19.2: un graphique avec des barres côte à côte

Pour des barres cumulées, on aura recours à position =


"fill". Pour que les étiquettes de l’axe des y soient repré-
sentées sous forme de pourcentages (i.e. 25% au lieu de 0.25),
on aura recours à la fonction scales::percent() qui sera
transmise à ggplot2::scale_y_continuous().

library(ggplot2)
ggplot(trial) +
aes(x = stage, fill = grade) +
geom_bar(position = "fill") +
labs(x = "T Stage", fill = "Grade", y = "Proportion") +
scale_y_continuous(labels = scales::percent)

280
100%

75%

Grade
Proportion

I
50%
II
III

25%

0%
T1 T2 T3 T4
T Stage

Figure 19.3: un graphique en barres cumulées

Ď Ajouter des étiquettes sur un diagramme en barres

Il est facile d’ajouter des étiquettes en ayant recours à


ggplot2::geom_text(), à condition de lui passer les bons
paramètres.
Tout d’abord, il faudra préciser stat = "count"
pour indiquer que l’on souhaite utiliser la statistique
ggplot2::stat_count() qui est celle utilisé par défaut
par ggplot2::geom_bar(). C’est elle qui permets de
compter le nombre d’observations.
Il faut ensuite utiliser l’esthétique label pour indiquer ce
que l’on souhaite afficher comme étiquettes. La fonction
after_stat(count) permet d’accéder à la variable count
calculée par ggplot2::stat_count().
Enfin, il faut indiquer la position verticale avec
ggplot2::position_stack(). En précisant un ajuste-
ment de vertical de 0.5, on indique que l’on souhaite po-
sitionner l’étiquette au milieu.

281
ggplot(trial) +
aes(
x = stage, fill = grade,
label = after_stat(count)
) +
geom_bar() +
geom_text(
stat = "count",
position = position_stack(.5)
)

17 18
15
40

18 grade
count

I
17 17
23 II
20 11 III

19 18
13 14

0
T1 T2 T3 T4
stage

Pour un graphique en barres cumulées, on peut uti-


liser de manière similaire ggplot2::position_fill().
On ne peut afficher directement les proportions avec
ggplot2::stat_count(). Cependant, nous pouvons avoir
recours à ggstats::stat_prop(), déjà évoquée dans le
chapitre sur la statistique univariée (cf. Section 18.1.2) et
dont le dénominateur doit être précisé via l’esthétique by.

282
library(ggstats)
ggplot(trial) +
aes(
x = stage,
fill = grade,
by = stage,
label = scales::percent(after_stat(prop), accuracy = .1)
) +
geom_bar(position = "fill") +
geom_text(
stat = "prop",
position = position_fill(.5)
) +
scale_y_continuous(labels = scales::percent)

100%

32.1% 33.3% 30.0%


41.9%
75%

grade
count

34.0% I
50% 31.5%
43.4% 25.6% II
III

25%
35.2% 32.6% 36.0%
24.5%

0%
T1 T2 T3 T4
stage

On peut aussi comparer facilement deux distributions, ici


la proportion de chaque niveau de qualification au sein
chaque sexe.

283
p <- ggplot(trial) +
aes(
x = stage,
y = after_stat(prop),
fill = grade,
by = grade,
label = scales::percent(after_stat(prop), accuracy = 1)
) +
geom_bar(
stat = "prop",
position = position_dodge(.9)
) +
geom_text(
aes(y = after_stat(prop) - 0.01),
stat = "prop",
position = position_dodge(.9),
vjust = "top"
) +
scale_y_continuous(labels = scales::percent)
p

34%
30%
30%
28%
26% 26%
25% 25% 25%
grade
20% 22% 22%
20% I
prop

II
16%
III
10%

0%
T1 T2 T3 T4
stage

Il est possible d’alléger le graphique en retirant des élé-


ments superflus.

284
p +
theme_light() +
xlab("") +
ylab("") +
labs(fill = "") +
ggtitle("Distribution selon le niveau, par grade") +
theme(
[Link] = element_blank(),
[Link] = element_blank(),
[Link].y = element_blank(),
[Link] = element_blank(),
[Link] = "top"
) +
scale_fill_brewer()

Distribution selon le niveau, par grade


I II III

34%
30%
28%
26% 26%
25% 25% 25%
22% 22%
20%
16%

T1 T2 T3 T4

Pour visualiser chaque étape du code, vous pouvez consulter


le diaporama suivant : [Link]
R/analyses/ressources/flipbook-geom_bar-[Link]

19.1.3 Calcul manuel

Les deux fonctions de base permettant le calcul d’un tri à plat


sont table() et xtabs() (cf. Section 18.3.2). Ces mêmes fonc-
tions permettent le calcul du tri croisé de deux variables (ou

285
plus). Pour table(), on passera les deux vecteurs à croisés,
tandis que pour xtabs() on décrira le tableau attendu à l’aide
d’une formule.

table(trial$stage, trial$grade)

I II III
T1 17 23 13
T2 18 17 19
T3 18 11 14
T4 15 17 18

tab <- xtabs(~ stage + grade, data = trial)


tab

grade
stage I II III
T1 17 23 13
T2 18 17 19
T3 18 11 14
T4 15 17 18

Le tableau obtenu est basique et ne contient que les effectifs. La


fonction addmargins() permet d’ajouter les totaux par ligne et
par colonne.

tab |> addmargins()

grade
stage I II III Sum
T1 17 23 13 53
T2 18 17 19 54
T3 18 11 14 43
T4 15 17 18 50
Sum 68 68 64 200

286
Pour le calcul des pourcentages, le plus simple est d’avoir
recours au package {questionr} qui fournit les fonc-
tions questionr::cprop(), questionr::rprop() et
questionr::prop() qui permettent de calculer, respecti-
vement, les pourcentages en colonne, en ligne et totaux.

questionr::cprop(tab)

grade
stage I II III Ensemble
T1 25.0 33.8 20.3 26.5
T2 26.5 25.0 29.7 27.0
T3 26.5 16.2 21.9 21.5
T4 22.1 25.0 28.1 25.0
Total 100.0 100.0 100.0 100.0

questionr::rprop(tab)

grade
stage I II III Total
T1 32.1 43.4 24.5 100.0
T2 33.3 31.5 35.2 100.0
T3 41.9 25.6 32.6 100.0
T4 30.0 34.0 36.0 100.0
Ensemble 34.0 34.0 32.0 100.0

questionr::prop(tab)

grade
stage I II III Total
T1 8.5 11.5 6.5 26.5
T2 9.0 8.5 9.5 27.0
T3 9.0 5.5 7.0 21.5
T4 7.5 8.5 9.0 25.0
Total 34.0 34.0 32.0 100.0

287
19.1.4 Test du Chi² et dérivés

Dans le cadre d’un tableau croisé, on peut tester l’existence


d’un lien entre les modalités de deux variables, avec le très
classique test du Chi² (parfois écrit �² ou Chi²). Pour une pré-
sentation plus détaillée du test, on pourra se référer à ce cours
de Julien Barnier.
Le test du Chi² peut se calculer très facilement avec la fonction
[Link]() appliquée au tableau obtenu avec table() ou
xtabs().

tab <- xtabs(~ stage + grade, data = trial)


tab

grade
stage I II III
T1 17 23 13
T2 18 17 19
T3 18 11 14
T4 15 17 18

[Link](tab)

Pearson's Chi-squared test

data: tab
X-squared = 4.8049, df = 6, p-value = 0.5691

Si l’on est adepte de {gtsummary}, il suffit d’appliquer


gtsummary::add_p() au tableau produit avec gtsummary::tbl_summary().

trial |>
tbl_summary(
include = stage,
by = grade
) |>
add_p()

288
Table printed with `knitr::kable()`, not {gt}. Learn why at
[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Table 19.4: un tableau croisé avec test du khi²

II, N = III, N = p-
Caractéristique
I, N = 68 68 64 valeur
T Stage 0,6
T1 17 (25%) 23 (34%) 13 (20%)
T2 18 (26%) 17 (25%) 19 (30%)
T3 18 (26%) 11 (16%) 14 (22%)
T4 15 (22%) 17 (25%) 18 (28%)

Dans notre exemple, les deux variables stage et grade ne sont


clairement pas corrélées.
Un test alternatif est le test exact de Fisher. Il s’obtient
aisément avec [Link]() ou bien en le spécifiant via
l’argument test de gtsummary::add_p().

tab <- xtabs(~ stage + grade, data = trial)


[Link](tab)

Fisher's Exact Test for Count Data

data: tab
p-value = 0.5801
alternative hypothesis: [Link]

trial |>
tbl_summary(
include = stage,
by = grade
) |>
add_p(test = all_categorical() ~ "[Link]")

289
Table printed with `knitr::kable()`, not {gt}. Learn why at
[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Table 19.5: un tableau croisé avec test exact de Fisher

II, N = III, N = p-
Caractéristique
I, N = 68 68 64 valeur
T Stage 0,6
T1 17 (25%) 23 (34%) 13 (20%)
T2 18 (26%) 17 (25%) 19 (30%)
T3 18 (26%) 11 (16%) 14 (22%)
T4 15 (22%) 17 (25%) 18 (28%)

Ĺ Note

Formellement, le test de Fisher suppose que les marges du


tableau (totaux lignes et colonnes) sont fixées, puisqu’il
repose sur une loi hypergéométrique, et donc celui-ci se
prête plus au cas des situations expérimentales (plans
d’expérience, essais cliniques) qu’au cas des données tirées
d’études observationnelles.
En pratique, le test du Chi² étant assez robuste quant aux
déviations par rapport aux hypothèses d’applications du
test (effectifs théoriques supérieurs ou égaux à 5), le test
de Fisher présente en général peu d’intérêt dans le cas de
l’analyse des tableaux de contingence.

19.1.5 Comparaison de deux proportions

Pour comparer deux proportions, la fonction de base est


[Link]() à laquelle on passera un tableau à 2×2 dimen-
sions.

tab <- xtabs(~ I(stage == "T1") + trt, data = trial)


tab |> questionr::cprop()

290
trt
I(stage == "T1") Drug A Drug B Ensemble
FALSE 71.4 75.5 73.5
TRUE 28.6 24.5 26.5
Total 100.0 100.0 100.0

tab |> [Link]()

2-sample test for equality of proportions with continuity correction

data: tab
X-squared = 0.24047, df = 1, p-value = 0.6239
alternative hypothesis: [Link]
95 percent confidence interval:
-0.2217278 0.1175050
sample estimates:
prop 1 prop 2
0.4761905 0.5283019

Il est également envisageable d’avoir recours à un test exact de


Fisher. Dans le cas d’un tableau à 2×2 dimensions, le test exact
de Fisher ne teste pas si les deux proportions sont différents,
mais plutôt si leur odds ratio (qui est d’ailleurs renvoyé par la
fonction) est différent de 1.

[Link](tab)

Fisher's Exact Test for Count Data

data: tab
p-value = 0.5263
alternative hypothesis: true odds ratio is not equal to 1
95 percent confidence interval:
0.4115109 1.5973635
sample estimates:
odds ratio
0.8125409

291
Mais le plus simple reste encore d’avoir recours à {gtsummary}
et à sa fonction gtsummary::add_difference() que l’on peut
appliquer à un tableau où le paramètre by n’a que deux moda-
lités. Pour la différence de proportions, il faut que les variables
transmises à include soit dichotomiques.

trial |>
tbl_summary(
by = trt,
include = response
) |>
add_difference()

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Table 19.6: différence entre deux proportions

Drug A, Drug B, 95% p-


Caractéristique
N = 98 N = 102 Difference IC valeur
Tumor 28 (29%) 33 (34%) -4,2% -18% 0,6
Response – 9,9%
Manquant 3 4

Attention : si l’on passe une variable catégorielle à trois mo-


dalités ou plus, c’est la différence des moyennes standardisées
(globale pour la variable) qui sera calculée et non la différence
des proportions dans chaque groupe.

trial |>
tbl_summary(
by = trt,
include = grade
) |>
add_difference()

Table printed with `knitr::kable()`, not {gt}. Learn why at

292
[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Table 19.7: différence moyenne standardisée

Drug A, Drug B, N 95%


Caractéristique
N = 98 = 102 Difference IC
Grade 0,07 -0,20 –
0,35
I 35 (36%) 33 (32%)
II 32 (33%) 36 (35%)
III 31 (32%) 33 (32%)

Pour calculer la différence des proportions pour chaque mo-


dalité de grade, il est nécessaire de transformer, en amont, la
variable catégorielle grade en trois variables dichotomiques (de
type oui/non, une par modalité), ce qui peut se faire facilement
avec la fonction fastDummies::dummy_cols() de l’extension
{fastDummies}.

trial |>
fastDummies::dummy_cols("grade") |>
tbl_summary(
by = trt,
include = starts_with("grade_"),
digits = ~ c(0, 1)
) |>
add_difference()

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

293
Table 19.8: différence entre proportions avec création de va-
riables dichotomiques

Drug A, Drug B, 95% p-


Caractéristique
N = 98 N = 102 Difference IC valeur
grade_I 35 33 3,4% -11% 0,7
(35,7%) (32,4%) – 17%
grade_II 32 36 -2,6% -17% 0,8
(32,7%) (35,3%) – 11%
grade_III 31 33 -0,72% -14% >0,9
(31,6%) (32,4%) – 13%

19.2 Une variable continue selon une


variable catégorielle

19.2.1 Tableau comparatif avec gtsummary

Dans le chapitre sur la statistique univariée (cf. Section 18.2),


nous avons abordé comment afficher les statistiques descrip-
tives d’une variable continue avec gtsummary::tbl_summary().
Pour comparer une variable continue selon plusieurs groupes dé-
finis par une variable catégorielle, il suffit d’utiliser le paramètre
by :

trial |>
tbl_summary(
include = age,
by = grade
)

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

294
Table 19.9: âge médian et intervalle interquartile selon le grade

Caractéristique I, N = 68 II, N = 68 III, N = 64


Age 47 (37 – 56) 49 (37 – 57) 47 (38 – 58)
Manquant 2 6 3

La fonction gtsummary::add_overall() permet d’ajouter


une colonne total et gtsummary::modify_spanning_header()
peut-être utilisé pour ajouter un en-tête de colonne.

trial |>
tbl_summary(
include = age,
by = grade
) |>
add_overall(last = TRUE) |>
modify_spanning_header(
all_stat_cols(stat_0 = FALSE) ~ "**Grade**"
)

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Table 19.10: âge médian et intervalle interquartile selon le


grade

I, N = II, N = III, N = Total, N =


Caractéristique 68 68 64 200
Age 47 (37 – 49 (37 – 47 (38 – 47 (38 – 57)
56) 57) 58)
Manquant 2 6 3 11

Comme pour un tri à plat, on peut personnaliser les statistiques


à afficher avec statistic.

295
trial |>
tbl_summary(
include = age,
by = grade,
statistic = all_continuous() ~ "{mean} ({sd})",
digits = all_continuous() ~ c(1, 1)
) |>
add_overall(last = TRUE)

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Table 19.11: âge moyen et écart-type selon le grade

I, N = II, N = III, N = Total, N =


Caractéristique 68 68 64 200
Age 46,2 47,5 48,1 47,2 (14,3)
(15,2) (13,7) (14,1)
Manquant 2 6 3 11

19.2.2 Représentations graphiques

La moyenne ou la médiane sont des indicateurs centraux et ne


suffisent pas à rendre compte des différences de distribution
d’une variable continue entre plusieurs sous-groupes.
Une représentation usuelle pour comparer deux distributions
consiste à avoir recours à des boîtes à moustaches que l’on ob-
tient avec ggplot2::geom_boxplot().

ggplot(trial) +
aes(x = grade, y = age) +
geom_boxplot(fill = "lightblue") +
theme_light()

296
80

60
age

40

20

I II III
grade

Figure 19.4: boîtes à moustache

Ď Astuce

Le trait central représente la médiane, le rectangle est dé-


limité par le premier et le troisième quartiles (i.e. le 25e
et le 75e percentiles). Les traits verticaux vont jusqu’aux
extrêmes (minimum et maximum) ou jusqu’à 1,5 fois
l’intervalle interquartile. Si des points sont situés à plus
d’1,5 fois l’intervalle interquartile au-dessus du 3e quartile
ou en-dessous du 1er quartile, ils sont considérés comme
des valeurs atypiques et représentés par un point. Dans
l’exemple précédent, c’est le cas des deux plus petites va-
leurs observées pour le grade I.

Alternativement, on peut utiliser un graphique en violons qui


représentent des courbes de densité dessinées en miroir.

ggplot(trial) +
aes(x = grade, y = age) +
geom_violin(fill = "lightblue") +
theme_light()

297
80

60
age

40

20

I II III
grade

Figure 19.5: graphique en violons

Il est toujours possible de représenter les observations indivi-


duelles sous la forme d’un nuage de points. Le paramètre alpha
permet de rendre les points transparents afin de mieux visuali-
ser les superpositions de points.

ggplot(trial) +
aes(x = grade, y = age) +
geom_point(alpha = .25, colour = "blue") +
theme_light()

298
80

60
age

40

20

I II III
grade

Figure 19.6: un nuage de points avec une variable continue et


une variable catégorielle

Comme la variable grade est catégorielle, tous les points


d’une même modalité sont représentées sur une même ligne.
La représentation peut être améliorée en ajoutant un dé-
calage aléatoire sur l’axe horizontal. Cela s’obtient avec
ggplot2::position_jitter() en précisant height = 0 pour
ne pas ajouter de décalage vertical et width = .2 pour décaler
horizontalement les points entre -20% et +20%.

ggplot(trial) +
aes(x = grade, y = age) +
geom_point(
alpha = .25,
colour = "blue",
position = position_jitter(height = 0, width = .2)
) +
theme_light()

299
80

60
age

40

20

I II III
grade

Figure 19.7: un nuage de points avec une variable continue et


une variable catégorielle et avec un décalage hori-
zontal aléatoire

La statistique ggstats::stat_weighted_mean() de
{ggstats} permets de calculer à la volée la moyenne du
nuage de points.

ggplot(trial) +
aes(x = grade, y = age) +
geom_point(stat = "weighted_mean", colour = "blue") +
theme_light()

300
48.0

47.5
age

47.0

46.5

I II III
grade

Figure 19.8: âge moyen selon le grade

Cela peut être utile pour effectuer des comparaisons mul-


tiples.

ggplot(trial) +
aes(x = grade, y = age, colour = stage, group = stage) +
geom_line(stat = "weighted_mean") +
geom_point(stat = "weighted_mean") +
facet_grid(cols = vars(trt)) +
theme_light()

301
Drug A Drug B

55

stage
T1
50
age

T2
T3
T4
45

I II III I II III
grade

Figure 19.9: âge moyen selon le grade, par traitement et état


d’avancement de la maladie

19.2.3 Calcul manuel

Le plus simple pour calculer des indicateurs par sous-


groupe est d’avoir recours à dplyr::summarise() avec
dplyr::group_by().

library(dplyr)
trial |>
group_by(grade) |>
summarise(
age_moy = mean(age, [Link] = TRUE),
age_med = median(age, [Link] = TRUE)
)

# A tibble: 3 x 3
grade age_moy age_med
<fct> <dbl> <dbl>
1 I 46.2 47
2 II 47.5 48.5
3 III 48.1 47

302
En base R, on peut avoir recours à tapply(). On lui indique
d’abord le vecteur sur lequel on souhaite réaliser le calcul, puis
un facteur qui indiquera les sous-groupes, puis une fonction
qui sera appliquée à chaque sous-groupe et enfin, optionnelle-
ment, des arguments additionnels qui seront transmis à cette
fonction.

tapply(trial$age, trial$grade, mean, [Link] = TRUE)

I II III
46.15152 47.53226 48.11475

19.2.4 Tests de comparaison

Pour comparer des moyennes ou des médianes, le plus facile


est encore d’avoir recours à {gtsummary} et sa fonction
gtsummary::add_p().

trial |>
tbl_summary(
include = age,
by = grade
) |>
add_p()

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Table 19.12: test de comparaison sur la somme des rangs

II, N = III, N = p-
Caractéristique
I, N = 68 68 64 valeur
Age 47 (37 – 49 (37 – 47 (38 – 0,8
56) 57) 58)
Manquant 2 6 3

303
Par défaut, pour les variables continues, un test de Kruskal-
Wallis calculé avec la fonction stats::[Link]() est uti-
lisé lorsqu’il y a trois groupes ou plus, et un test de Wilcoxon-
Mann-Whitney calculé avec stats::[Link]() (test de
comparaison des rangs) lorsqu’il n’y a que deux groupes. Au
sens strict, il ne s’agit pas de tests de comparaison des mé-
dianes mais de tests sur la somme des rangs26 . En pratique, ces 26
Si l’on a besoin spécifique-
tests sont appropriés lorsque l’on présente les médianes et les ment d’un test de comparaison
des médianes, il existe le test
intervalles inter-quartiles.
de Brown-Mood disponible dans
Si l’on affiche des moyennes, il serait plus juste d’utiliser un le package {coin} avec la fonc-
tion coin::median_test(). Atten-
test t de Student (test de comparaison des moyennes) calculé tion, il ne faut pas confondre ce
avec stats::[Link](), valable seulement si l’on compare deux test avec le test de dispersion de
moyennes. Pour tester si trois moyennes ou plus sont égales, on Mood implémenté dans la fonction
aura plutôt recours à stats::[Link](). stats::[Link]().

On peut indiquer à gtsummary::add_p() le test à utiliser avec


le paramètre test.

trial |>
tbl_summary(
include = age,
by = grade,
statistic = all_continuous() ~ "{mean} ({sd})"
) |>
add_p(
test = all_continuous() ~ "[Link]"
)

Multiple parameters; naming those columns [Link], [Link]

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

304
Table 19.13: test de comparaison des moyennes

II, N = III, N = p-
Caractéristique
I, N = 68 68 64 valeur
Age 46 (15) 48 (14) 48 (14) 0,7
Manquant 2 6 3

ĺ Précision statistique

Classiquement, le test t de Student présuppose l’égalité


des variances entre les deux sous-groupes, ce qui permet
de former une estimation commune de la variance des deux
échantillons (on parle de pooled variance), qui revient à
une moyenne pondérée des variances estimées à partir des
deux échantillons. Pour tester l’égalité des variances de
deux échantillons, on peut utiliser stats::[Link]().
Dans le cas où l’on souhaite relaxer cette hypothèse
d’égalité des variances, le test de Welch ou la correction de
Satterthwaite reposent sur l’idée que l’on utilise les deux
estimations de variance séparément, suivie d’une approxi-
mation des degrés de liberté pour la somme de ces deux
variances.
Par défaut, la fonction stats::[Link]() réalise un test
de Welch. Pour un test classique de Student, il faut lui
préciser [Link] = TRUE.
De manière similaire, stats::[Link]() ne présup-
pose pas, par défaut, l’égalité des variances et généra-
lise donc le test de Welch au cas à trois modalités ou
plus. Cependant, on peut là encore indiquer [Link]
= TRUE, auquel cas une analyse de variance (ANOVA)
classique sera réalisée, que l’on peut aussi obtenir avec
stats::aov().
Il est possible d’indiquer à gtsummary::add_p() des ar-
guments additionnels à passer à la fonction utilisée pour
réaliser le test :

305
trial |>
tbl_summary(
include = age,
by = trt,
statistic = all_continuous() ~ "{mean} ({sd})"
) |>
add_p(
test = all_continuous() ~ "[Link]",
[Link] = all_continuous() ~ list([Link] = TRUE)
)

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Drug A, N Drug B, N p-
Caractéristique = 98 = 102 valeur
Age 47 (15) 47 (14) 0,8
Manquant 7 4

19.2.5 Différence de deux moyennes

La fonctions gtsummary::add_difference() permet, pour


une variable continue et si la variable catégorielle spécifiée via
by n’a que deux modalités, de calculer la différence des deux
moyennes, l’intervalle de confiance de cette différence et test
si cette différence est significativement différente de 0 avec
stats::[Link]().

trial |>
tbl_summary(
include = age,
by = trt,
statistic = all_continuous() ~ "{mean} ({sd})"
) |>
add_difference()

306
Table printed with `knitr::kable()`, not {gt}. Learn why at
[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Table 19.15: différence de deux moyennes

Drug A, Drug B, 95% p-


Caractéristique
N = 98 N = 102 Difference IC valeur
Age 47 (15) 47 (14) -0,44 -4,6 – 0,8
3,7
Manquant 7 4

19.3 Deux variables continues

19.3.1 Représentations graphiques

La comparaison de deux variables continues se fait en pre-


mier lieu graphique, en représentant, via un nuage de points,
l’ensemble des couples de valeurs. Notez ici l’application d’un
niveau de transparence (alpha) afin de faciliter la lecture des
points superposés.

ggplot(iris) +
aes(x = [Link], y = [Link]) +
geom_point(colour = "blue", alpha = .25) +
theme_light()

307
2.5

2.0
[Link]

1.5

1.0

0.5

0.0
2 4 6
[Link]

Figure 19.10: nuage de points

La géométrie ggplot2::geom_smooth() permets d’ajouter


une courbe de tendance au graphique, avec son intervalle de
confiance. Par défaut, il s’agit d’une régression polynomiale
locale obtenue avec stats::loess().

ggplot(iris) +
aes(x = [Link], y = [Link]) +
geom_smooth() +
geom_point(colour = "blue", alpha = .25) +
theme_light()

308
2.5

2.0
[Link]

1.5

1.0

0.5

0.0

2 4 6
[Link]

Figure 19.11: nuage de points avec une courbe de tendance

Pour afficher plutôt la droite de régression linéaire entre les


deux variables, on précisera method = "lm".

ggplot(iris) +
aes(x = [Link], y = [Link]) +
geom_smooth(method = "lm") +
geom_point(colour = "blue", alpha = .25) +
theme_light()

309
2
[Link]

2 4 6
[Link]

Figure 19.12: nuage de points avec droite de régression linéaire

Ď Astuce pour afficher l’intercept

Supposons que nous souhaitions montrer l’endroit où la


droite de régression coupe l’axe des ordonnées (soit le
point sur l’axe y pour x = 0).
Nous pouvons étendre la surface du graphique avec
ggplot2::expand_limits(). Cependant, cela n’étend
pas pour autant la droite de régression.

ggplot(iris) +
aes(x = [Link], y = [Link]) +
geom_smooth(method = "lm") +
geom_point(colour = "blue", alpha = .25) +
theme_light() +
expand_limits(x = 0, y = -0.5)

`geom_smooth()` using formula = 'y ~ x'

310
2
[Link]

0 2 4 6
[Link]

Une solution simple consiste à utiliser l’option fullrange


= TRUE dans ggplot2::geom_smooth() pour étendre la
droite de régression à l’ensemble du graphique.

ggplot(iris) +
aes(x = [Link], y = [Link]) +
geom_smooth(method = "lm", fullrange = TRUE) +
geom_point(colour = "blue", alpha = .25) +
theme_light() +
expand_limits(x = 0, y = -0.5)

`geom_smooth()` using formula = 'y ~ x'

311
2
[Link]

0 2 4 6
[Link]

On peut contrôler plus finement la partie de droite à affi-


cher avec l’argument xseq (liste des valeurs pour lesquelles
on prédit et affiche le lissage). On peut coupler deux ap-
pels à ggplot2::geom_smooth() pour afficher l’extension
de la droite vers la gauche en pointillés. L’option se =
FALSE permet de ne pas calculer d’intervalles de confiance
pour ce second appel.

ggplot(iris) +
aes(x = [Link], y = [Link]) +
geom_smooth(
method = "lm",
xseq = seq(0, 1, by = .1),
linetype = "dotted",
se = FALSE
) +
geom_smooth(method = "lm") +
geom_point(colour = "blue", alpha = .25) +
theme_light() +
expand_limits(x = 0, y = -0.5)

`geom_smooth()` using formula = 'y ~ x'


`geom_smooth()` using formula = 'y ~ x'

312
2
[Link]

0 2 4 6
[Link]

La géométrie ggplot2::geom_rug() permet d’afficher une re-


présentation synthétique de la densité de chaque variable sur
les deux axes.

ggplot(iris) +
aes(x = [Link], y = [Link]) +
geom_smooth(method = "lm") +
geom_point(colour = "blue", alpha = .25) +
geom_rug() +
theme_light()

313
2
[Link]

2 4 6
[Link]

Figure 19.13: nuage de points avec représentation synthétique


des densités marginales

19.3.2 Tester la relation entre les deux variables

Si l’on a besoin de calculer le coefficient de corrélation de Pear-


son entre deux variables, on aura recours à stats::cor().

cor(iris$[Link], iris$[Link])

[1] 0.9628654

Pour aller plus loin, on peut calculer une régression linéaire


entre les deux variables avec stats::lm().

m <- lm([Link] ~ [Link], data = iris)


summary(m)

Call:
lm(formula = [Link] ~ [Link], data = iris)

Residuals:

314
Min 1Q Median 3Q Max
-1.33542 -0.30347 -0.02955 0.25776 1.39453

Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 1.08356 0.07297 14.85 <2e-16 ***
[Link] 2.22994 0.05140 43.39 <2e-16 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.4782 on 148 degrees of freedom


Multiple R-squared: 0.9271, Adjusted R-squared: 0.9266
F-statistic: 1882 on 1 and 148 DF, p-value: < 2.2e-16

Les résultats montrent une corrélation positive et significative


entre les deux variables.
Pour une présentation propre des résultats de la régression
linéaire, on utilisera gtsummary::tbl_regression(). La
fonction gtsummary::add_glance_source_note() permet
d’ajouter différentes statistiques en notes du tableau de
résultats.

m |>
tbl_regression() |>
add_glance_source_note()

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Caractéristique Beta 95% IC p-valeur


[Link] 2,2 2,1 – 2,3 <0,001

19.4 Matrice de corrélations

Le package {GGally} et sa fonction GGally::ggpairs() per-


mettent de représenter facilement une matrice de corrélation

315
entre plusieurs variables, tant quantitatives que qualitatives.

library(GGally)
ggpairs(iris)

[Link] [Link] [Link] [Link] Species

[Link]
0.4
0.3 Corr: Corr: Corr:
0.2
0.1 −0.118 0.872*** 0.818***
0.0

[Link]
4.5
4.0 Corr: Corr:
3.5
3.0
2.5 −0.428*** −0.366***
2.0

[Link]
6
Corr:
4
2 0.963***

[Link] Species
2.5
2.0
1.5
1.0
0.5
0.0
7.5
5.0
2.5
0.0
7.5
5.0
2.5
0.0
7.5
5.0
2.5
0.0
5 6 7 8 [Link].54.04.5 2 4 6 [Link].52.02.5 setosa
versicolor
virginica

Figure 19.14: une matrice de corrélation avec ggpairs()

GGally::ggpairs() et sa petite sœur GGally::ggduo()


offrent de nombreuses options de personnalisation qui sont
détaillées sur le site dédié du package.

ggpairs(trial, mapping = aes(colour = trt))

316
trt age marker stage grade response death ttdeath
100

75

trt
50

25

80
Corr: −0.003 Corr: 0.124. Corr: 0.076 Corr: −0.051

60

Drug A: 0.054 Drug A: 0.134 Drug A: 0.146 Drug A: −0.096

age
40

20 Drug B: −0.053 Drug B: 0.115 Drug B: 0.004 Drug B: −0.009

4
Corr: 0.123. Corr: −0.048 Corr: 0.083
3

marker
2 Drug A: 0.106 Drug A: 0.146 Drug A: −0.061

1
Drug B: 0.155 Drug B: −0.230* Drug B: 0.191.
0
30
20
10
0
30
20
10

stage
0
30
20
10
0
30
20
10
0
30
20
10
0
30

grade
20
10
0
30
20
10
0
1.00
Corr: −0.220** Corr: 0.204**
0.75

response
0.50
Drug A: −0.113 Drug A: 0.086

0.25
Drug B: −0.331*** Drug B: 0.317**
0.00

1.00
Corr: −0.737***
0.75

death
0.50
Drug A: −0.714***

0.25
Drug B: −0.759***
0.00
25

20
ttdeath
15

10

0 10 20 30 40 500 10 20 30 40 50 20 40 60 80 0 1 2 3 4 0 1020300 1020300 1020300 102030 0 102030 0 102030 0 102030 0.00 0.25 0.50 0.75 1.000.00 0.25 0.50 0.75 1.00 5 10 15 20 25

Figure 19.15: un second example de matrice de corrélation

19.5 webin-R

La statistique univariée est présentée dans le webin-R #03


(statistiques descriptives avec gtsummary et esquisse) sur You-
Tube.
[Link]

317
20 Échelles de Likert

Les échelles de Likert tirent leur nom du psychologue américain


Rensis Likert qui les a développées. Elles sont le plus souvent
utilisées pour des variables d’opinion. Elles sont codées sous
forme de variable catégorielle et chaque item est codé selon
une graduation comprenant en général cinq ou sept choix de
réponse, par exemple : Tout à fait d’accord, D’accord, Ni en
désaccord ni d’accord, Pas d’accord, Pas du tout d’accord.
Pour les échelles à nombre impair de choix, le niveau central
permet d’exprimer une absence d’avis, ce qui rend inutile une
modalité « Ne sait pas ». Les échelles à nombre pair de moda-
lités voient l’omission de la modalité neutre et sont dites « à
choix forcé ».

20.1 Exemple de données

Générons un jeu de données qui nous servira pour les différents


exemples.

library(tidyverse)

-- Attaching core tidyverse packages ------------------------ tidyverse 2.0.0 --


v dplyr 1.1.2 v readr 2.1.4
v forcats 1.0.0 v stringr 1.5.0
v ggplot2 3.4.3 v tibble 3.2.1
v lubridate 1.9.2 v tidyr 1.3.0
v purrr 1.0.1
-- Conflicts ------------------------------------------ tidyverse_conflicts() --
x dplyr::filter() masks stats::filter()
x dplyr::lag() masks stats::lag()
i Use the conflicted package (<[Link] to force all conflicts to become

318
library(labelled)
niveaux <- c(
"Pas du tout d'accord",
"Plutôt pas d'accord",
"Ni d'accord, ni pas d'accord",
"Plutôt d'accord",
"Tout à fait d'accord"
)
[Link](42)
df <-
tibble(
groupe = sample(c("A", "B"), 150, replace = TRUE),
q1 = sample(niveaux, 150, replace = TRUE),
q2 = sample(niveaux, 150, replace = TRUE, prob = 5:1),
q3 = sample(niveaux, 150, replace = TRUE, prob = 1:5),
q4 = sample(niveaux, 150, replace = TRUE, prob = 1:5),
q5 = sample(c(niveaux, NA), 150, replace = TRUE),
q6 = sample(niveaux, 150, replace = TRUE, prob = c(1, 0, 1, 1, 0))
) |>
mutate(across(q1:q6, ~ factor(.x, levels = niveaux))) |>
set_variable_labels(
q1 = "Première question",
q2 = "Seconde question",
q3 = "Troisième question",
q4 = "Quatrième question",
q5 = "Cinquième question",
q6 = "Sixième question"
)

20.2 Tableau de fréquence

On peut tout à fait réaliser un tableau de fréquence classique


avec gtsummary::tbl_summary().

library(gtsummary)
df |>
tbl_summary(include = q1:q6)

319
Table printed with `knitr::kable()`, not {gt}. Learn why at
[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Characteristic N = 150
Première question
Pas du tout d’accord 39 (26%)
Plutôt pas d’accord 32 (21%)
Ni d’accord, ni pas d’accord 25 (17%)
Plutôt d’accord 30 (20%)
Tout à fait d’accord 24 (16%)
Seconde question
Pas du tout d’accord 56 (37%)
Plutôt pas d’accord 44 (29%)
Ni d’accord, ni pas d’accord 19 (13%)
Plutôt d’accord 26 (17%)
Tout à fait d’accord 5 (3.3%)
Troisième question
Pas du tout d’accord 8 (5.3%)
Plutôt pas d’accord 17 (11%)
Ni d’accord, ni pas d’accord 29 (19%)
Plutôt d’accord 43 (29%)
Tout à fait d’accord 53 (35%)
Quatrième question
Pas du tout d’accord 11 (7.3%)
Plutôt pas d’accord 19 (13%)
Ni d’accord, ni pas d’accord 31 (21%)
Plutôt d’accord 40 (27%)
Tout à fait d’accord 49 (33%)
Cinquième question
Pas du tout d’accord 33 (26%)
Plutôt pas d’accord 25 (20%)
Ni d’accord, ni pas d’accord 28 (22%)
Plutôt d’accord 25 (20%)
Tout à fait d’accord 16 (13%)
Unknown 23
Sixième question
Pas du tout d’accord 50 (33%)
Plutôt pas d’accord 0 (0%)

320
Characteristic N = 150
Ni d’accord, ni pas d’accord 50 (33%)
Plutôt d’accord 50 (33%)
Tout à fait d’accord 0 (0%)

Cependant, cela produit un tableau inutilement long, d’autant


plus que les variables q1 à q6 ont les mêmes modalités de
réponse. Le package {bstfun} propose une fonction expéri-
mentale bstfun::tbl_likert() offrant un affichage plus com-
pact.

ĺ Important

Ce package n’est pas disponible sur CRAN :


il est donc nécessaire de l’installer de-
puis GitHub avec la commande suivante :
remotes::install_github("MSKCC-Epi-Bio/bstfun").
Si vous êtes sous Windows, il vous faudra pro-
bablement installer au préalable RTools qui
peut être téléchargé à l’adresse h t t p s : / / c r a n .r -
[Link]/bin/windows/Rtools/.

library(bstfun)

Attachement du package : 'bstfun'

L'objet suivant est masqué depuis 'package:gtsummary':

trial

df |>
tbl_likert(
include = q1:q6
)

321
Table printed with `knitr::kable()`, not {gt}. Learn why at
[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Ni
Pas du Plutôt d’accord, Tout à
tout pas ni pas Plutôt fait
Characteristic
d’accord d’accord d’accord d’accordd’accord
Première 39 32 25 (17%) 30 24
ques- (26%) (21%) (20%) (16%)
tion
Seconde 56 44 19 (13%) 26 5 (3.3%)
ques- (37%) (29%) (17%)
tion
Troisième8 (5.3%) 17 29 (19%) 43 53
ques- (11%) (29%) (35%)
tion
Quatrième 11 19 31 (21%) 40 49
ques- (7.3%) (13%) (27%) (33%)
tion
Cinquième 33 25 28 (22%) 25 16
ques- (26%) (20%) (20%) (13%)
tion
Sixième 50 0 (0%) 50 (33%) 50 0 (0%)
ques- (33%) (33%)
tion

On peut utiliser add_n() pour ajouter les effectifs totaux et


add_continuous_stat() pour ajouter une statistique continue
en traitant la variable comme un score (ici nous allons attribuer
les valeurs -2, -1, 0, +1 et +2).

df |>
tbl_likert(
include = q1:q6,
statistic = ~ "{p}%"
) |>
add_n() |>
add_continuous_stat(score_values = -2:2)

322
Table printed with `knitr::kable()`, not {gt}. Learn why at
[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Pas Ni
du Plutôt d’accord, Tout
tout pas ni pas Plutôt à fait
Characteristic
N d’accordd’accordd’accord d’accordd’accordMean
Première
150 26% 21% 17% 20% 16% -
ques- 0.21
tion
Seconde150 37% 29% 13% 17% 3.3% -
ques- 0.80
tion
Troisième
150 5.3% 11% 19% 29% 35% 0.77
ques-
tion
Quatrième
150 7.3% 13% 21% 27% 33% 0.65
ques-
tion
127 26%
Cinquième 20% 22% 20% 13% -
ques- 0.27
tion
Sixième150 33% 0% 33% 33% 0% -
ques- 0.33
tion

20.3 Représentations graphiques

À partir de sa version 0.3.0, le package {ggstats} propose une


fonction ggstats::gglikert() pour représenter des données
de Likert sous forme d’un diagramme en barre centré sur la
modalité centrale.

library(ggstats)
gglikert(df, include = q1:q6)

323
Première question 47% 26% 21% 17% 20% 16% 36%

Seconde question 67% 37% 29% 13% 17% 21%

Troisième question 17% 5%11% 19% 29% 35% 64%

Quatrième question 20% 7%13% 21% 27% 33% 59%

Cinquième question 46% 26% 20% 22% 20% 13% 32%

Sixième question 33% 33% 33% 33% 33%

50% 0% 50%

Pas du tout d'accord Plutôt pas d'accord Ni d'accord, ni pas d'accord Plutôt d'accord Tout à fait d'accord

Par défaut, les pourcentages totaux ne prennent pas en


compte la modalité centrale (lorsque le nombre de moda-
lité est impair). On peut inclure la modalité centrale avec
totals_include_center = TRUE, auquel cas la modalité
centrale seront comptabilisée pour moitié de chaque côté. Le
paramètre sort permet de trier les modalités (voir l’aide de
ggstats::gglikert() pour plus de détails sur les différentes
méthodes de tri).

df |>
gglikert(
include = q1:q6,
totals_include_center = TRUE,
sort = "ascending"
) +
guides(
fill = guide_legend(nrow = 2)
)

324
Seconde question 73% 37% 29% 13% 17% 27%

Cinquième question 57% 26% 20% 22% 20% 13% 43%

Première question 56% 26% 21% 17% 20% 16% 44%

Sixième question 50% 33% 33% 33% 50%

Quatrième question 30% 7%13% 21% 27% 33% 70%

Troisième question 26% 5%11% 19% 29% 35% 74%

50% 0% 50%

Pas du tout d'accord Ni d'accord, ni pas d'accord Tout à fait d'accord


Plutôt pas d'accord Plutôt d'accord

Il est possible de séparer les résultats par sous-groupe avec des


facettes.

df |>
gglikert(
include = q1:q6,
facet_cols = vars(groupe)
)

A B

Première question 51% 30%20%20%


16%
13% 29%44% 22%22%
14%
23%19% 42%

Seconde question 65%29% 36%14%


19% 20%68% 44% 23%
11%
16% 21%

Troisième question 10% 6%


17%28% 45% 72%22% 6%
16%
21%30% 27% 57%

Quatrième question 22% 7%


14%
22% 33% 23% 57%19% 7%
11%
20%21% 41% 62%

Cinquième question 41% 21%21%25%19%


14% 33%50% 31%19%
19%20%
11% 31%

Sixième question 28% 28% 29% 43% 43%38% 38% 37% 25% 25%

50% 0% 50% 50% 0% 50%

Pas du tout d'accord Plutôt pas d'accord Ni d'accord, ni pas d'accord Plutôt d'accord Tout à fait d'accord

325
df |>
gglikert(
include = q1:q6,
y = "groupe",
facet_rows = vars(.question),
facet_label_wrap = 15
)

PremièreSeconde
questionquestionquestionquestionquestionquestion
A 51% 30% 20% 20% 16% 13% 29%
B 44% 22% 22% 14% 23% 19% 42%
A 65% 29% 36% 14% 19% 20%
B 68% 44% 23% 11% 16% 21%

Troisième
A 10% 6% 17% 28% 45% 72%
B 22% 6% 16% 21% 30% 27% 57%

Quatrième
A 22% 7% 14% 22% 33% 23% 57%
B 19% 7%11% 20% 21% 41% 62%

Cinquième
A 41% 21% 21% 25% 19% 14% 33%
B 50% 31% 19% 19% 20% 11% 31%

Sixième
A 28% 28% 29% 43% 43%
B 38% 38% 37% 25% 25%
50% 0% 50%

Pas du tout d'accord Plutôt pas d'accord Ni d'accord, ni pas d'accord Plutôt d'accord Tout à fait d'accord

Une représentation alternative consiste à réaliser un graphique


en barres classiques, ce que l’on peut aisément obtenir avec
ggstats::gglikert_stacked().

df |>
gglikert_stacked(
include = q1:q6,
sort = "ascending",
add_median_line = TRUE
)

326
Seconde question 37% 29% 13% 17%

Cinquième question 26% 20% 22% 20% 13%

Sixième question 33% 33% 33%

Première question 26% 21% 17% 20% 16%

Quatrième question 7% 13% 21% 27% 33%

Troisième question 5% 11% 19% 29% 35%

0% 25% 50% 75% 100%

Pas du tout d'accord Plutôt pas d'accord Ni d'accord, ni pas d'accord Plutôt d'accord Tout à fait d'accord

327
21 Régression linéaire

Un modèle de régression linéaire est un modèle de régression


qui cherche à établir une relation linéaire entre une variable
continue, dite expliquée, et une ou plusieurs variables, dites
explicatives.

21.1 Modèle à une seule variable explicative


continue

Nous avons déjà abordé très rapidement la régression linéaire


dans le chapitre sur la statistique bivariée (cf. Section 19.3).
Reprenons le même exemple à partir du jeu de données iris
qui comporte les caractéristiques de 150 fleurs de trois espèces
différentes d’iris. Nous cherchons dans un premier temps à ex-
plorer la relation entre la largeur ([Link]) et la longueur
des pétales ([Link]). Représentons cette relation sous la
forme d’un nuage de points.

library(tidyverse)
ggplot(iris) +
aes(x = [Link], y = [Link]) +
geom_point(colour = "blue", alpha = .25) +
labs(x = "Longueur", y = "Largeur") +
theme_light()

328
2.5

2.0

1.5
Largeur

1.0

0.5

0.0
2 4 6
Longueur

Figure 21.1: Relation entre la largeur et la longueur des pétales


(nuage de points)

Il semble bien qu’il y a une relation linéaire entre ces deux


variables, c’est-à-dire que la relation entre ces deux variables
peut être représentée sous la forme d’une droite. Pour cela, on
va rechercher la droite telle que la distance entre les points ob-
servés et la droite soit la plus petite possible. Cette droite peut
être représentée graphique avec ggplot2::geom_smooth() et
l’option method = "lm" :

ggplot(iris) +
aes(x = [Link], y = [Link]) +
geom_point(colour = "blue", alpha = .25) +
geom_smooth(method = "lm") +
labs(x = "Longueur", y = "Largeur") +
theme_light()

329
2
Largeur

2 4 6
Longueur

Figure 21.2: Relation linéaire entre la largeur et la longueur des


pétales

La fonction de base pour calculer une régression linéaire est la


fonction stats::m(). On doit en premier lieu spécifier le mo-
dèle à l’aide d’une formule : on indique la variable à expliquer
dans la partie gauche de la formule et la variable explicative
dans la partie droite, les deux parties étant séparées par un
tilde27 (~). 27
Avec un clavier français, sous Win-
dows, le caractère tilde s’obtient en
Dans le cas présent, la variable [Link] fait office de va- pressant simultanément les touches
riable à expliquer et [Link] de variable explicative. Le Alt Gr et 7.
modèle s’écrit donc [Link] ~ [Link].

mod <- lm([Link] ~ [Link], data = iris)


mod

Call:
lm(formula = [Link] ~ [Link], data = iris)

Coefficients:
(Intercept) [Link]
-0.3631 0.4158

330
Le résultat comporte deux coefficients. Le premier, d’une valeur
de 0, 4158, est associé à la variable [Link] et indique la
pente de la courbe (on parle de slope en anglais). Le second,
d’une valeur de −0, 3631, représente l’ordonnée à l’origine (in-
tercept en anglais), c’est-à-dire la valeur estimée de [Link]
lorsque [Link] vaut 0. Nous pouvons rendre cela plus vi-
sible en élargissant notre graphique.

ggplot(iris) +
aes(x = [Link], y = [Link]) +
geom_point(colour = "blue", alpha = .25) +
geom_abline(
intercept = mod$coefficients[1],
slope = mod$coefficients[2],
linewidth = 1,
colour = "red"
) +
geom_vline(xintercept = 0, linewidth = 1, linetype = "dotted") +
labs(x = "Longueur", y = "Largeur") +
expand_limits(x = 0, y = -1) +
theme_light()

2
Largeur

−1
0 2 4 6
Longueur

Figure 21.3: Relation linéaire entre la largeur et la longueur des


pétales (représentation graphique de l’intercept)

331
Le modèle linéaire calculé estime donc que le relation entre nos
deux variables peut s’écrire sous la forme suivante :

𝑃 𝑒𝑡𝑎𝑙.𝑊 𝑖𝑑𝑡ℎ = 0, 4158 ⋅ 𝑃 𝑒𝑡𝑎𝑙.𝐿𝑒𝑛𝑔𝑡ℎ − 0, 3631

Le package {gtsummary} fournit gtsummary::tbl_regression(),


une fonction bien pratique pour produire un tableau propre
avec les coefficients du modèle, leur intervalle de confiance à
95% et leur p-valeurs28 . On précisera intercept = TRUE pour 28
Si l’on a besoin de ces informations
forcer l’affichage de l’intercept qui est masqué par défaut. sous la forme d’un tableau de don-
nées classique, on pourra se référer à
[Link]::tidy_plus_plus(),
library(gtsummary) utilisée de manière sous-jacente
mod %>% par gtsummary::tbl_regression(),
tbl_regression(intercept = TRUE) ainsi qu’à la méthode broom::tidy().
Ces fonctions sont génériques et peut
être utilisées avec une très grande
variété de modèles.
Table printed with `knitr::kable()`, not {gt}. Learn why at
[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Table 21.1: un tableau mis en forme des coefficients du modèle

Characteristic Beta 95% CI p-value


(Intercept) -0.36 -0.44, -0.28 <0.001
[Link] 0.42 0.40, 0.43 <0.001

Les p-valeurs calculées nous indique si le coefficient est statis-


tiquement différent de 0. En effet, pour la variable explicative,
cela nous indique si la relation est statistiquement significative.
Le signe du coefficient (positif ou négatif) nous indique le sens
de la relation.

Ď Astuce

Dans certains cas, si l’on suppose que la relation entre


les deux variables est proportionnelle, on peut souhaiter
calculer un modèle sans intercept. Par défaut, R ajoute un
intercept à ses modèles. Pour forcer le calcul d’un modèle
sans intercept, on ajoutera - 1 à la formule définissant le

332
modèle.

lm([Link] ~ [Link] - 1, data = iris)

Call:
lm(formula = [Link] ~ [Link] - 1, data = iris)

Coefficients:
[Link]
0.3365

21.2 Modèle à une seule variable explicative


catégorielle

Si dans un modèle linéaire la variable à expliquer est nécessaire-


ment continue, il est possible de définir une variable explicative
catégorielle. Prenons la variable Species.

library(labelled)
iris %>% look_for("Species")

pos variable label col_type missing values


5 Species — fct 0 setosa
versicolor
virginica

Il s’agit d’un facteur à trois modalités. Par défaut, la première


valeur du facteur (ici setosa) va servir de modalité de réfé-
rence.

mod <- lm([Link] ~ Species, data = iris)


mod

Call:

333
lm(formula = [Link] ~ Species, data = iris)

Coefficients:
(Intercept) Speciesversicolor Speciesvirginica
0.246 1.080 1.780

mod %>%
tbl_regression(intercept = TRUE)

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Table 21.2: régression linaire avec une variable explicative caté-


gorielle

Characteristic Beta 95% CI p-value


(Intercept) 0.25 0.19, 0.30 <0.001
Species
setosa — —
versicolor 1.1 1.0, 1.2 <0.001
virginica 1.8 1.7, 1.9 <0.001

Dans ce cas de figure, l’intercept représente la situation à la


référence, donc pour l’espèce setosa.
Calculons les moyennes par espèce :

iris %>%
group_by(Species) %>%
summarise(mean([Link]))

# A tibble: 3 x 2
Species `mean([Link])`
<fct> <dbl>
1 setosa 0.246
2 versicolor 1.33
3 virginica 2.03

334
Comme on le voit, l’intercept nous indique donc la moyenne
observée pour l’espèce de référence (0, 246).
Le coefficient associé à versicolor correspond à la différence
par rapport à la référence (ici +1, 080). Comme vous pouvez le
constater, il s’agit de la différence entre la moyenne observée
pour versicolor (1, 326) et celle de la référence setosa (0, 246) :
1, 326 − 0, 246 = 1, 080.
Ce coefficient est significativement différent de 0 (p<0,001), in-
diquant que la largeur des pétales diffère significativement entre
les deux espèces.

Ď Astuce

Lorsque l’on calcule le même modèle sans intercept, les


coefficients s’interprètent un différemment :

lm([Link] ~ Species - 1, data = iris)

Call:
lm(formula = [Link] ~ Species - 1, data = iris)

Coefficients:
Speciessetosa Speciesversicolor Speciesvirginica
0.246 1.326 2.026

En l’absence d’intercept, trois coefficients sont calculés et


il n’y a plus ici de modalité de référence. Chaque coeffi-
cient représente donc la moyenne observée pour chaque
modalité.
On appelle contrastes les différents manières de coder
des variables catégorielles dans un modèle. Nous y re-
viendrons plus en détail dans un chapitre dédié (cf. Cha-
pitre 25).

335
21.3 Modèle à plusieurs variables explicatives

Un des intérêts de la régression linéaire est de pouvoir estimer


un modèle multivarié, c’est-à-dire avec plusieurs variables expli-
catives. Pour cela, on listera les différentes variables explicatives
dans la partie droite de la formule, séparées par le symbole +.

mod <- lm(


[Link] ~ [Link] + [Link] + [Link] + Species,
data = iris
)
mod

Call:
lm(formula = [Link] ~ [Link] + [Link] + [Link] +
Species, data = iris)

Coefficients:
(Intercept) [Link] [Link] [Link]
-0.47314 0.24220 0.24220 -0.09293
Speciesversicolor Speciesvirginica
0.64811 1.04637

mod %>%
tbl_regression(intercept = TRUE)

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Table 21.3: régression linaire avec plusieurs variables explica-


tives

Characteristic Beta 95% CI p-value


(Intercept) -0.47 -0.82, -0.12 0.008
[Link] 0.24 0.15, 0.34 <0.001

336
Characteristic Beta 95% CI p-value
[Link] 0.24 0.15, 0.34 <0.001
[Link] -0.09 -0.18, 0.00 0.039
Species
setosa — —
versicolor 0.65 0.40, 0.89 <0.001
virginica 1.0 0.72, 1.4 <0.001

Ce type de modèle permet d’estimer l’effet de chaque variable


explicative, toutes choses égales par ailleurs. Dans le cas présent,
on s’aperçoit que la largeur des pétales diffère significativement
selon les espèces, est fortement corrélée positivement à la lon-
gueur du pétale et la largeur du sépale et qu’il y a, lorsque l’on
ajuste sur l’ensemble des autres variables, une relation négative
(faiblement significative) avec la longueur du sépale.
Lorsque le nombre de coefficients est élevé, une représentation
graphique est souvent plus facile à lire qu’un tableau. On parle
alors de graphique en forêt ou forest plot en anglais. Rien de plus
facile ! Il suffit d’avoir recours à ggstats::ggcoef_model().

library(ggstats)
ggcoef_model(mod)

[Link]
(p<0.001***)

[Link]
(p<0.001***)

[Link]
(p=0.039*)

Species
setosa

versicolor (p<0.001***)

virginica (p<0.001***)

0.0 0.5 1.0


Beta

p = 0.05 p > 0.05

Figure 21.4: un graphique en forêt des coefficients du modèle

337
22 Régression logistique
binaire

Dans le chapitre précédent (Chapitre 21), nous avons abordé


la régression linéaire pour les variables continues. Pour analy-
ser une variable catégorielle, il est nécessaire d’avoir recours à
d’autres types de modèles. Les modèles linéaires généralisés (ge-
neralized linear models ou GLM en anglais) sont une généralisa-
tion de la régression linéaire grâce à l’utilisation d’une fonction
de lien utilisée pour transformer la variable à expliquer et pou-
voir ainsi retomber sur un modèle linéaire classique. Il existe
de multiples fonctions de lien correspondant à tout autant de
modèles. Par exemple, on pourra utiliser un modèle de Poisson
pour une variable entière positive ou un modèle d’incidence.
Pour une variable binaire (c’est-à-dire du type oui / non ou
vrai / faux), les modèles les plus courants utilisent les fonctions
de lien probit ou logit, cette dernière fonction correspondent
à la régression logistique binaire (modèle logit). Comme
pour la régression linéaire, les variables explicatives peuvent
être continues et/ou catégorielles.

22.1 Préparation des données

Dans ce chapitre, nous allons encore une fois utiliser les


données de l’enquête Histoire de vie, fournies avec l’extension
{questionr}.

data(hdv2003, package = "questionr")


d <- hdv2003

À titre d’exemple, nous allons étudier l’effet de l’âge, du sexe,


du niveau d’étude, de la pratique religieuse et du nombre moyen

338
d’heures passées à regarder la télévision par jour sur la proba-
bilité de pratiquer un sport.
En premier lieu, il importe de vérifier, par exemple avec
labelled::look_for(), que notre variable d’intérêt (ici
sport) est correctement codée, c’est-à-dire que la première
modalité correspondent à la référence (soit ne pas avoir vécu
l’évènement d’intérêt) et que la seconde modalité corresponde
au fait d’avoir vécu l’évènement.

library(labelled)
d |> look_for("sport")

pos variable label col_type missing values


19 sport — fct 0 Non
Oui

Dans notre exemple, la modalité Non est déjà la première mo-


dalité. Il n’y a donc pas besoin de modifier notre variable.
Il faut également la présence éventuelle de données manquantes
(NA)29 . Les observations concernées seront tout simplement ex- 29
Pour visualiser le nombre
clues du modèle lors de son calcul. Ce n’est pas notre cas ici. de données manquantes (NA) de
l’ensemble des variables d’un ta-
bleau, on pourra avoir recours à
Ď Astuce questionr::[Link]().

Alternativement, on pourra aussi coder notre variable à


expliquer sous forme booléenne (FALSE / TRUE) ou numé-
riquement en 0/1.
Il est possible d’indiquer un facteur à plus de deux mo-
dalités. Dans une telle situation, R considérera que tous
les modalités, sauf la modalité de référence, est une réa-
lisation de la variable d’intérêt. Cela serait correct, par
exemple, si notre variable sport était codée ainsi : Non,
Oui, de temps en temps, Oui, régulièrement. Cependant,
afin d’éviter tout risque d’erreur ou de mauvaise interpré-
tation, il est vivement conseillé de recoder au préalable sa
variable d’intérêt en un facteur à deux modalités.

La notion de modalité de référence s’applique également aux


variables explicatives catégorielles. En effet, dans un modèle,

339
tous les coefficients sont calculés par rapport à la modalité de
référence (cf. Section 21.2). Il importe donc de choisir une mo-
dalité de référence qui fasse sens afin de faciliter l’interprétation.
Par ailleurs, ce choix doit dépendre de la manière dont on sou-
haite présenter les résultats (le data storytelling est essentiel).
De manière générale on évitera de choisir comme référence une
modalité peu représentée dans l’échantillon ou bien une moda-
lité correspondant à une situation atypique.
Prenons l’exemple de la variable sexe. Souhaite-t-on connaitre
l’effet d’être une femme par rapport au fait d’être un homme
ou bien l’effet d’être un homme par rapport au fait d’être une
femme ? Si l’on opte pour le second, alors notre modalité de ré-
férence sera le sexe féminin. Comme est codée cette variable ?

d |> look_for("sexe")

pos variable label col_type missing values


3 sexe — fct 0 Homme
Femme

La modalité Femme s’avère ne pas être la première modalité.


Nous devons appliquer la fonction forcats::fct_relevel()
ou la fonction stats::relevel() :

library(tidyverse)
d <- d |>
mutate(sexe = sexe |> fct_relevel("Femme"))

d$sexe |> questionr::freq()

n % val%
Femme 1101 55 55
Homme 899 45 45

Données labellisées
Si l’on utilise des données labellisées (voir Chapitre 12), nos
variables catégorielles seront stockées sous la forme d’un

340
vecteur numérique avec des étiquettes. Il sera donc nécessaire
de convertir ces variables en facteurs, tout simplement avec
labelled::to_factor() ou labelled::unlabelled().
Les variables age et [Link] sont des variables quantitatives.
Il importe de vérifier qu’elles sont bien enregistrées en tant que
variables numériques. En effet, il arrive parfois que dans le fi-
chier source les variables quantitatives soient renseignées sous
forme de valeur textuelle et non sous forme numérique.

d |> look_for("age", "heures")

pos variable label col_type missing values


2 age — int 0
20 [Link] — dbl 5

Nos deux variables sont bien renseignées sous forme numérique


(respectivement des entiers et des nombres décimaux).
Cependant, l’effet de l’âge est rarement linéaire. Un exemple
trivial est par exemple le fait d’occuper un emploi qui sera
moins fréquent aux jeunes âges et aux âges élevés. Dès lors, on
pourra transformer la variable age en groupe d’âges (et donc en
variable catégorielle) avec la fonction cut() (cf. Section 9.4) :

d <- d |>
mutate(
groupe_ages = age |>
cut(
c(18, 25, 45, 65, 99),
right = FALSE,
[Link] = TRUE,
labels = c("18-24 ans", "25-44 ans",
"45-64 ans", "65 ans et plus")
)
)
d$groupe_ages |> questionr::freq()

n % val%
18-24 ans 169 8.5 8.5

341
25-44 ans 706 35.3 35.3
45-64 ans 745 37.2 37.2
65 ans et plus 380 19.0 19.0

Jetons maintenant un œil à la variable nivetud :

d$nivetud |> questionr::freq()

n % val%
N'a jamais fait d'etudes 39 2.0 2.1
A arrete ses etudes, avant la derniere annee d'etudes primaires 86 4.3 4.6
Derniere annee d'etudes primaires 341 17.0 18.1
1er cycle 204 10.2 10.8
2eme cycle 183 9.2 9.7
Enseignement technique ou professionnel court 463 23.2 24.5
Enseignement technique ou professionnel long 131 6.6 6.9
Enseignement superieur y compris technique superieur 441 22.0 23.4
NA 112 5.6 NA

En premier lieu, cette variable est détaillée en pas moins de


huit modalités dont certaines sont peu représentées (seulement
39 individus soit 2 % n’ont jamais fait d’études par exemple).
Afin d’améliorer notre modèle logistique, il peut être pertinent
de regrouper certaines modalités (cf. Section 9.3) :

d <- d |>
mutate(
etudes = nivetud |>
fct_recode(
"Primaire" = "N'a jamais fait d'etudes",
"Primaire" = "A arrete ses etudes, avant la derniere annee d'etudes primaires",
"Primaire" = "Derniere annee d'etudes primaires",
"Secondaire" = "1er cycle",
"Secondaire" = "2eme cycle",
"Technique / Professionnel" = "Enseignement technique ou professionnel court",
"Technique / Professionnel" = "Enseignement technique ou professionnel long",
"Supérieur" = "Enseignement superieur y compris technique superieur"
)
)

342
d$etudes |> questionr::freq()

n % val%
Primaire 466 23.3 24.7
Secondaire 387 19.4 20.5
Technique / Professionnel 594 29.7 31.5
Supérieur 441 22.0 23.4
NA 112 5.6 NA

Notre variable comporte également 112 individus avec une va-


leur manquante. Si nous conservons cette valeur manquante,
ces 112 individus seront, par défaut, exclus de l’analyse. Ces va-
leurs manquantes n’étant pas négligeable (5,6 %), nous pouvons
également faire le choix de considérer ces valeurs manquantes
comme une modalité supplémentaire. Auquel cas, nous utilise-
rons la fonction forcats::fct_na_value_to_level() :

d$etudes <- d$etudes |>


fct_na_value_to_level("Non documenté")

Enfin, pour améliorer les différentes sorties (tableaux et figures),


nous allons ajouter des étiquettes de variables (cf. Chapitre 11)
avec labelled::set_variable_labels().

d <- d |>
set_variable_labels(
sport = "Pratique un sport ?",
sexe = "Sexe",
groupe_ages = "Groupe d'âges",
etudes = "Niveau d'études",
relig = "Rapport à la religion",
[Link] = "Heures de télévision / jour"
)

343
Ĺ Code récapitulatif (préparation des données)

data(hdv2003, package = "questionr")


d <-
hdv2003 |>
mutate(
sexe = sexe |> fct_relevel("Femme"),
groupe_ages = age |>
cut(
c(18, 25, 45, 65, 99),
right = FALSE,
[Link] = TRUE,
labels = c("18-24 ans", "25-44 ans",
"45-64 ans", "65 ans et plus")
),
etudes = nivetud |>
fct_recode(
"Primaire" = "N'a jamais fait d'etudes",
"Primaire" = "A arrete ses etudes, avant la derniere annee d'etudes primaires",
"Primaire" = "Derniere annee d'etudes primaires",
"Secondaire" = "1er cycle",
"Secondaire" = "2eme cycle",
"Technique / Professionnel" = "Enseignement technique ou professionnel court",
"Technique / Professionnel" = "Enseignement technique ou professionnel long",
"Supérieur" = "Enseignement superieur y compris technique superieur"
) |>
fct_na_value_to_level("Non documenté")
) |>
set_variable_labels(
sport = "Pratique un sport ?",
sexe = "Sexe",
groupe_ages = "Groupe d'âges",
etudes = "Niveau d'études",
relig = "Rapport à la religion",
[Link] = "Heures de télévision / jour"
)

344
22.2 Statistiques descriptives

Avant toute analyse multivariée, il est toujours bon de procéder


à une analyse descriptive bivariée simple, tout simplement
avec gtsummary::tbl_summary(). Ajoutons quelques tests
de comparaison avec gtsummary::add_p(). Petite astuce :
gtsummary::modify_spanning_header() permet de rajouter
un en-tête sur plusieurs colonnes.

library(gtsummary)
theme_gtsummary_language("fr", [Link] = ",", [Link] = " ")

d |>
tbl_summary(
by = sport,
include = c(sexe, groupe_ages, etudes, relig, [Link])
) |>
add_overall(last = TRUE) |>
add_p() |>
bold_labels() |>
modify_spanning_header(
update = all_stat_cols() ~ "**Pratique un sport ?**"
)

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Table 22.1: Pratique d’un sport selon différentes variables ex-


plicatives (analyse bivariée)

Non, N Oui, N Total, N p-


Caractéristique = 1 277 = 723 = 2 000 valeur
Sexe <0,001
Femme 747 354 1 101
(58%) (49%) (55%)
Homme 530 369 899 (45%)
(42%) (51%)

345
Non, N Oui, N Total, N p-
Caractéristique = 1 277 = 723 = 2 000 valeur
Groupe <0,001
d’âges
18-24 ans 58 (4,5%) 111 169
(15%) (8,5%)
25-44 ans 359 347 706 (35%)
(28%) (48%)
45-64 ans 541 204 745 (37%)
(42%) (28%)
65 ans et plus 319 61 (8,4%) 380 (19%)
(25%)
Niveau <0,001
d’études
Primaire 416 50 (6,9%) 466 (23%)
(33%)
Secondaire 270 117 387 (19%)
(21%) (16%)
Technique / 378 216 594 (30%)
Professionnel (30%) (30%)
Supérieur 186 255 441 (22%)
(15%) (35%)
Non 27 (2,1%) 85 (12%) 112
documenté (5,6%)
Rapport à la 0,14
religion
Pratiquant 182 84 (12%) 266 (13%)
regulier (14%)
Pratiquant 295 147 442 (22%)
occasionnel (23%) (20%)
Appartenance 473 287 760 (38%)
sans pratique (37%) (40%)
Ni croyance ni 239 160 399 (20%)
appartenance (19%) (22%)
Rejet 60 (4,7%) 33 (4,6%) 93 (4,7%)
NSP ou NVPR 28 (2,2%) 12 (1,7%) 40 (2,0%)
Heures de 2,00 (1,00 2,00 (1,00 2,00 (1,00 <0,001
télévision / – 3,00) – 3,00) – 3,00)
jour
Manquant 2 3 5

346
22.3 Calcul de la régression logistique binaire

La spécification d’une régression logistique se fait avec


stats::glm() et est très similaire à celle d’une régression
linéaire simple (cf. Section 21.3) : on indique la variable à
expliquer suivie d’un tilde (~) puis des variables explicatives
séparées par un plus (+)30 . Il faut indiquer à glm() la famille 30
Il est possible de spécifier des
du modèle souhaité : on indiquera simplement family = modèles plus complexes, notamment
avec des effets d’interaction, qui se-
binomial pour un modèle logit 31 .
ront aborder plus loin (cf. Cha-
pitre 26).
mod <- glm( 31
Pour un modèle pro-
sport ~ sexe + groupe_ages + etudes + relig + [Link],
bit, on indiquera family =
family = binomial, binomial("probit").
data = d
)

Pour afficher les résultats du modèle, le plus simple est d’avoir


recours à gtsummary::tbl_regression().

mod |>
tbl_regression(intercept = TRUE) |>
bold_labels()

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Table 22.2: Facteurs associés à la pratique d’un sport (régres-


sion logistique binaire)

Caractéristique log(OR) 95% IC p-valeur


(Intercept) -0,80 -1,4 – -0,17 0,014
Sexe
Femme — —
Homme 0,44 0,23 – 0,65 <0,001
Groupe d’âges
18-24 ans — —
25-44 ans -0,42 -0,87 – 0,065
0,03

347
Caractéristique log(OR) 95% IC p-valeur
45-64 ans -1,1 -1,6 – -0,62 <0,001
65 ans et plus -1,4 -1,9 – -0,85 <0,001
Niveau d’études
Primaire — —
Secondaire 0,95 0,57 – 1,3 <0,001
Technique / 1,0 0,68 – 1,4 <0,001
Professionnel
Supérieur 1,9 1,5 – 2,3 <0,001
Non documenté 2,2 1,5 – 2,8 <0,001
Rapport à la
religion
Pratiquant regulier — —
Pratiquant occasionnel -0,02 -0,39 – >0,9
0,35
Appartenance sans -0,01 -0,35 – >0,9
pratique 0,34
Ni croyance ni -0,22 -0,59 – 0,3
appartenance 0,16
Rejet -0,38 -0,95 – 0,2
0,17
NSP ou NVPR -0,08 -0,92 – 0,8
0,70
Heures de télévision -0,12 -0,19 – <0,001
/ jour -0,06

22.4 Interpréter les coefficients

L’intercept traduit la situation à la référence (i.e. toutes les va-


riables catégorielles à leur modalité de référence et les variables
continues à 0), après transformation selon la fonction de lien
(i.e. après la transformation logit).
Illustrons cela. Supposons donc une personne de sexe féminin,
âgée entre 18 et 24 ans, de niveau d’étude primaire, pratiquante
régulière et ne regardant pas la télévision (situation de réfé-
rence). Seul l’intercept s’applique dans le modèle, et donc le
modèle prédit que sa probabilité de faire du sport est de −0, 80

348
selon l’échelle logit. Retraduisons cela en probabilité classique
avec la fonction logit inverse.

logit_inverse <- binomial("logit") |> purrr::pluck("linkinv")


logit_inverse(-0.80)

[1] 0.3100255

Selon le modèle, la probabilité que cette personne fasse du sport


est donc de 31%.
Prenons maintenant une personne identique mais de sexe mas-
culin. Nous devons donc considérer, en plus de l’intercept, le
coefficient associé à la modalité Homme. Sa probabilité de faire
du sport est donc :

logit_inverse(-0.80 + 0.44)

[1] 0.4109596

Le coefficient associé à Homme est donc un modificateur par


rapport à la situation de référence.
Enfin, considérons que cette dernière personne regarde égale-
ment la télévision 2 heures en moyenne par jour. Nous devons
alors considérer le coefficient associé à la variable [Link] et,
comme il s’agit d’une variable continue, multiplier ce coefficient
par 2, car le coefficient représente le changement pour un incré-
ment de 1 unité.

logit_inverse(-0.80 + 0.44 + 2 * -0.12)

[1] 0.3543437

Il est crucial de bien comprendre comment dans quels cas et


comment chaque coefficient est utilisé par le modèle.
Le package {breakdown} permet de mieux visualiser notre der-
nier exemple.

349
individu3 <- d[1, ]
individu3$sexe[1] <- "Homme"
individu3$groupe_ages[1] <- "18-24 ans"
individu3$etudes[1] <- "Primaire"
individu3$relig[1] <- "Pratiquant regulier"
individu3$[Link][1] <- 2

library(breakDown)
logit <- function(x) exp(x) / (1 + exp(x))
plot(
broken(mod, individu3, [Link] = betas),
trans = logit
) +
scale_y_continuous(
labels = scales::label_percent(),
breaks = 0:5/5,
limits = c(0, 1)
)

final_prognosis −0.146

relig = Pratiquant regulier 0

etudes = Primaire 0

groupe_ages = 18−24 ans 0

[Link] = 2 −0.057

sexe = Homme 0.101

(Intercept) −0.19

0% 20% 40% 60% 80% 100%


pmax(cummulative, prev)

Figure 22.1: Décomposition de la probabilité de faire du sport


de l’individu 3

350
22.5 La notion d’odds ratio

L’un des intérêts de la régression logistique logit réside dans


le fait que l’exponentiel des coefficients correspond à des odds
ratio ou rapport des côtes en français.

Ď Astuce

Pour comprendre la notion de côte (odd en anglais), on


peut se référer aux paris sportifs. Par exemple, lorsque
les trois quarts des parieurs parient que le cheval A va
remporter la course, on dit alors que ce cheval à une côte
de trois contre un (trois personnes parient qu’il va gagner
contre une personne qu’il va perdre). Prenons un autre
cheval : si les deux tiers pensent que le cheval B va perdre
(donc un tiers pense qu’il va gagner), on dira alors que sa
côte est de un contre deux (une personne pense qu’il va
gagner contre deux qu’il va perdre).
Si l’on connait la proportion ou probabilité p d’avoir
vécu ou de vivre un évènement donné (ici gagner la
course), la côte (l’odd) s’obtient avec la formule suivante :
𝑝/(1 − 𝑝). La côte du cheval A est bien 0, 75/(1 − 0, 75) =
0, 75/0, 25 = 3 est celle du cheval B (1/3)/(2/3) = 1/2 =
0, 5.
Pour comparer deux côtes (par exemple pour savoir si le
cheval A a une probabilité plus élevée de remporter la
course que le cheval B, selon les parieurs), on calculera
tout simplement le rapport des côtes ou odds ratio (OR) :
𝑂𝑅𝐴/𝐵 = 𝑂𝑑𝑑𝑠𝐴 /𝑂𝑑𝑑𝑠𝐵 = 3/0, 5 = 6.
Ce calcul peut se faire facilement dans R avec la fonction
questionr::[Link]().

questionr::[Link](.75, 1/3)

[1] 6

L’odds ratio est donc égal à 1 si les deux côtes sont iden-
tiques, est supérieur à 1 si le cheval A une probabilité
supérieure à celle du cheval B, et inférieur à 1 si c’est
probabilité est inférieure.

351
On le voit, par construction, l’odds ratio de B par rapport
à A est l’inverse de celui de A par rapport à B : 𝑂𝑅𝐵/𝐴 =
1/𝑂𝑅𝐴/𝐵 .

Pour afficher les odds ratio il suffit d’indiquer exponentiate =


TRUE à gtsummary::tbl_regression().

mod |>
tbl_regression(exponentiate = TRUE) |>
bold_labels()

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Table 22.3: Facteurs associés à la pratique d’un sport (odds ra-


tios)

Caractéristique OR 95% IC p-valeur


Sexe
Femme — —
Homme 1,55 1,26 – 1,91 <0,001
Groupe d’âges
18-24 ans — —
25-44 ans 0,66 0,42 – 1,03 0,065
45-64 ans 0,34 0,21 – 0,54 <0,001
65 ans et plus 0,25 0,15 – 0,43 <0,001
Niveau d’études
Primaire — —
Secondaire 2,59 1,77 – 3,83 <0,001
Technique / Professionnel 2,86 1,98 – 4,17 <0,001
Supérieur 6,63 4,55 – 9,80 <0,001
Non documenté 8,59 4,53 – 16,6 <0,001
Rapport à la religion
Pratiquant regulier — —
Pratiquant occasionnel 0,98 0,68 – 1,42 >0,9
Appartenance sans pratique 0,99 0,71 – 1,40 >0,9
Ni croyance ni appartenance 0,81 0,55 – 1,18 0,3

352
Caractéristique OR 95% IC p-valeur
Rejet 0,68 0,39 – 1,19 0,2
NSP ou NVPR 0,92 0,40 – 2,02 0,8
Heures de télévision / jour 0,89 0,83 – 0,95 <0,001

Pour une représentation visuelle, graphique en forêt ou


forest plot en anglais, on aura tout simplement recours à
ggstats::ggcoef_model().

mod |>
ggstats::ggcoef_model(exponentiate = TRUE)

Sexe Femme
Homme (p<0.001***)
Groupe d'âges 18−24 ans
25−44 ans (p=0.065)
45−64 ans (p<0.001***)
65 ans et plus (p<0.001***)
Niveau d'études Primaire
Secondaire (p<0.001***)
Technique / Professionnel (p<0.001***)
Supérieur (p<0.001***)
Non documenté (p<0.001***)
Rapport à la religion Pratiquant regulier
Pratiquant occasionnel (p=0.908)
Appartenance sans pratique (p=0.969)
Ni croyance ni appartenance (p=0.265)
Rejet (p=0.180)
NSP ou NVPR (p=0.838)
Heures de télévision / jour (p<0.001***)
0.3 1.0 3.0 10.0
OR

p <= 0.05 p > 0.05

Figure 22.2: Facteurs associés à la pratique d’un sport (forest


plot)

On pourra alternativement préférer ggstats::ggcoef_table()32 32


ggstats::ggcoef_table() est dis-
qui affiche un tableau des coefficients à la droite du forest ponible à partir de la version 0.4.0 de
{ggstats}.
plot.

mod |>
ggstats::ggcoef_table(exponentiate = TRUE)

353
OR
95% CIp
Sexe Femme 1.0
Homme 1.6
1.3,<0.001
1.9
Groupe d'âges 18−24 ans 1.0
25−44 ans 0.7
0.4, 0.065
1.0
45−64 ans 0.3
0.2,<0.001
0.5
65 ans et plus 0.3
0.1,<0.001
0.4
Niveau d'études Primaire 1.0
Secondaire 2.6
1.8,<0.001
3.8
Technique / Professionnel 2.9
2.0,<0.001
4.2
Supérieur 6.6
4.6,<0.001
9.8
Non documenté 8.6
4.5, <0.001
16.6
Rapport à la religion Pratiquant regulier 1.0
Pratiquant occasionnel 1.0
0.7, 0.908
1.4
Appartenance sans pratique 1.0
0.7, 0.969
1.4
Ni croyance ni appartenance 0.8
0.6, 0.265
1.2
Rejet 0.7
0.4, 0.180
1.2
NSP ou NVPR 0.9
0.4, 0.838
2.0
Heures de télévision / jour 0.9
0.8,<0.001
0.9
0.3 [Link]
OR

p <= 0.05 p > 0.05

Figure 22.3: Facteurs associés à la pratique d’un sport (forest


plot avec table des coefficients)

Ĺ Note

Lorsque l’on réalise un forest plot de coefficients expo-


nentialisés tels que des odds ratios, une bonne pratique
consiste à utiliser une échelle logarithmique. En effet,
l’inverse d’un odds ratio de 2 est 0,5. Avec une échelle
logarithmique, la distance entre 0,5 et 1 est égale à celle
entre 1 et 2. Sur la figure précédente, vous pourrez no-
ter que ggstats::ggcoef_model() applique automati-
quement une échelle logarithmique lorsque exponentiate
= TRUE.
Quelques références : Forest Plots: Linear or Logarithmic
Scale? ou encore Graphing Ratio Measures on Forest Plot.

¾ Mise en garde

En rédigeant les résultats de la régression, il faudra être


vigilant à ne pas confondre les odds ratios avec des pre-
valence ratios. Avec un odds ratio de 1,55, il serait ten-
tant d’écrire que les hommes ont une probabilité 55%
supérieure de pratique un sport que les femmes (toutes

354
choses égales par ailleurs). Une telle formulation corres-
pond à un prevalence ratio (rapport des prévalences en
français) ou risk ratio (rapport des risques), à savoir divi-
ser la probabilité de faire du sport des hommes par celle
des femmes, 𝑝ℎ𝑜𝑚𝑚𝑒𝑠 /𝑝𝑓𝑒𝑚𝑚𝑒𝑠 . Or, cela ne correspond
pas à la formule de l’odds ratio, à savoir (𝑝ℎ𝑜𝑚𝑚𝑒𝑠 /(1 −
𝑝ℎ𝑜𝑚𝑚𝑒𝑠 ))/(𝑝𝑓𝑒𝑚𝑚𝑒𝑠 /(1 − 𝑝𝑓𝑒𝑚𝑚𝑒𝑠 )).
Lorsque le phénomène étudié est rare et donc que les pro-
babilités sont faibles (inférieures à quelques pour-cents),
alors il est vrai que les odds ratio sont approximativement
égaux aux prevalence ratios. Mais ceci n’est plus du tout
vrai pour des phénomènes plus fréquents.

22.6 Afficher les écarts-types plutôt que les


intervalles de confiance

La manière de présenter les résultats d’un modèle de régression


varie selon les disciplines, les champs thématiques et les revues.
Si en sociologie et épidémiologie on aurait plutôt tendance à
afficher les odds ratio avec leur intervalle de confiance, il est fré-
quent en économétrie de présenter plutôt les coefficients bruts
et leurs écarts-types (standard deviation ou sd en anglais). De
même, plutôt que d’ajouter une colonne avec les p valeurs, un
usage consiste à afficher des étoiles de significativité à la droite
des coefficients significatifs.
Pour cela, on pourra personnaliser le tableau produit
avec gtsummary::tbl_regression(), notamment avec
gtsummary::add_significance_stars() pour l’ajout des
étoiles de significativité, ainsi que gtsummary::modify_column_hide()
et gtsummary::modify_column_unhide() pour afficher / mas-
quer les colonnes du tableau produit33 . 33
La liste des colonnes dispo-
nibles peut être obtenues avec
mod |> tbl_regression() |>
mod |>
purrr::pluck("table_body") |>
tbl_regression() |> colnames().
add_significance_stars() |>
modify_column_hide(c("ci", "[Link]")) |>
modify_column_unhide("[Link]") |>

355
bold_labels()

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Table 22.4: Présentation économétrique des facteurs associés à


la pratique d’un sport

Caractéristique log(OR) ET
Sexe
Femme — —
Homme 0,44*** 0,106
Groupe d’âges
18-24 ans — —
25-44 ans -0,42 0,228
45-64 ans -1,1*** 0,238
65 ans et plus -1,4*** 0,274
Niveau d’études
Primaire — —
Secondaire 0,95*** 0,197
Technique / Professionnel 1,0*** 0,190
Supérieur 1,9*** 0,195
Non documenté 2,2*** 0,330
Rapport à la religion
Pratiquant regulier — —
Pratiquant occasionnel -0,02 0,189
Appartenance sans pratique -0,01 0,175
Ni croyance ni appartenance -0,22 0,193
Rejet -0,38 0,286
NSP ou NVPR -0,08 0,411
Heures de télévision / jour -0,12*** 0,034

Les économistes pourraient préférer le package {modelsummary}


à {gtsummary}. Ces deux packages ont un objectif similaire
(la production de tableaux statistiques) mais abordent cet
objectif avec des approches différentes. Il faut noter que

356
modelsummary::modelsummary() n’affiche pas les modalités
de référence, ni les étiquettes de variable.

mod |> modelsummary::modelsummary(stars = TRUE)

La fonction modelsummary::modelplot() permet d’afficher un


graphique des coefficients.

mod |> modelsummary::modelplot()

[Link]
religNSP ou NVPR
religRejet
religNi croyance ni appartenance
religAppartenance sans pratique
religPratiquant occasionnel
etudesNon documenté
etudesSupérieur
etudesTechnique / Professionnel
etudesSecondaire
groupe_ages65 ans et plus
groupe_ages45−64 ans
groupe_ages25−44 ans
sexeHomme
(Intercept)
−2 −1 0 1 2 3
Coefficient estimates and 95% confidence intervals

Figure 22.4: Facteurs associés à la pratique d’un sport avec mo-


delplot()

ATTENTION : si l’on affiche les odds ratio avec


exponentiate = TRUE, modelsummary::modelplot()
conserve par défaut une échelle linéaire. On sera donc vi-
gilant à appliquer ggplot2::scale_x_log10() manuellement
pour utiliser une échelle logarithmique.

mod |>
modelsummary::modelplot(exponentiate = TRUE) +
ggplot2::scale_x_log10()

357
Table 22.5: Présentation des facteurs associés à la pratique d’un
sport avec modelsummary()

(1)
(Intercept) −0.798*
(0.324)
sexeHomme 0.440***
(0.106)
groupe_ages25-44 ans −0.420+
(0.228)
groupe_ages45-64 ans −1.085***
(0.238)
groupe_ages65 ans et plus −1.381***
(0.274)
etudesSecondaire 0.951***
(0.197)
etudesTechnique / Professionnel 1.049***
(0.190)
etudesSupérieur 1.892***
(0.195)
etudesNon documenté 2.150***
(0.330)
religPratiquant occasionnel −0.022
(0.189)
religAppartenance sans pratique −0.007
(0.175)
religNi croyance ni appartenance −0.215
(0.193)
religRejet −0.384
(0.286)
religNSP ou NVPR −0.084
(0.411)
[Link] −0.121***
(0.034)
[Link]. 1995
AIC 2236.2
BIC 2320.1
[Link]. −1103.086
F 21.691
RMSE 0.43
+ p < 0.1, * p < 0.05, ** p < 0.01, *** p < 0.001

358
[Link]
religNSP ou NVPR
religRejet
religNi croyance ni appartenance
religAppartenance sans pratique
religPratiquant occasionnel
etudesNon documenté
etudesSupérieur
etudesTechnique / Professionnel
etudesSecondaire
groupe_ages65 ans et plus
groupe_ages45−64 ans
groupe_ages25−44 ans
sexeHomme
(Intercept)
0.3 1.0 3.0 10.0
Coefficient estimates and 95% confidence intervals

Figure 22.5: Odds Ratios associés à la pratique d’un sport avec


modelplot()

22.7 Afficher toutes les comparaisons


(pairwise contrasts)

Dans le tableau des résultats (Table 22.3), pour les variables


catégorielles, il importe de bien garder en mémoire que chaque
odds ratio doit se comparer à la valeur de référence. Ainsi, les
odds ratios affichés pour chaque classe d’âges correspondent à
une comparaison avec la classe d’âges de références, les 18-24
ans. La p-valeur associée nous indique quant à elle si cet odds
ratio est significativement de 1, donc si cette classe d’âges don-
nées se comporte différemment de celle de référence.
Mais cela ne nous dit nullement si les 65 ans et plus diffèrent
des 45-64 ans. Il est tout à fait possible de recalculer l’odds
ratio correspondant en rapport les odds ratio à la référence :
𝑂𝑅65+/45−64 = 𝑂𝑅65+/18−24 /𝑂𝑅45−64/18−24 .
Le package {emmeans} et sa fonction emmeans::emmeans() per-
mettent de recalculer toutes les combinaisons d’odds ratio (on
parle alors de pairwise contrasts) ainsi que leur intervalle de
confiance et la p-valeur correspondante.
On peut ajouter facilement34 cela au tableau produit 34
Cela nécessite néanmoins au mi-
nimum la version 1.11.0 du package
{[Link]} et la version 1.6.3
de {gtsummary}.
359
avec gtsummary::tbl_regression() en ajoutant l’option
add_pairwise_contrasts = TRUE.

mod |>
tbl_regression(
exponentiate = TRUE,
add_pairwise_contrasts = TRUE
) |>
bold_labels()

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

De même, on peur visualiser les coefficients avec la


même option dans ggstats::ggcoef_model()35 . On peut 35
Cela nécessite néanmoins au mi-
d’ailleurs choisir les variables concernées avec l’argument nimum la version 1.11.0 du package
{[Link]} et la version 0.2.0
pairwise_variables.
de {ggstats}.

mod |>
ggstats::ggcoef_model(
exponentiate = TRUE,
add_pairwise_contrasts = TRUE,
pairwise_variables = c("groupe_ages", "etudes")
)

360
Table 22.6: Facteurs associés à la pratique d’un sport (pairwise
contrasts)

**Caractéristique** **OR** **95% IC** **p-valeur**


__Sexe__
Homme / Femme 1,55 1,26 – 1,91 <0,001
__Groupe d’âges__
(25-44 ans) / (18-24 ans) 0,66 0,37 – 1,18 0,3
(45-64 ans) / (18-24 ans) 0,34 0,18 – 0,62 <0,001
(45-64 ans) / (25-44 ans) 0,51 0,38 – 0,70 <0,001
65 ans et plus / (18-24 ans) 0,25 0,12 – 0,51 <0,001
65 ans et plus / (25-44 ans) 0,38 0,24 – 0,61 <0,001
65 ans et plus / (45-64 ans) 0,74 0,47 – 1,17 0,3
__Niveau d’études__
Secondaire / Primaire 2,59 1,51 – 4,43 <0,001
(Technique / Professionnel) / Primaire 2,86 1,70 – 4,79 <0,001
(Technique / Professionnel) / Secondaire 1,10 0,74 – 1,64 >0,9
Supérieur / Primaire 6,63 3,89 – 11,3 <0,001
Supérieur / Secondaire 2,56 1,69 – 3,88 <0,001
Supérieur / (Technique / Professionnel) 2,32 1,61 – 3,36 <0,001
Non documenté / Primaire 8,59 3,49 – 21,1 <0,001
Non documenté / Secondaire 3,32 1,46 – 7,53 <0,001
Non documenté / (Technique / Professionnel) 3,01 1,38 – 6,56 0,001
Non documenté / Supérieur 1,30 0,58 – 2,90 >0,9
__Rapport à la religion__
Pratiquant occasionnel / Pratiquant regulier 0,98 0,57 – 1,68 >0,9
Appartenance sans pratique / Pratiquant regulier 0,99 0,60 – 1,63 >0,9
Appartenance sans pratique / Pratiquant occasionnel 1,02 0,68 – 1,52 >0,9
Ni croyance ni appartenance / Pratiquant regulier 0,81 0,47 – 1,40 0,9
Ni croyance ni appartenance / Pratiquant occasionnel 0,82 0,52 – 1,31 0,8
Ni croyance ni appartenance / Appartenance sans pratique 0,81 0,54 – 1,21 0,7
Rejet / Pratiquant regulier 0,68 0,30 – 1,54 0,8
Rejet / Pratiquant occasionnel 0,70 0,33 – 1,49 0,8
Rejet / Appartenance sans pratique 0,69 0,33 – 1,41 0,7
Rejet / Ni croyance ni appartenance 0,85 0,40 – 1,79 >0,9
NSP ou NVPR / Pratiquant regulier 0,92 0,29 – 2,97 >0,9
NSP ou NVPR / Pratiquant occasionnel 0,94 0,30 – 2,92 >0,9
NSP ou NVPR / Appartenance sans pratique 0,93 0,30 – 2,82 >0,9
NSP ou NVPR / Ni croyance ni appartenance 1,14 0,37 – 3,55 >0,9
NSP ou NVPR / Rejet 1,35 0,37 – 4,88 >0,9
__Heures de télévision / jour__ 0,89 0,83 – 0,95 <0,001

361
Femme
Sexe Homme (p<0.001***)
(25−44 ans) / (18−24 ans) (p=0.253)
Groupe d'âges (45−64 ans) / (18−24 ans) (p<0.001***)
(45−64 ans) / (25−44 ans) (p<0.001***)
65 ans et plus / (18−24 ans) (p<0.001***)
65 ans et plus / (25−44 ans) (p<0.001***)
65 ans et plus / (45−64 ans) (p=0.335)
Secondaire / Primaire (p<0.001***)
Niveau d'études (Technique / Professionnel) / Primaire (p<0.001***)
(Technique / Professionnel) / Secondaire (p=0.961)
Supérieur / Primaire (p<0.001***)
Supérieur / Secondaire (p<0.001***)
Supérieur / (Technique / Professionnel) (p<0.001***)
Non documenté / Primaire (p<0.001***)
Non documenté / Secondaire (p<0.001***)
Non documenté / (Technique / Professionnel) (p=0.001**)
Non documenté / Supérieur (p=0.905)
Pratiquant regulier
Rapport à la religion Pratiquant occasionnel (p=0.908)
Appartenance sans pratique (p=0.969)
Ni croyance ni appartenance (p=0.265)
Rejet (p=0.180)
NSP ou NVPR (p=0.838)
Heures de télévision / jour (p<0.001***)
0.1
1.0
10.0
OR

p <= 0.05 p > 0.05

Figure 22.6: Facteurs associés à la pratique d’un sport (pairwise


contrasts)

22.8 Identifier les variables ayant un effet


significatif

Pour les variables catégorielles à trois modalités ou plus, les p-


valeurs associées aux odds ratios nous indique si un odd ratio est
significativement différent de 1, par rapport à la modalité de ré-
férence. Mais cela n’indique pas si globalement une variable a un
effet significatif sur le modèle. Pour tester l’effet global d’une va-
riable, on peut avoir recours à la fonction car::Anova(). Cette
dernière va tour à tour supprimer chaque variable du modèle
et réaliser une analyse de variance (ANOVA) pour voir si la
variance change significativement.

car::Anova(mod)

Analysis of Deviance Table (Type II tests)

Response: sport
LR Chisq Df Pr(>Chisq)
sexe 17.309 1 3.176e-05 ***

362
groupe_ages 52.803 3 2.020e-11 ***
etudes 123.826 4 < 2.2e-16 ***
relig 4.232 5 0.5165401
[Link] 13.438 1 0.0002465 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Ainsi, dans le cas présent, la suppression de la variable relig


ne modifie significativement pas le modèle, indiquant l’absence
d’effet de cette variable.
Si l’on a recours à gtsummary::tbl_regression(),
on peut facilement ajouter les p-valeurs globales avec
gtsummary::add_global_p()36 . 36
Si l’on veut conserver les p-valeurs
individuelles associées à chaque odds
ratio, on ajoutera l’option keep =
mod |>
TRUE.
tbl_regression(exponentiate = TRUE) |>
bold_labels() |>
add_global_p()

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Ĺ Note

Concernant le test réalisé dans le cadre d’une Anova, il


existe trois tests différents que l’on présente comme le
type 1, le type 2 et le type 3 (ou I, II et III). Pour
une explication sur ces différents types, on pourra se ré-
férer (en anglais) à [Link]
/2011/03/02/anova-type-iiiiii-ss-explained/ ou encore
[Link]
/lm_cat_unbal_ss_explained.html.
Le type I n’est pas recommandé dans le cas présent car il
dépend de l’ordre dans lequel les différentes variables sont
testées.
Lorsqu’il n’y a pas d’interaction dans un modèle, le type II
serait à privilégier car plus puissant (nous aborderons les

363
Table 22.7: Ajout des p-valeurs globales

**Caractéristique** **OR** **95% IC** **p-valeur**


__Sexe__ <0,001
Femme — —
Homme 1,55 1,26 – 1,91
__Groupe d’âges__ <0,001
18-24 ans — —
25-44 ans 0,66 0,42 – 1,03
45-64 ans 0,34 0,21 – 0,54
65 ans et plus 0,25 0,15 – 0,43
__Niveau d’études__ <0,001
Primaire — —
Secondaire 2,59 1,77 – 3,83
Technique / Professionnel 2,86 1,98 – 4,17
Supérieur 6,63 4,55 – 9,80
Non documenté 8,59 4,53 – 16,6
__Rapport à la religion__ 0,5
Pratiquant regulier — —
Pratiquant occasionnel 0,98 0,68 – 1,42
Appartenance sans pratique 0,99 0,71 – 1,40
Ni croyance ni appartenance 0,81 0,55 – 1,18
Rejet 0,68 0,39 – 1,19
NSP ou NVPR 0,92 0,40 – 2,02
__Heures de télévision / jour__ 0,89 0,83 – 0,95 <0,001

364
interactions dans un prochain chapitre, cf. Chapitre 26).
En présence d’interactions, il est conseillé d’avoir plutôt
recours au type III. Cependant, en toute rigueur, pour
utiliser le type III, il faut que les variables catégorielles
soient codées en utilisant un contrastes dont la somme est
nulle (un contraste de type somme ou polynomial). Or,
par défaut, les variables catégorielles sont codées avec un
contraste de type traitement (nous aborderons les diffé-
rents types de contrastes plus tard, cf. Chapitre 25).
Par défaut, car::Anova() utilise le type II et
gtsummary::add_global_p() le type III. Dans les deux
cas, il est possible de préciser le type de test avec type =
"II" ou type = "III".
Dans le cas de notre exemple, un modèle simple sans in-
teraction, le type de test ne change pas les résultats.

22.9 Régressions logistiques univariées

Les usages varient selon les disciplines et les revues scienti-


fiques, mais il n’est pas rare de présenter, avant le modèle logis-
tique multivarié, une succession de modèles logistiques univariés
(i.e. avec une seule variable explicative à la fois) afin de présen-
ter les odds ratios et leur intervalle de confiance et p-valeur
associés avant l’ajustement multiniveau.
Afin d’éviter le code fastidieux consistant à réaliser chaque mo-
dèle un par un (par exemple glm(sport ~ sexe, family =
binomial, data = d)) puis à en fusionner les résultats, on
pourra tirer partie de gtsummary::tbl_uvregression() qui
permet de réaliser toutes ces régressions individuelles en une
fois et de les présenter dans un tableau synthétique.

d |>
tbl_uvregression(
y = sport,
include = c(sexe, groupe_ages, etudes, relig, [Link]),
method = glm,
[Link] = list(family = binomial),

365
Table 22.8: Régressions logistiques univariées

**Caractéristique** **N** **OR** **95% IC** **p-valeur**


__Sexe__ 2 000
Femme — —
Homme 1,47 1,22 – 1,77 <0,001
__Rapport à la religion__ 2 000
Pratiquant regulier — —
Pratiquant occasionnel 1,08 0,78 – 1,50 0,6
Appartenance sans pratique 1,31 0,98 – 1,78 0,071
Ni croyance ni appartenance 1,45 1,05 – 2,02 0,026
Rejet 1,19 0,72 – 1,95 0,5
NSP ou NVPR 0,93 0,44 – 1,88 0,8
__Heures de télévision / jour__ 1 995 0,79 0,74 – 0,84 <0,001
__Groupe d’âges__ 2 000
18-24 ans — —
25-44 ans 0,51 0,35 – 0,71 <0,001
45-64 ans 0,20 0,14 – 0,28 <0,001
65 ans et plus 0,10 0,07 – 0,15 <0,001
__Niveau d’études__ 2 000
Primaire — —
Secondaire 3,61 2,52 – 5,23 <0,001
Technique / Professionnel 4,75 3,42 – 6,72 <0,001
Supérieur 11,4 8,11 – 16,3 <0,001
Non documenté 26,2 15,7 – 44,9 <0,001

exponentiate = TRUE
) |>
bold_labels()

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

366
22.10 Présenter l’ensemble des résultats
dans un même tableau

La fonction gtsummary::tbl_merge() permet de fusionner plu-


sieurs tableaux (en tenant compte du nom des variables) et donc
de présenter les différents résultats de l’analyse descriptive, uni-
variée et multivariée dans un seul et même tableau.

tbl_desc <-
d |>
tbl_summary(
by = sport,
include = c(sexe, groupe_ages, etudes, relig, [Link]),
statistic = all_categorical() ~ "{p}% ({n}/{N})",
percent = "row",
digits = all_categorical() ~ c(1, 0, 0)
) |>
modify_column_hide("stat_1") |>
modify_header("stat_2" ~ "**Pratique d'un sport**")

tbl_uni <-
d |>
tbl_uvregression(
y = sport,
include = c(sexe, groupe_ages, etudes, relig, [Link]),
method = glm,
[Link] = list(family = binomial),
exponentiate = TRUE
) |>
modify_column_hide("stat_n")

tbl_multi <-
mod |>
tbl_regression(exponentiate = TRUE)

list(tbl_desc, tbl_uni, tbl_multi) |>


tbl_merge(
tab_spanner = c(
NA,

367
Table 22.9: tableau synthétique de l’analyse

**Caractéristique** **Pratique d’un sport** **OR** **95% IC** **p-valeur** **OR**


__Sexe__
Femme 32,2% (354/1 101) — — —
Homme 41,0% (369/899) 1,47 1,22 – 1,77 <0,001 1,55
__Groupe d’âges__
18-24 ans 65,7% (111/169) — — —
25-44 ans 49,2% (347/706) 0,51 0,35 – 0,71 <0,001 0,66
45-64 ans 27,4% (204/745) 0,20 0,14 – 0,28 <0,001 0,34
65 ans et plus 16,1% (61/380) 0,10 0,07 – 0,15 <0,001 0,25
__Niveau d’études__
Primaire 10,7% (50/466) — — —
Secondaire 30,2% (117/387) 3,61 2,52 – 5,23 <0,001 2,59
Technique / Professionnel 36,4% (216/594) 4,75 3,42 – 6,72 <0,001 2,86
Supérieur 57,8% (255/441) 11,4 8,11 – 16,3 <0,001 6,63
Non documenté 75,9% (85/112) 26,2 15,7 – 44,9 <0,001 8,59
__Rapport à la religion__
Pratiquant regulier 31,6% (84/266) — — —
Pratiquant occasionnel 33,3% (147/442) 1,08 0,78 – 1,50 0,6 0,98
Appartenance sans pratique 37,8% (287/760) 1,31 0,98 – 1,78 0,071 0,99
Ni croyance ni appartenance 40,1% (160/399) 1,45 1,05 – 2,02 0,026 0,81
Rejet 35,5% (33/93) 1,19 0,72 – 1,95 0,5 0,68
NSP ou NVPR 30,0% (12/40) 0,93 0,44 – 1,88 0,8 0,92
__Heures de télévision / jour__ 2,00 (1,00 – 3,00) 0,79 0,74 – 0,84 <0,001 0,89
Manquant 3

"**Régressions univariées**",
"**Régression multivariée**"
)
) |>
bold_labels()

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Pour visualiser chaque étape du code, vous pouvez consulter


le diaporama suivant : [Link]

368
R/analyses/ressources/[Link]

22.11 webin-R

La régression logistique est présentée sur YouTube dans le


webin-R #06 (régression logistique (partie 1)) et le le webin-R
#07 (régression logistique (partie 2)).
[Link]
[Link]

369
23 Sélection pas à pas d’un
modèle

Il est toujours tentant lorsque l’on recherche les facteurs as-


sociés à un phénomène d’inclure un nombre important de va-
riables explicatives potentielles dans son modèle de régression.
Cependant, un tel modèle n’est pas forcément le plus efficace
et certaines variables n’auront probablement pas d’effet signifi-
catif sur la variable d’intérêt.
Un autre problème potentiel est celui dur sur-ajustement ou sur-
appentissage. Un modèle sur-ajusté est un modèle statistique
qui contient plus de paramètres que ne peuvent le justifier les
données. Dès lors, il va être trop ajusté aux données observées
mais perdre en capacité de généralisation.
Pour une présentation didactique du cadre théorique de la sé-
lection de modèle, vous pouvez consulter en ligne le cours de L.
Rouvière sur la sélection/validation de modèles.
Les techniques de sélection pas à pas sont des approches visant à
améliorerun modèle explicatif. On part d’un modèle initial puis
on regarde s’il est possible d’améliorer le modèle en ajoutant
ou en supprimant une des variables du modèle pour obtenir
un nouveau modèle. Le processus est répété jusqu’à obtenir un
modèle final que l’on ne peut plus améliorer.

23.1 Données d’illustration

Pour illustrer ce chapitre, nous allons prendre un modèle logis-


tique inspiré de celui utilisé dans le chapitre sur la régression
logistique binaire (cf. Chapitre 22).

370
library(tidyverse)
library(labelled)
library(gtsummary)
theme_gtsummary_language(
"fr",
[Link] = ",",
[Link] = " "
)

data(hdv2003, package = "questionr")

d <-
hdv2003 |>
mutate(
sexe = sexe |> fct_relevel("Femme"),
groupe_ages = age |>
cut(
c(18, 25, 45, 65, 99),
right = FALSE,
[Link] = TRUE,
labels = c("18-24 ans", "25-44 ans",
"45-64 ans", "65 ans et plus")
),
etudes = nivetud |>
fct_recode(
"Primaire" = "N'a jamais fait d'etudes",
"Primaire" = "A arrete ses etudes, avant la derniere annee d'etudes primaires",
"Primaire" = "Derniere annee d'etudes primaires",
"Secondaire" = "1er cycle",
"Secondaire" = "2eme cycle",
"Technique / Professionnel" = "Enseignement technique ou professionnel court",
"Technique / Professionnel" = "Enseignement technique ou professionnel long",
"Supérieur" = "Enseignement superieur y compris technique superieur"
) |>
fct_na_value_to_level("Non documenté")
) |>
set_variable_labels(
sport = "Pratique un sport ?",
sexe = "Sexe",

371
groupe_ages = "Groupe d'âges",
etudes = "Niveau d'études",
relig = "Rapport à la religion",
[Link] = "Lit des bandes dessinées ?"
)

mod <- glm(


sport ~ sexe + groupe_ages + etudes + relig + [Link],
family = binomial,
data = d
)

23.2 Présentation de l’AIC

Il faut définir un critère pour déterminer la qualité d’un modèle.


L’un des plus utilisés est le Akaike Information Criterion ou
AIC. Il s’agit d’un compromis entre le nombre de degrés de
liberté (e.g. le nombre de coefficients dans le modèle) que l’on
cherche à minimiser et la variance expliquée que l’on cherche à
maximiser (la vraisemblance).
Plus précisément 𝐴𝐼𝐶 = 2𝑘 − 2𝑙𝑛(𝐿) où 𝐿 est le maximum de
la fonction de vraisemblance du modèle et 𝑘 le nombre de pa-
ramètres (i.e. de coefficients) du modèle. Plus l’AIC sera faible,
meilleur sera le modèle.
L’AIC d’un modèle s’obtient aisément avec AIC().

AIC(mod)

[1] 2257.101

23.3 Sélection pas à pas descendante

La fonction step() permet de sélectionner le meilleur modèle


par une procédure pas à pas descendante basée sur la minimisa-
tion de l’AIC. La fonction affiche à l’écran les différentes étapes
de la sélection et renvoie le modèle final.

372
mod2 <- step(mod)

Start: AIC=2257.1
sport ~ sexe + groupe_ages + etudes + relig + [Link]

Df Deviance AIC
- relig 5 2231.9 2251.9
- [Link] 1 2227.9 2255.9
<none> 2227.1 2257.1
- sexe 1 2245.6 2273.6
- groupe_ages 3 2280.1 2304.1
- etudes 4 2375.5 2397.5

Step: AIC=2251.95
sport ~ sexe + groupe_ages + etudes + [Link]

Df Deviance AIC
- [Link] 1 2232.6 2250.6
<none> 2231.9 2251.9
- sexe 1 2248.8 2266.8
- groupe_ages 3 2282.1 2296.1
- etudes 4 2380.5 2392.5

Step: AIC=2250.56
sport ~ sexe + groupe_ages + etudes

Df Deviance AIC
<none> 2232.6 2250.6
- sexe 1 2249.2 2265.2
- groupe_ages 3 2282.5 2294.5
- etudes 4 2385.2 2395.2

Le modèle initial a un AIC de 2257,1.


À la première étape, il apparaît que la suppression de la variable
relig permettrait diminuer l’AIC à 2251,9 et la suppression de
la variable [Link] de le diminuer à 2255,9. Le gain maxi-
mal est obtenu en supprimant relig et donc cette variable est
supprimée à ce stade. On peut noter que la suppression de la va-
riable entraîne de facto une augmentation des résidus (colonne

373
Deviance) et donc une baisse de la vraisemblance du modèle,
mais cela est compensé par la réduction du nombre de degrés
de liberté.
Le processus est maintenant répété. À la seconde étape, suppri-
mer [Link] permettrait de diminuer encore l’AIC à 2250,6
et cette variable est supprimée.
À la troisième étape, tout retrait d’une variable additionnelle
reviendrait à augmenter l’AIC.
Lors de la seconde étape, toute suppression d’une autre variable
ferait augmenter l’AIC. La procédure s’arrête donc.
L’objet mod2 renvoyé par step() est le modèle final.

mod2

Call: glm(formula = sport ~ sexe + groupe_ages + etudes, family = binomial,


data = d)

Coefficients:
(Intercept) sexeHomme
-1.2815 0.4234
groupe_ages25-44 ans groupe_ages45-64 ans
-0.3012 -0.9261
groupe_ages65 ans et plus etudesSecondaire
-1.2696 0.9670
etudesTechnique / Professionnel etudesSupérieur
1.0678 1.9955
etudesNon documenté
2.3192

Degrees of Freedom: 1999 Total (i.e. Null); 1991 Residual


Null Deviance: 2617
Residual Deviance: 2233 AIC: 2251

On peut effectuer une analyse de variance ou ANOVA pour


comparer les deux modèles avec la fonction anova().

anova(mod, mod2, test = "Chisq")

374
Analysis of Deviance Table

Model 1: sport ~ sexe + groupe_ages + etudes + relig + [Link]


Model 2: sport ~ sexe + groupe_ages + etudes
Resid. Df Resid. Dev Df Deviance Pr(>Chi)
1 1985 2227.1
2 1991 2232.6 -6 -5.4597 0.4863

Il n’y a pas de différences significatives entre nos deux modèles


(p=0,55). Autrement dit, notre second modèle explique tout
autant de variance que notre premier modèle, tout en étant
plus parcimonieux.

Ď Astuce

Une alternative à la fonction step() est la fonction


MASS::stepAIC() du package {MASS} qui fonctionne de
la même manière. Si cela ne change rien aux régressions
logistiques classiques, il arrive que pour certains types de
modèle la méthode step() ne soit pas disponible, mais
que MASS::stepAIC() puisse être utilisée à la place.

library(MASS)

Attachement du package : 'MASS'

L'objet suivant est masqué depuis 'package:gtsummary':

select

L'objet suivant est masqué depuis 'package:dplyr':

select

mod2bis <- stepAIC(mod)

Start: AIC=2257.1
sport ~ sexe + groupe_ages + etudes + relig + [Link]

375
Df Deviance AIC
- relig 5 2231.9 2251.9
- [Link] 1 2227.9 2255.9
<none> 2227.1 2257.1
- sexe 1 2245.6 2273.6
- groupe_ages 3 2280.1 2304.1
- etudes 4 2375.5 2397.5

Step: AIC=2251.95
sport ~ sexe + groupe_ages + etudes + [Link]

Df Deviance AIC
- [Link] 1 2232.6 2250.6
<none> 2231.9 2251.9
- sexe 1 2248.8 2266.8
- groupe_ages 3 2282.1 2296.1
- etudes 4 2380.5 2392.5

Step: AIC=2250.56
sport ~ sexe + groupe_ages + etudes

Df Deviance AIC
<none> 2232.6 2250.6
- sexe 1 2249.2 2265.2
- groupe_ages 3 2282.5 2294.5
- etudes 4 2385.2 2395.2

On peut facilement comparer visuellement deux modèles avec


ggstats::ggcoef_compare() de {ggstats}.

library(ggstats)
ggcoef_compare(
list("modèle complet" = mod, "modèle réduit" = mod2),
exponentiate = TRUE
)

376
Femme
Sexe Homme
18−24 ans
Groupe d'âges 25−44 ans
45−64 ans
65 ans et plus
Primaire
Niveau d'études Secondaire
Technique / Professionnel
Supérieur
Non documenté
Pratiquant regulier
Rapport à la religion Pratiquant occasionnel
Appartenance sans pratique
Ni croyance ni appartenance
Rejet
NSP ou NVPR
Non
Lit des bandes dessinées ? Oui
0.3 1.0 3.0 10.0
OR

modèle complet modèle réduit

p = 0.05 p > 0.05

Figure 23.1: Comparaison visuelle des deux modèles (dodge)

ggcoef_compare(
list("modèle complet" = mod, "modèle réduit" = mod2),
type = "faceted",
exponentiate = TRUE
)

modèle complet modèle réduit


Sexe Femme
Homme
Groupe d'âges 18−24 ans
25−44 ans
45−64 ans
65 ans et plus
Niveau d'études Primaire
Secondaire
Technique / Professionnel
Supérieur
Non documenté
Rapport à la religion Pratiquant regulier
Pratiquant occasionnel
Appartenance sans pratique
Ni croyance ni appartenance
Rejet
NSP ou NVPR
Lit des bandes dessinées ? Non
Oui
[Link].0 [Link].0
OR

p = 0.05 p > 0.05

Figure 23.2: Comparaison visuelle des deux modèles (faceted)

377
23.4 Sélection pas à pas ascendante

Pour une approche ascendante, nous allons partir d’un modèle


vide, c’est-à-dire d’un modèle sans variable explicative avec sim-
plement un intercept.

mod_vide <- glm(


sport ~ 1,
family = binomial,
data = d
)

Nous allons ensuite passer ce modèle vide à step() et préciser,


via un élément nommé upper dans une liste passée à l’argument
scope, la formule du modèle maximum à considérer. Nous pré-
cisons direction = "forward" pour indiquer que nous souhai-
tons une procédure ascendante.

mod3 <- step(


mod_vide,
direction = "forward",
scope = list(
upper = ~ sexe + groupe_ages + etudes + relig + [Link]
)
)

Start: AIC=2619.11
sport ~ 1

Df Deviance AIC
+ etudes 4 2294.9 2304.9
+ groupe_ages 3 2405.4 2413.4
+ sexe 1 2600.2 2604.2
+ [Link] 1 2612.7 2616.7
<none> 2617.1 2619.1
+ relig 5 2608.8 2620.8

Step: AIC=2304.92
sport ~ etudes

378
Df Deviance AIC
+ groupe_ages 3 2249.2 2265.2
+ sexe 1 2282.5 2294.5
<none> 2294.9 2304.9
+ [Link] 1 2294.7 2306.7
+ relig 5 2293.0 2313.0

Step: AIC=2265.17
sport ~ etudes + groupe_ages

Df Deviance AIC
+ sexe 1 2232.6 2250.6
<none> 2249.2 2265.2
+ [Link] 1 2248.8 2266.8
+ relig 5 2246.0 2272.0

Step: AIC=2250.56
sport ~ etudes + groupe_ages + sexe

Df Deviance AIC
<none> 2232.6 2250.6
+ [Link] 1 2231.9 2251.9
+ relig 5 2227.9 2255.9

Cette fois-ci, à chaque étape, la fonction step() évalue le gain


à ajouter chaque variable dans le modèle, ajoute la variable la
plus pertinente, pour recommence le processus jusqu’à ce qu’il
n’y ait plus de gain à ajouter une variable au modèle. Notons
que nous aboutissons ici au même résultat.

Ď Astuce

Nous aurions pu nous passer de préciser direction =


"forward". Dans cette situation, step() regarde simul-
tanément les gains à ajouter une variable additionnelle
au modèle et à supprimer une variable déjà inclue pour .
Lorsque l’on part d’un modèle vide, cela ne change rien
au résultat.

379
mod3 <- step(
mod_vide,
scope = list(
upper = ~ sexe + groupe_ages + etudes + relig + [Link]
)
)

Start: AIC=2619.11
sport ~ 1

Df Deviance AIC
+ etudes 4 2294.9 2304.9
+ groupe_ages 3 2405.4 2413.4
+ sexe 1 2600.2 2604.2
+ [Link] 1 2612.7 2616.7
<none> 2617.1 2619.1
+ relig 5 2608.8 2620.8

Step: AIC=2304.92
sport ~ etudes

Df Deviance AIC
+ groupe_ages 3 2249.2 2265.2
+ sexe 1 2282.5 2294.5
<none> 2294.9 2304.9
+ [Link] 1 2294.7 2306.7
+ relig 5 2293.0 2313.0
- etudes 4 2617.1 2619.1

Step: AIC=2265.17
sport ~ etudes + groupe_ages

Df Deviance AIC
+ sexe 1 2232.6 2250.6
<none> 2249.2 2265.2
+ [Link] 1 2248.8 2266.8
+ relig 5 2246.0 2272.0
- groupe_ages 3 2294.9 2304.9
- etudes 4 2405.4 2413.4

380
Step: AIC=2250.56
sport ~ etudes + groupe_ages + sexe

Df Deviance AIC
<none> 2232.6 2250.6
+ [Link] 1 2231.9 2251.9
+ relig 5 2227.9 2255.9
- sexe 1 2249.2 2265.2
- groupe_ages 3 2282.5 2294.5
- etudes 4 2385.2 2395.2

23.5 Forcer certaines variables dans le


modèle réduit

Même si l’on a recourt à step(), on peut vouloir forcer la


présence de certaines variables dans le modèle, même si leur
suppression minimiserait l’AIC. Par exemple, si l’on a des hy-
pothèses spécifiques pour ces variables et que l’on a intérêt à
montrer qu’elles n’ont pas d’effet dans le modèle multivarié.
Supposons que nous avons une hypothèse sur le lien entre la
pratique d’un sport et la lecture de bandes dessinées. Nous
souhaitons donc forcer la présence de la variable [Link] dans
le modèle final. Cette fois-ci, nous allons indiquer, via la liste
passée à scope, un élément lower indiquant le modèle minimum
souhaité. Toutes les variables de ce modèle minimum seront
donc conserver dans le modèle final.

mod4 <- step(


mod,
scope = list(
lower = ~ [Link]
)
)

Start: AIC=2257.1
sport ~ sexe + groupe_ages + etudes + relig + [Link]

381
Df Deviance AIC
- relig 5 2231.9 2251.9
<none> 2227.1 2257.1
- sexe 1 2245.6 2273.6
- groupe_ages 3 2280.1 2304.1
- etudes 4 2375.5 2397.5

Step: AIC=2251.95
sport ~ sexe + groupe_ages + etudes + [Link]

Df Deviance AIC
<none> 2231.9 2251.9
- sexe 1 2248.8 2266.8
- groupe_ages 3 2282.1 2296.1
- etudes 4 2380.5 2392.5

Cette fois-ci, nous constatons que la fonction step() n’a pas


considéré la suppression éventuelle de la variable [Link] qui
est donc conservée.

mod4$formula

sport ~ sexe + groupe_ages + etudes + [Link]

23.6 Minimisation du BIC

Un critère similaire à l’AIC est le critère BIC (Bayesian In-


formation Criterion) appelé aussi SBC (Schwarz information
criterion).
Sa formule est proche de celle de l’AIC : 𝐵𝐼𝐶 = 𝑙𝑛(𝑛)𝑘−2𝑙𝑛(𝐿)
où 𝑛 correspond au nombre d’observations dans l’échantillon.
Par rapport à l’AIC, il pénalise donc plus le nombre de degrés
de liberté du modèle.
Pour réaliser une sélection pas à pas par optimisation du BIC,
on appellera step() en ajoutant l’argument k = log(n) où n
est le nombre d’observations inclues dans le modèle. Par dé-
faut, un modèle est calculé en retirant les observations pour

382
lesquelles des données sont manquantes. Dès lors, pour obte-
nir le nombre exact d’observations incluses dans le modèle, on
peut utiliser la syntaxe mod |> [Link]() |> nrow(),
[Link]() renvoyant la matrice de données ayant servi
au calcul du modèle et nrow() le nombre de lignes.

mod5 <- mod |>


step(
k = mod |> [Link]() |> nrow() |> log()
)

Start: AIC=2341.11
sport ~ sexe + groupe_ages + etudes + relig + [Link]

Df Deviance AIC
- relig 5 2231.9 2308.0
- [Link] 1 2227.9 2334.3
<none> 2227.1 2341.1
- sexe 1 2245.6 2352.0
- groupe_ages 3 2280.1 2371.3
- etudes 4 2375.5 2459.1

Step: AIC=2307.96
sport ~ sexe + groupe_ages + etudes + [Link]

Df Deviance AIC
- [Link] 1 2232.6 2301.0
<none> 2231.9 2308.0
- sexe 1 2248.8 2317.2
- groupe_ages 3 2282.1 2335.3
- etudes 4 2380.5 2426.1

Step: AIC=2300.97
sport ~ sexe + groupe_ages + etudes

Df Deviance AIC
<none> 2232.6 2301.0
- sexe 1 2249.2 2310.0
- groupe_ages 3 2282.5 2328.1
- etudes 4 2385.2 2423.2

383
23.7 Afficher les indicateurs de performance

Il existe plusieurs indicateurs de performance ou qualité d’un


modèle. Pour les calculer/afficher (dont l’AIC et le BIC),
on pourra avoir recours à broom::glance() ou encore à
performance::model_performance().

mod |> broom::glance()

# A tibble: 1 x 8
[Link] [Link] logLik AIC BIC deviance [Link] nobs
<dbl> <int> <dbl> <dbl> <dbl> <dbl> <int> <int>
1 2617. 1999 -1114. 2257. 2341. 2227. 1985 2000

mod |> performance::model_performance()

# Indices of model performance

AIC | AICc | BIC | Tjur's R2 | RMSE | Sigma | Log_loss | Score_log | Score_spher


-----------------------------------------------------------------------------------------------
2257.101 | 2257.343 | 2341.115 | 0.183 | 0.434 | 1.000 | 0.557 | -Inf | 0

Le fonction performance::compare_performance() permet


de comparer rapidement plusieurs modèles.

performance::compare_performance(mod, mod2, mod4)

# Comparison of Model Performance Indices

Name | Model | AIC (weights) | AICc (weights) | BIC (weights) | Tjur's R2 | RMSE | Sigma | L
-----------------------------------------------------------------------------------------------
mod | glm | 2257.1 (0.025) | 2257.3 (0.023) | 2341.1 (<.001) | 0.183 | 0.434 | 1.000 |
mod2 | glm | 2250.6 (0.651) | 2250.7 (0.654) | 2301.0 (0.971) | 0.181 | 0.435 | 1.000 |
mod4 | glm | 2252.0 (0.325) | 2252.1 (0.323) | 2308.0 (0.029) | 0.181 | 0.435 | 1.000 |

Si l’on souhaite afficher l’AIC (ainsi que d’autres statistiques


globales du modèle) en note du tableau des coefficients, on
pourra utiliser gtsummary::add_glance_source_note().

384
mod2 |>
tbl_regression(exponentiate = TRUE) |>
bold_labels() |>
add_glance_source_note()

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Table 23.1: Modèle obtenu après réduction du nombre de va-


riables

Caractéristique OR 95% IC p-valeur


Sexe
Femme — —
Homme 1,53 1,25 – 1,87 <0,001
Groupe d’âges
18-24 ans — —
25-44 ans 0,74 0,48 – 1,15 0,2
45-64 ans 0,40 0,25 – 0,62 <0,001
65 ans et plus 0,28 0,17 – 0,47 <0,001
Niveau d’études
Primaire — —
Secondaire 2,63 1,80 – 3,88 <0,001
Technique / Professionnel 2,91 2,03 – 4,22 <0,001
Supérieur 7,36 5,10 – 10,8 <0,001
Non documenté 10,2 5,43 – 19,4 <0,001

23.8 Sélection pas à pas et valeurs


manquantes

Si certaines de nos variables explications contiennent des va-


leurs manquantes (NA), cela peut entraîner des erreurs au mo-
ment d’avoir recours à step(), car le nombre d’observations
dans le modèle va changer si on retire du modèle une variable
explicative avec des valeurs manquantes.

385
Prenons un exemple, en ajoutant des valeurs manquantes à la
variable relig (pour cela nous allons recoder les refus et les ne
sait pas en NA).

d$relig_na <-
d$relig |>
fct_recode(
NULL = "Rejet",
NULL = "NSP ou NVPR"
)

mod_na <- glm(


sport ~ sexe + groupe_ages + etudes + relig_na + [Link],
family = binomial,
data = d
)

Au moment d’exécuter step() nous obtenons l’erreur mention-


née précédemment.

step(mod_na)

Start: AIC=2096.64
sport ~ sexe + groupe_ages + etudes + relig_na + [Link]

Df Deviance AIC
- relig_na 3 2073.2 2093.2
- [Link] 1 2072.2 2096.2
<none> 2070.6 2096.6
- sexe 1 2088.6 2112.6
- groupe_ages 3 2118.0 2138.0
- etudes 4 2218.1 2236.1

Error in step(mod_na): le nombre de lignes utilisées a changé : supprimer les valeurs manquante

Pas d’inquiétude ! Il y a moyen de s’en sortir en adoptant la


stratégie suivante :

1. créer une copie du jeu de données avec uniquement des


observations sans valeur manquante pour nos variables
explicatives ;

386
2. calculer notre modèle complet à partir de ce jeu de don-
nées ;
3. appliquer step() ;
4. recalculer le modèle réduit en repartant du jeu de données
complet.

Première étape, ne garder que les observations complètes à


l’aide de tidyr::drop_na(), en lui indiquant la liste des va-
riables dans lesquelles vérifier la présence ou non de NA.

d_complet <- d |>


drop_na(sexe, groupe_ages, etudes, relig_na, [Link])

Deuxième étape, calculons le modèle complet avec ce jeu don-


nées.

mod_na_alt <- glm(


sport ~ sexe + groupe_ages + etudes + relig_na +[Link],
family = binomial,
data = d_complet
)

Le modèle mod_na_alt est tout à fait identique au modèle


mod_na, car glm() supprime de lui-même les valeurs man-
quantes quand elles existent. Nous pouvons maintenant utiliser
step().

mod_na_reduit <- step(mod_na_alt)

Start: AIC=2096.64
sport ~ sexe + groupe_ages + etudes + relig_na + [Link]

Df Deviance AIC
- relig_na 3 2073.2 2093.2
- [Link] 1 2072.2 2096.2
<none> 2070.6 2096.6
- sexe 1 2088.6 2112.6
- groupe_ages 3 2118.0 2138.0
- etudes 4 2218.1 2236.1

387
Step: AIC=2093.19
sport ~ sexe + groupe_ages + etudes + [Link]

Df Deviance AIC
- [Link] 1 2074.6 2092.6
<none> 2073.2 2093.2
- sexe 1 2090.2 2108.2
- groupe_ages 3 2118.5 2132.5
- etudes 4 2221.4 2233.4

Step: AIC=2092.59
sport ~ sexe + groupe_ages + etudes

Df Deviance AIC
<none> 2074.6 2092.6
- sexe 1 2091.1 2107.1
- groupe_ages 3 2119.6 2131.6
- etudes 4 2227.2 2237.2

Cela s’exécute sans problème car tous les sous-modèles sont cal-
culés à partir de d_complet et donc ont bien le même nombre
d’observations. Cependant, dans notre modèle réduit, on a re-
tiré 137 observations en raison d’une valeur manquante sur la
variable relig_na, variable qui n’est plus présente dans notre
modèle réduit. Il serait donc pertinent de réintégrer ces obser-
vations.
Nous allons donc recalculer le modèle réduit mais à partir
de d. Inutile de recopier à la main la formule du mo-
dèle réduit, car nous pouvons l’obtenir directement avec
mod_na_reduit$formula.

mod_na_reduit2 <- glm(


mod_na_reduit$formula,
family = binomial,
data = d
)

Attention : mod_na_reduit et mod_na_reduit2 ne sont pas


identiques puisque le second a été calculé sur un plus grand

388
nombre d’observations, ce qui change très légèrement les valeurs
des coefficients.

Ď Astuce

Pour automatiser l’ensemble de ce processus, on peut co-


pier/coller le code de la fonction générique suivante :

step_with_na <- function(model, ...) {


# refit the model without NAs
model_no_na <- update(model, data = [Link](model))
# apply step()
model_simplified <- step(model_no_na, ...)
# recompute simplified model using full data
update(model, formula = terms(model_simplified))
}

Elle réalise l’ensemble des opérations décrites plus haut en


profitant de la flexibilité offerte par la fonction update().
La fonction [Link]() permet de récupérer le jeu
de données utilisé par un modèle (et dans lequel les lignes
incomplètes ont été supprimées). La fonction terms() per-
met de récupérer l’équation du modèle.
Attention : il s’agit d’une fonction expérimentale et elle
n’est peut-être pas compatible avec tous les types de mo-
dèles. Elle a été testée avec les modèles lm(), glm() et
nnet::multinom().

mod_na_reduit_direct <- step_with_na(mod_na, trace = 0)

Le résultat obtenu est strictement identique.

anova(mod_na_reduit2, mod_na_reduit_direct)

Analysis of Deviance Table

Model 1: sport ~ sexe + groupe_ages + etudes


Model 2: sport ~ sexe + groupe_ages + etudes
Resid. Df Resid. Dev Df Deviance
1 1991 2232.6
2 1991 2232.6 0 0

389
24 Prédictions marginales,
contrastes marginaux &
effets marginaux

Á Avertissement

Ce chapitre nécessite une version récente de


{[Link]} (version � 1.12.0), de {gtsummary}
(version � 1.6.3), de {ggstats} (version � 0.2.1) et de
{marginaleffects} (version � 0.10.0).

Les coefficients d’une régression multivariée ne sont pas tou-


jours facile à interpréter car ils ne sont pas forcément exprimés
dans la même dimension que la variable d’intérêt. C’est no-
tamment le cas pour une régression logistique binaire (cf. Cha-
pitre 22). Comment traduire la valeur d’un odds ratio en écart
de probabilité ?
Dans certaines disciplines, notamment en économétrie, on pré-
fère souvent présenter les effets marginaux qui tentent de tra-
duire les résultats du modèle dans la dimension de la variable
d’intérêt. Plusieurs approches existent et l’on trouve dans la lit-
térature des expressions telles que effets marginaux, effets sta-
tistiques, moyennes marginales, pentes marginales, effets mar-
ginaux à la moyenne, et autres expressions similaires.
Différents auteurs peuvent utiliser la même expression pour
désigner des indicateurs différents, ou bien des manières dif-
férentes de les calculer.

390
Ĺ Note

Si vous n’êtes pas familier des estimations marginales


et souhaitez aller à l’essentiel, vous pouvez, en première
lecture, vous concentrer sur les prédications marginales
moyennes et les contrastes marginaux moyens, avant
d’explorer les autres variantes.

24.1 Terminologie

Dans ce guide, nous avons décidé d’adopter une terminologie


consistante avec celle du package {[Link]}, elle même
basée sur celle du package {marginaleffects}, dont la pre-
mière version a été publié en septembre 2021, et avec le billet
d’Andrew Heiss intitulé Marginalia et publié en mai 2022.
Lorsque l’on utilise un modèle ajusté pour prédire l’outcome
selon certaines combinaisons de valeurs des régresseurs / va-
riables explicatives, par exemple leurs valeurs observées ou leur
moyenne, on obtient des prédictions ajustées. Lorsque ces
dernières sont moyennées selon un régresseur spécifique, nous
parlerons alors de prédictions marginales.
Les contrastes marginaux correspondent au calcul d’une dif-
férence entre des prédictions marginales, que ce soit pour une
variable catégorielle (e.g. différence entre deux modalités) ou
pour une variable continue (différence observée au niveau de
l’outcome pour un certain changement du prédicteur).
Les pentes marginales ou effets marginaux sont définis,
pour des variables continues, comme la dérivée partielle (slope)
de l’équation de régression pour certains valeurs de la variable
explicative. Dit autrement, un effet marginal correspond à la
pente locale de la fonction de régression pour certaines valeurs
choisies d’un prédicteur continue. De manière pratique, les ef-
fets marginaux sont similaires aux contrastes marginaux.
L’ensemble de ces indicateurs marginaux se calculent pour cer-
taines valeurs typiques des variables explicatives, avec plusieurs
approches possibles pour définir des valeurs typiques : moyenne
/ mode, valeurs observées, valeurs personnalisées…

391
Nous présenterons ces différents concepts plus en détail dans la
suite de ce chapitre.
Plusieurs packages proposent des fonctions pour le calcul
d’estimations marginales, {marginaleffects}, {emmeans},
{margins}, {effects}, ou encore {ggeffects}, chacun avec
des approches et un vocabulaire légèrement différent.
Le package {[Link]} fournit plusieurs tidiers qui
permettent d’appeler les fonctions de ces autres packages
et de renvoyer un tableau de données compatible avec
la fonction [Link]::tidy_plus_plus() et dès
lors de pouvoir générer un tableau mis en forme avec
gtsummary::tbl_regression() ou un graphique avec
ggstats::ggcoef_model().

24.2 Données d’illustration

Pour illustrer ce chapitre, nous allons prendre un modèle logis-


tique issu du chapitre sur la régression logistique binaire (cf.
Chapitre 22).

library(tidyverse)
library(labelled)
library(gtsummary)
theme_gtsummary_language(
"fr",
[Link] = ",",
[Link] = " "
)

data(hdv2003, package = "questionr")

d <-
hdv2003 |>
mutate(
sexe = sexe |> fct_relevel("Femme"),
groupe_ages = age |>
cut(
c(18, 25, 45, 65, 99),

392
right = FALSE,
[Link] = TRUE,
labels = c("18-24 ans", "25-44 ans",
"45-64 ans", "65 ans et plus")
),
etudes = nivetud |>
fct_recode(
"Primaire" = "N'a jamais fait d'etudes",
"Primaire" = "A arrete ses etudes, avant la derniere annee d'etudes primaires",
"Primaire" = "Derniere annee d'etudes primaires",
"Secondaire" = "1er cycle",
"Secondaire" = "2eme cycle",
"Technique / Professionnel" = "Enseignement technique ou professionnel court",
"Technique / Professionnel" = "Enseignement technique ou professionnel long",
"Supérieur" = "Enseignement superieur y compris technique superieur"
) |>
fct_na_value_to_level("Non documenté")
) |>
set_variable_labels(
sport = "Pratique un sport ?",
sexe = "Sexe",
groupe_ages = "Groupe d'âges",
etudes = "Niveau d'études",
[Link] = "Heures de télévision / jour"
)

mod <- glm(


sport ~ sexe + groupe_ages + etudes + [Link],
family = binomial,
data = d
)

mod |>
tbl_regression(exponentiate = TRUE) |>
bold_labels()

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

393
Table 24.1: Odds Ratios du modèle logistique

Caractéristique OR 95% IC p-valeur


Sexe
Femme — —
Homme 1,52 1,24 – 1,87 <0,001
Groupe d’âges
18-24 ans — —
25-44 ans 0,68 0,43 – 1,06 0,084
45-64 ans 0,36 0,23 – 0,57 <0,001
65 ans et plus 0,27 0,16 – 0,46 <0,001
Niveau d’études
Primaire — —
Secondaire 2,54 1,73 – 3,75 <0,001
Technique / Professionnel 2,81 1,95 – 4,10 <0,001
Supérieur 6,55 4,50 – 9,66 <0,001
Non documenté 8,54 4,51 – 16,5 <0,001
Heures de télévision / jour 0,89 0,83 – 0,95 <0,001

Il faut se rappeler que pour calculer le modèle, les observa-


tions ayant au moins une valeur manquante ont été exclues.
Le modèle n’a donc pas été calculé sur 2000 observations
(nombre de lignes de hdv2003) mais sur 1995. On peut obtenir
le tableau de données du modèle (model frame), qui ne
contient que les variables et les observations utilisées, avec
[Link]::model_get_model_frame().

mf <- mod %>%


[Link]::model_get_model_frame()
nrow(mf)

[1] 1995

colnames(mf)

[1] "sport" "sexe" "groupe_ages" "etudes" "[Link]"

394
24.3 Prédictions marginales

24.3.1 Prédictions marginales moyennes

Pour illustrer et mieux comprendre ce que représente la dif-


férence entre les femmes et les hommes, nous allons effectuer
des prédictions avec notre modèle en ne faisant varier que la
variable sexe.
Une première approche consiste à dupliquer nos données obser-
vées et à supposer que tous les individus sont des femmes, puis
à supposer que tous les individus sont des hommes.

mf_femmes <- mf |> mutate(sexe = "Femme")


mf_hommes <- mf |> mutate(sexe = "Homme")

Nos deux jeux de données sont donc identiques pour toutes les
autres variables et ne varient que pour le sexe. Nous pouvons
maintenant prédire, à partir de notre modèle ajusté, la proba-
bilité de faire du sport de chacun des individus de ces deux
nouveaux jeux de données, puis à en calculer la moyenne.

mod |> predict(type = "response", newdata = mf_femmes) |> mean()

[1] 0.324814

mod |> predict(type = "response", newdata = mf_hommes) |> mean()

[1] 0.4036624

Nous obtenons ainsi des prédictions marginales moyennes,


average marginal predictions en anglais, de respectivement 32%
et 40% pour les femmes et pour les hommes.
Le même résultat, avec en plus un intervalle de confiance, peut
s’obtenir avec marginaleffects::predictions().

395
library(marginaleffects)
mod |>
predictions(variables = "sexe", by = "sexe", type = "response")

sexe Estimate Std. Error z Pr(>|z|) S 2.5 % 97.5 %


Femme 0.325 0.0130 25.0 <0.001 456.7 0.299 0.350
Homme 0.404 0.0147 27.5 <0.001 549.0 0.375 0.432

Columns: sexe, estimate, [Link], statistic, [Link], [Link], [Link], [Link]


Type: response

Pour une variable continue, on peut procéder de la même ma-


nière en générant des prédictions marginales pour certaines va-
leurs de la variable. Par défaut, marginaleffects::predictions()
réalise des prédictions selon les 5 nombres de Tukey (Tukey’s
five numbers, à savoir minimum, premier quartile, médiane,
troisième quartile et maximum).

mod |>
predictions(variables = "[Link]", by = "[Link]", type = "response")

[Link] Estimate Std. Error z Pr(>|z|) S 2.5 % 97.5 %


0 0.410 0.01711 23.96 <0.001 419.0 0.3764 0.443
1 0.386 0.01220 31.64 <0.001 727.2 0.3621 0.410
2 0.363 0.00991 36.58 <0.001 970.7 0.3432 0.382
3 0.340 0.01145 29.66 <0.001 639.9 0.3173 0.362
12 0.168 0.04220 3.99 <0.001 13.9 0.0855 0.251

Columns: [Link], estimate, [Link], statistic, [Link], [Link], [Link], [Link]


Type: response

Le package {[Link]} fournit la fonction [Link]::tidy_marginal_predictions()


qui génèrent les prédictions marginales de chaque va-
riable37 avec marginaleffects::predictions() et renvoie 37
La fonction
les résultat dans un format directement utilisable avec [Link]::tidy_marginal_predictions()
peut également gérer des combinai-
gtsummary::tbl_regression().
sons de variables ou interactions,
voir Chapitre 26).

396
Ĺ Note

Il est à noter que [Link]::tidy_marginal_predictions()


renvoie des p-valeurs qui, par défaut, teste si les valeurs
prédites sont différentes de 0 (sur l’échelle de la fonc-
tion de lien, donc différentes de 50% dans le cas
d’une régression logistique). Ce type de tests n’est
pas vraiment pertinent dans le cas présent. On peut
facilement masquer la colonne des p-valeurs avec
modify_column_hide("[Link]").

mod |>
tbl_regression(
tidy_fun = [Link]::tidy_marginal_predictions,
type = "response",
estimate_fun = scales::label_percent(accuracy = 0.1)
) |>
bold_labels() |>
modify_column_hide("[Link]")

Table printed with `knitr::kable()`, not {gt}. Learn why at


[Link]
To suppress this message, include `message = FALSE` in code chunk header.

Table 24.2: Prédictions marginales moyennes

Prédictions
Caractéristique Marginales Moyennes 95% IC
Sexe
Femme 32.5% 29.9% –
35.0%
Homme 40.4% 37.5% –
43.2%
Groupe d’âges
25-44 ans 42.7% 39.3% –
46.2%
18-24 ans 51.2% 42.2% –
60.1%

397
Prédictions
Caractéristique Marginales Moyennes 95% IC
45-64 ans 29.9% 26.6% –
33.2%
65 ans et plus 24.9% 19.7% –
30.0%
Niveau d’études
Supérieur 53.2% 48.4% –
57.9%
Non documenté 59.2% 47.0% –
71.5%
Primaire 16.1% 11.9% –
20.4%
Technique / 34.0% 30.3% –
Professionnel 37.7%
Secondaire 31.8% 27.2% –
36.4%
Heures de
télévision / jour
0 41.0% 37.6% –
44.3%
1 38.6% 36.2% –
41.0%
2 36.3% 34.3% –
38.2%
3 34.0% 31.7% –
36.2%
12 16.8% 8.6% –
25.1%

La fonction [Link]::plot_marginal_predictions()
permet de visualiser les prédictions marginales à la moyenne en
réalisant une liste de graphiques, un par variable, que nous pou-
vons combiner avec patchwork::wrap_plots(). L’opérateur &
permet d’appliquer une fonction de {ggplot2} à chaque sous-
graphique. Ici, nous allons uniformiser l’axe des y.

p <- mod |>


[Link]::plot_marginal_predictions(type = "response") |>

398
patchwork::wrap_plots() &
scale_y_continuous(
limits = c(0, .8),
labels = scales::label_percent()
)

80% 80%

60% 60%

40% 40%

20% 20%

0% 0%
Femme Homme 18−24 ans
25−44 ans
45−64 ans
65 ans et plus
Sexe Groupe d'âges
80% 80%

60% 60%

40% 40%

20% 20%

0% 0%
Primaire
Secondaire
Technique / Professionnel
Supérieur
Non documenté 0.0 2.5 5.0 7.5 10.0 12.5
Niveau d'études Heures de télévision / jour

Figure 24.1: Prédictions marginales moyennes

Il est ici difficile de lire les étiquettes de la variable etudes. Nous


pouvons éventuellement inverser l’axe des x et celui des y avec
ggplot2::coord_flip().

p & coord_flip()

399
65 ans et plus

Groupe d'âges
Homme
45−64 ans
Sexe

2