Guide R
Guide R
Joseph Larmarange
17 août 2023
Table des matières
Préface 12
Remerciements . . . . . . . . . . . . . . . . . . . . . . 14
Licence . . . . . . . . . . . . . . . . . . . . . . . . . . 14
I Bases du langage 15
1 Packages 16
1.1 Installation (CRAN) . . . . . . . . . . . . . . . . 17
1.2 Chargement . . . . . . . . . . . . . . . . . . . . . 17
1.3 Mise à jour . . . . . . . . . . . . . . . . . . . . . 18
1.4 Installation depuis GitHub . . . . . . . . . . . . . 19
1.5 Le tidyverse . . . . . . . . . . . . . . . . . . . . . 20
2 Vecteurs 23
2.1 Types et classes . . . . . . . . . . . . . . . . . . . 23
2.2 Création d’un vecteur . . . . . . . . . . . . . . . 24
2.3 Longueur d’un vecteur . . . . . . . . . . . . . . . 27
2.4 Combiner des vecteurs . . . . . . . . . . . . . . . 28
2.5 Vecteurs nommés . . . . . . . . . . . . . . . . . . 28
2.6 Indexation par position . . . . . . . . . . . . . . 30
2.7 Indexation par nom . . . . . . . . . . . . . . . . 31
2.8 Indexation par condition . . . . . . . . . . . . . . 32
2.9 Assignation par indexation . . . . . . . . . . . . 36
2.10 En résumé . . . . . . . . . . . . . . . . . . . . . . 37
2.11 webin-R . . . . . . . . . . . . . . . . . . . . . . . 38
3 Listes 39
3.1 Propriétés et création . . . . . . . . . . . . . . . 39
3.2 Indexation . . . . . . . . . . . . . . . . . . . . . . 42
3.3 En résumé . . . . . . . . . . . . . . . . . . . . . . 46
3.4 webin-R . . . . . . . . . . . . . . . . . . . . . . . 46
2
4 Tableaux de données 47
4.1 Propriétés et création . . . . . . . . . . . . . . . 47
4.2 Indexation . . . . . . . . . . . . . . . . . . . . . . 49
4.3 Afficher les données . . . . . . . . . . . . . . . . . 53
4.4 En résumé . . . . . . . . . . . . . . . . . . . . . . 62
4.5 webin-R . . . . . . . . . . . . . . . . . . . . . . . 62
5 Tibbles 63
5.1 Le concept de tidy data . . . . . . . . . . . . . . 63
5.2 tibbles : des tableaux de données améliorés . . . 63
5.3 Données et tableaux imbriqués . . . . . . . . . . 68
6 Attributs 71
II Manipulation de données 74
7 Le pipe 75
7.1 Le pipe natif de R : |> . . . . . . . . . . . . . . . 76
7.2 Le pipe du tidyverse : %>% . . . . . . . . . . . . . 77
7.3 Vaut-il mieux utiliser |> ou %>% ? . . . . . . . . . 78
7.4 Accéder à un élément avec purrr::pluck() et
purrr::chuck() . . . . . . . . . . . . . . . . . . 79
8 dplyr 82
8.1 Opérations sur les lignes . . . . . . . . . . . . . . 83
8.1.1 filter() . . . . . . . . . . . . . . . . . . . . 83
8.1.2 slice() . . . . . . . . . . . . . . . . . . . . 88
8.1.3 arrange() . . . . . . . . . . . . . . . . . . 89
8.1.4 slice_sample() . . . . . . . . . . . . . . . 91
8.1.5 distinct() . . . . . . . . . . . . . . . . . . 92
8.2 Opérations sur les colonnes . . . . . . . . . . . . 94
8.2.1 select() . . . . . . . . . . . . . . . . . . . 94
8.2.2 relocate() . . . . . . . . . . . . . . . . . . 99
8.2.3 rename() . . . . . . . . . . . . . . . . . . 100
8.2.4 rename_with() . . . . . . . . . . . . . . . 101
8.2.5 pull() . . . . . . . . . . . . . . . . . . . . 102
8.2.6 mutate() . . . . . . . . . . . . . . . . . . . 102
8.3 Opérations groupées . . . . . . . . . . . . . . . . 103
8.3.1 group_by() . . . . . . . . . . . . . . . . . 103
8.3.2 summarise() . . . . . . . . . . . . . . . . . 108
8.3.3 count() . . . . . . . . . . . . . . . . . . . 110
3
8.3.4 Grouper selon plusieurs variables . . . . . 111
8.4 Cheatsheet . . . . . . . . . . . . . . . . . . . . . 116
8.5 webin-R . . . . . . . . . . . . . . . . . . . . . . . 116
4
13.2.3 Conversion . . . . . . . . . . . . . . . . . 181
5
16.3 Palettes de couleurs . . . . . . . . . . . . . . . . 208
16.3.1 Color Brewer . . . . . . . . . . . . . . . . 208
16.3.2 Palettes de Paul Tol . . . . . . . . . . . . 210
16.3.3 Interface unifiée avec {paletteer} . . . . 212
6
19 Statistique bivariée & Tests de comparaison 271
19.1 Deux variables catégorielles . . . . . . . . . . . . 271
19.1.1 Tableau croisé avec gtsummary . . . . . . 271
19.1.2 Représentations graphiques . . . . . . . . 274
19.1.3 Calcul manuel . . . . . . . . . . . . . . . 281
19.1.4 Test du Chi² et dérivés . . . . . . . . . . . 284
19.1.5 Comparaison de deux proportions . . . . 286
19.2 Une variable continue selon une variable catégo-
rielle . . . . . . . . . . . . . . . . . . . . . . . . . 290
19.2.1 Tableau comparatif avec gtsummary . . . 290
19.2.2 Représentations graphiques . . . . . . . . 292
19.2.3 Calcul manuel . . . . . . . . . . . . . . . 298
19.2.4 Tests de comparaison . . . . . . . . . . . 299
19.2.5 Différence de deux moyennes . . . . . . . 302
19.3 Deux variables continues . . . . . . . . . . . . . . 303
19.3.1 Représentations graphiques . . . . . . . . 303
19.3.2 Tester la relation entre les deux variables 310
19.4 Matrice de corrélations . . . . . . . . . . . . . . . 311
19.5 webin-R . . . . . . . . . . . . . . . . . . . . . . . 313
7
22.9 Sélection pas à pas d’un meilleur modèle . . . . . 362
22.10Régressions logistiques univariées . . . . . . . . . 376
22.11Présenter l’ensemble des résultats dans un même
tableau . . . . . . . . . . . . . . . . . . . . . . . 377
22.12webin-R . . . . . . . . . . . . . . . . . . . . . . . 379
8
24.4 Autres types de contrastes . . . . . . . . . . . . . 445
24.4.1 Contrastes de type Helmert . . . . . . . . 445
24.4.2 Contrastes polynomiaux . . . . . . . . . . 447
24.5 Lectures additionnelles . . . . . . . . . . . . . . . 448
25 Interactions 449
25.1 Données d’illustration . . . . . . . . . . . . . . . 449
25.2 Modèle sans interaction . . . . . . . . . . . . . . 450
25.3 Définition d’une interaction . . . . . . . . . . . . 452
25.4 Significativité de l’interaction . . . . . . . . . . . 454
25.5 Interprétation des coefficients . . . . . . . . . . . 456
25.6 Définition alternative de l’interaction . . . . . . . 461
25.7 Pour aller plus loin . . . . . . . . . . . . . . . . . 465
25.8 webin-R . . . . . . . . . . . . . . . . . . . . . . . 465
26 Multicolinéarité 466
26.1 Définition . . . . . . . . . . . . . . . . . . . . . . 466
26.2 Mesure de la colinéarité . . . . . . . . . . . . . . 468
26.3 La multicolinéarité est-elle toujours un problème ?474
26.4 webin-R . . . . . . . . . . . . . . . . . . . . . . . 476
9
31 Régression logistique binaire pondérée 512
31.1 Données des exemples . . . . . . . . . . . . . . . 512
31.2 Calcul de la régression logistique binaire . . . . . 513
31.3 Sélection de modèle . . . . . . . . . . . . . . . . . 514
31.4 Affichage des résultats . . . . . . . . . . . . . . . 516
31.5 Prédictions marginales . . . . . . . . . . . . . . . 518
10
VI Analyses avancées 565
11
Préface
12
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 : https://www.youtube.com/c/webinR.
Cette version du guide a utilisé r R.Version()[["version.string"]].
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 https://larmarange.github.io/guide-R/.
13
Remerciements
Licence
14
partie I
Bases du langage
15
1 Packages
16
1.1 Installation (CRAN)
install.packages("gtsummary")
remotes::install_cran("gtsummary")
Ĺ Note
1.2 Chargement
library(gtsummary)
17
À 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
update.packages()
remove.packages("gtsummary")
18
Ď Installer / Mettre à jour les packages utilisés par un
projet
renv::dependencies() |>
purrr::pluck("Package") |>
unique() |>
remotes::install_cran()
19
Á Sous Windows
remotes::install_github("larmarange/labelled")
1.5 Le tidyverse
• visualisation ({ggplot2})
• manipulation des tableaux de données ({dplyr},
{tidyr})
• import/export de données ({readr}, {readxl}, {haven})
• manipulation de variables ({forcats}, {stringr},
{lubridate})
20
• programmation ({purrr}, {magrittr}, {glue})
install.packages("tidyverse")
21
Figure 1.1: Packages chargés avec library(tidyverse)
22
2 Vecteurs
23
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.
x class(x) typeof(x)
3L integer integer
5.3 numeric double
TRUE logical logical
"abc" character character
factor("a") factor integer
as.Date("2020-01-01") Date double
Ď Astuce
24
sexe <- c("h", "f", "h", "f", "f", "f")
sexe
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 :
class(x)
[1] "character"
rep(2, 10)
[1] 2 2 2 2 2 2 2 2 2 2
25
rep(c("a", "b"), 3)
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
[1] 100 90 80 70 60 50 40 30 20 10
[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
26
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
length(taille)
[1] 6
length(c("a", "b"))
[1] 2
length(NULL)
[1] 0
27
2.4 Combiner des vecteurs
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
sexe <- c(
Michel = "h", Anne = "f",
Dominique = NA, Jean = "h",
28
Claude = NA, Marie = "f"
)
sexe
names(sexe)
29
2.6 Indexation par position
taille
taille[1]
[1] 1.88
taille[1:3]
taille[c(2, 5, 6)]
30
taille[length(taille)]
[1] 1.72
taille[c(5, 1, 4, 3)]
taille[c(-1, -5)]
taille[23:25]
[1] NA NA NA
sexe["Anna"]
Anna
"f"
31
sexe[c("Mary", "Michael", "John")]
sexe[names(sexe) != "Dom"]
sexe
Michael John
"h" "h"
32
urbain <- c(TRUE, TRUE, FALSE, FALSE, FALSE, TRUE)
poids <- c(80, 63, 75, 87, 82, 67)
sexe[urbain]
poids >= 80
33
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 :
34
de cette condition n’est pas toujours TRUE ou FALSE, il
peut aussi être à son tour une valeur manquante.
taille
taille
poids
[1] 80 63 75 87 82 67
35
[1] 80 75 NA
[1] 80 75
v <- 1:5
v
[1] 1 2 3 4 5
v[1] <- 3
v
[1] 3 2 3 4 5
36
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 :
length(sexe)
[1] 6
length(sexe)
[1] 7
2.10 En résumé
37
• 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
38
3 Listes
[[1]]
[1] 1 2 3 4 5
[[2]]
[1] "abc"
length(l1)
[1] 2
39
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 = month.name
)
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)
40
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" ...
[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" ...
41
Ĺ Note
3.2 Indexation
[[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
42
[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"
[[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"
str(l[1])
List of 1
$ : int [1:5] 1 2 3 4 5
43
Supposons que je souhaite calculer la moyenne des valeurs du
premier élément de ma liste. Essayons la commande suivante :
mean(l[1])
[1] NA
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
mean(l[[1]])
[1] 3
44
l[["mois"]]
l$mois
l$1
[[1]]
[1] 1 2 3 4 5
[[2]]
[[2]][[1]]
[1] "un" "vecteur" "textuel"
45
$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é
3.4 webin-R
46
4 Tableaux de données
df <- data.frame(
sexe = c("f", "f", "h", "h"),
age = c(52, 31, 29, 35),
blond = c(FALSE, TRUE, TRUE, FALSE)
)
df
47
2 f 31 TRUE
3 h 29 TRUE
4 h 35 FALSE
str(df)
length(df)
[1] 3
names(df)
nrow(df)
[1] 4
ncol(df)
[1] 3
48
dim(df)
[1] 4 3
De plus, tout comme les colonnes ont un nom, il est aussi pos-
sible de nommer les lignes avec row.names() :
4.2 Indexation
df[1]
sexe
Anna f
Mary-Ann f
Michael h
John h
df[[1]]
49
df$sexe
df
df[3, 2]
[1] 29
df["Michael", "age"]
[1] 29
[1] 29
50
df[3, "age"]
[1] 29
df["Michael", 2]
[1] 29
df[1:2,]
df[,c("sexe", "blond")]
sexe blond
Anna f FALSE
Mary-Ann f TRUE
Michael h TRUE
John h FALSE
Á Avertissement
51
df[2, ]
df[, 2]
[1] 52 31 29 35
df[2]
age
Anna 52
Mary-Ann 31
Michael 29
John 35
Ĺ Note
str(df[2, ])
str(df[, 2])
num [1:4] 52 31 29 35
str(df[2])
52
str(df[[2]])
num [1:4] 52 31 29 35
library(questionr)
data(hdv2003)
53
View(hdv2003)
head(hdv2003)
54
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
hard.rock lecture.bd peche.chasse cuisine bricol cinema sport heures.tv
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)
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~
55
$ 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~
$ freres.soeurs <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~
$ trav.imp <fct> Peu important, NA, Aussi important que le reste, Moins i~
$ trav.satisf <fct> Insatisfaction, NA, Equilibre, Satisfaction, NA, Equilib~
$ hard.rock <fct> Non, Non, Non, Non, Non, Non, Non, Non, Non, Non, Non, N~
$ lecture.bd <fct> Non, Non, Non, Non, Non, Non, Non, Non, Non, Non, Non, N~
$ peche.chasse <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~
$ heures.tv <dbl> 0.0, 1.0, 0.0, 2.0, 3.0, 2.0, 2.9, 1.0, 2.0, 2.0, 1.0, 0~
library(labelled)
look_for(hdv2003)
56
5 poids — dbl 0
6 occup — fct 0
8 freres.soeurs — int 0
9 clso — fct 0
10 relig — fct 0
13 hard.rock — fct 0
14 lecture.bd — fct 0
15 peche.chasse — fct 0
16 cuisine — fct 0
17 bricol — fct 0
57
18 cinema — fct 0
19 sport — fct 0
20 heures.tv — 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~
Oui
Non
Ne sait pas
Pratiquant regulier
Pratiquant occasionnel
Appartenance sans pratique
58
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
look_for(hdv2003, "trav")
59
Equilibre
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 freres.soeurs
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
60
Ne sait pas: 27 Appartenance sans pratique :760
Ni croyance ni appartenance:399
Rejet : 93
NSP ou NVPR : 40
summary(hdv2003$sexe)
Homme Femme
899 1101
summary(hdv2003$age)
61
4.4 En résumé
4.5 webin-R
62
5 Tibbles
63
fonctions des extensions du tidyverse acceptent des data.frames
en entrée, mais retournent un tibble.
Contrairement aux data frames, les tibbles :
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
64
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
class(d)
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
65
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
as.data.frame(d)
66
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
column_to_rownames(as.data.frame(d))
67
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
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]>
68
d$data[[2]]
# A tibble: 2 x 2
x y
<int> <int>
1 4 6
2 5 7
reg <-
iris |>
group_by(Species) |>
nest() |>
mutate(
model = map(
data,
~ lm(Sepal.Length ~ Petal.Length + Petal.Width, data = .)
),
tbl = map(model, gtsummary::tbl_regression)
)
reg
# A tibble: 3 x 4
# Groups: Species [3]
Species data model tbl
<fct> <list> <list> <list>
69
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, "**")
)
70
6 Attributs
attributes(iris)
$names
[1] "Sepal.Length" "Sepal.Width" "Petal.Length" "Petal.Width" "Species"
$class
[1] "data.frame"
$row.names
[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
71
[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
class(iris)
[1] "data.frame"
names(iris)
$names
[1] "Sepal.Length" "Sepal.Width" "Petal.Length" "Petal.Width" "Species"
$class
[1] "data.frame"
$row.names
72
[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")
73
partie II
Manipulation de données
74
7 Le pipe
75
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),
decimal.mark = ","
),
"."
)
)
76
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(decimal.mark = ",") |>
paste0("La moyenne est de ", m = _, ".") |>
message()
77
library(magrittr)
v %>%
mean() %>%
round(digits = 1) %>%
format(decimal.mark = ",") %>%
paste0("La moyenne est de ", ., ".") %>%
message()
78
7.4 Accéder à un élément avec
purrr::pluck() et purrr::chuck()
iris |>
purrr::pluck("Petal.Width") |>
mean()
[1] 1.199333
mean(iris$Petal.Width)
[1] 1.199333
[1] "b"
79
v[2]
[1] "b"
iris |>
purrr::pluck("Sepal.Width", 3)
[1] 3.2
iris |>
purrr::pluck("Sepal.Width") |>
purrr::pluck(3)
[1] 3.2
iris[["Sepal.Width"]][3]
[1] 3.2
NULL
Error in `purrr::chuck()`:
! Can't find name `inconnu` in vector.
80
v |> purrr::pluck(10)
NULL
v |> purrr::chuck(10)
Error in `purrr::chuck()`:
! Index 1 exceeds the length of plucked object (10 > 4).
81
8 dplyr
library(nycflights13)
## Chargement des trois tables du jeu de données
data(flights)
data(airports)
82
data(airlines)
8.1.1 filter()
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>
83
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>
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
84
# 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>
flights |>
filter(dep_delay >= 10, dep_delay <= 15)
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>
85
Ď Évaluation contextuelle
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>
86
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>
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>
87
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()
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
airports |>
slice(1:5)
# A tibble: 5 x 8
88
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()
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>
89
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>
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
90
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>
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()
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 LSE La Crosse Municipal 43.9 -91.3 654 -6 A America/Chi~
91
2 UES Waukesha County Airport 43.0 -88.2 911 -6 A America/Chi~
3 SPW Spencer Muni 43.2 -95.2 1339 -6 A America/Chi~
4 FAR Hector International Airport 46.9 -96.8 902 -6 A America/Chi~
5 KPR Port Williams Seaplane Base 58.5 -153. 0 -9 A America/Anc~
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 8 8 1748 1713 35 2013 1919
2 2013 10 8 1040 1046 -6 1335 1330
3 2013 3 14 624 630 -6 754 811
4 2013 8 10 2153 2130 23 34 2359
5 2013 10 23 2235 2245 -10 2335 2356
6 2013 1 17 1230 1235 -5 1530 1606
7 2013 6 6 1000 955 5 1124 1110
8 2013 5 26 704 705 -1 938 956
9 2013 10 1 712 720 -8 934 1000
10 2013 3 1 1816 1820 -4 2000 2031
# 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>
8.1.5 distinct()
92
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
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
93
10 1 10
# i 355 more rows
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.1 select()
airports |>
select(lat, lon)
94
# 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
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
95
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
# 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
96
10 2013 1 1
# i 336,766 more rows
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.
97
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
flights |>
select(where(is.character))
# 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
98
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()
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/~
99
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()
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
100
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()
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/~
101
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
airports |>
pull(alt) |>
mean()
[1] 1001.416
Ĺ Note
8.2.6 mutate()
102
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)
flights <-
flights |>
mutate(
distance_km = distance / 0.62137,
vitesse = distance_km / air_time * 60
)
8.3.1 group_by()
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
103
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>
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
104
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>
flights |>
group_by(month) |>
mutate(mean_delay_month = mean(dep_delay, na.rm = 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>,
105
# vitesse <dbl>, mean_delay_month <dbl>
flights |>
group_by(month) |>
filter(dep_delay == max(dep_delay, na.rm = 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>
106
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
107
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()
flights |>
summarise(
retard_dep = mean(dep_delay, na.rm=TRUE),
retard_arr = mean(arr_delay, na.rm=TRUE)
)
# A tibble: 1 x 2
retard_dep retard_arr
<dbl> <dbl>
1 12.6 6.90
108
flights |>
group_by(month) |>
summarise(
max_delay = max(dep_delay, na.rm=TRUE),
min_delay = min(dep_delay, na.rm=TRUE),
mean_delay = mean(dep_delay, na.rm=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
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
109
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
8.3.3 count()
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
110
8.3.4 Grouper selon plusieurs variables
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
flights |>
count(origin, dest) |>
arrange(desc(n))
# A tibble: 224 x 3
origin dest n
<chr> <chr> <int>
111
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
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
112
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
`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
113
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
flights |>
group_by(month, dest) |>
summarise(nb = n()) |>
ungroup() |>
mutate(pourcentage = nb / sum(nb) * 100)
114
`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
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
115
8.4 Cheatsheet
8.5 webin-R
116
9 Facteurs et forcats
Ĺ Note
117
[1] nord sud sud est est est
Levels: est nord sud
x |>
factor(levels = c("nord", "est", "sud", "ouest"))
x |>
factor(levels = c("nord", "sud"))
x |>
readr::parse_factor(levels = c("nord", "sud"))
118
[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
f <- factor(x)
levels(f)
class(f)
[1] "factor"
119
typeof(f)
[1] "integer"
as.integer(f)
[1] 2 3 3 1 1 1
as.character(f)
library(tidyverse)
data("hdv2003", package = "questionr")
hdv2003$qualif |>
levels()
120
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_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
121
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
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
hdv2003$qualif |>
fct_infreq() |>
fct_rev() |>
122
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
[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
hdv2003$qualif_tri_age <-
hdv2003$qualif |>
fct_reorder(hdv2003$age, .fun = mean)
hdv2003 |>
dplyr::group_by(qualif_tri_age) |>
123
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
124
Une démonstration en vidéo de cet add-in est disponible
dans le webin-R #05 (recoder des variables) sur [You-
Tube](https://youtu.be/CokvTbtWdwc?t=3934).
https://youtu.be/CokvTbtWdwc
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
hdv2003$nivetud |>
questionr::freq()
125
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
126
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
127
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"
)
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
128
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
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
129
Autres 304 15.2 18.4
NA 347 17.3 NA
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
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
130
[1] a a b a
Levels: a b c
v |> fct_drop()
[1] a a b a
Levels: a b
[1] a a b a
Levels: a b c
[1] a a b a
Levels: a b c d e
131
• include.lowest et right influent sur la manière dont
les valeurs situées à la frontière des classes seront inclues
ou exclues ;
• dig.lab indique le nombre de chiffres après la virgule à
conserver dans les noms de modalités.
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
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
132
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 include.lowest=TRUE :
hdv2003 <-
hdv2003 |>
mutate(groupe_ages = cut(
age,
c(18, 20, 40, 60, 80, 97),
include.lowest = 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
hdv2003 <-
hdv2003 |>
mutate(groupe_ages = cut(
age,
c(18, 20, 40, 60, 80, 97),
include.lowest = TRUE,
right = FALSE
))
133
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
134
Une démonstration en vidéo de cet add-in est disponible
dans le webin-R #05 (recoder des variables) sur [You-
Tube](https://youtu.be/CokvTbtWdwc?t=2795).
https://youtu.be/CokvTbtWdwc
135
10 Combiner plusieurs
variables
library(tidyverse)
data("hdv2003", package = "questionr")
10.1 if_else()
136
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
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
137
<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()
138
10.2 case_when()
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
139
ĺ Important
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
140
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
10.3 recode_if()
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>
141
2 rouge <NA>
3 autre bleu ciel
4 rouge <NA>
5 autre jaune
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
df |>
mutate(pref = if_else(autre_details == "bleu ciel", factor("bleu"), pref))
142
# 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
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
143
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" | is.na(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
144
en base R fonctionne très bien, mais ne peut pas être intégrée
à un enchainement 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
145
11 Étiquettes de variables
11.1 Principe
146
Figure 11.1: Présentation du tableau gtsummary::trial dans
la visionneuse de RStudio
library(labelled)
gtsummary::trial |>
look_for()
gtsummary::trial |>
look_for("months")
147
pos variable label col_type missing values
8 ttdeath Months to Death/Censor dbl 0
Ď Astuce
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>
148
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
str(v)
num [1:5] 1 5 2 4 1
- attr(*, "label")= chr "Mon étiquette"
str(v)
num [1:5] 1 5 2 4 1
- attr(*, "label")= chr "Une autre étiquette"
149
num [1:5] 1 5 2 4 1
iris <-
iris |>
set_variable_labels(
Species = NULL,
Sepal.Length = "Longeur du sépale"
)
iris |>
look_for()
150
3 Petal.Length Longueur du pétale dbl 0
4 Petal.Width Largeur du pétale dbl 0
5 Species — fct 0 setosa
versicolor
virginica
iris |>
look_for()
iris |>
subset(Species == "setosa") |>
look_for()
151
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()
iris |>
subset(Species == "setosa") |>
copy_labels_from(iris) |>
look_for()
152
12 Étiquettes de valeurs
153
ĺ Important
library(labelled)
v <- c(1, 2, 1, 9)
v
[1] 1 2 1 9
class(v)
[1] "numeric"
154
val_labels(v)
NULL
non oui
1 2
<labelled<double>[4]>
[1] 1 2 1 9
Labels:
value label
1 non
2 oui
class(v)
155
val_label(v, 1)
[1] "non"
val_label(v, 9)
NULL
<labelled<double>[4]>
[1] 1 2 1 9
Labels:
value label
1 non
9 (manquant)
[1] 1 2 1 9
class(v)
[1] "numeric"
156
¾ Mise en garde
v <- c(1, 2, 1, 2)
val_labels(v) <- c(non = 1, oui = 2)
mean(v)
[1] 1.5
[1] NA
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
157
2 2 [oui] 9 [(manquant)]
3 1 [non] 9 [(manquant)]
4 2 [oui] 3
df |>
look_for()
df |>
look_for()
158
df <- df |>
set_value_labels(
x = c(yes = 2),
y = c("a répondu" = 3, "refus de répondre" = 9)
)
df |>
look_for()
df <- df |>
add_value_labels(
x = c(no = 1)
) |>
remove_value_labels(
y = 9
)
df |>
look_for()
12.4 Conversion
159
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.
160
l’approche A, il faudra prévoir une conversion des variables
labellisées au moment de l’analyse.
Á Avertissement
<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)
161
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')
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
162
12.4.3 Convertir un vecteur labellisé en numérique ou
en texte
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)
163
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.
[1] 1 2 9 3 3 2 NA
[1] 1 2 9 3 3 2 NA
attr(,"label")
[1] "Etiquette de variable"
remove_labels(y)
to_character(x)
164
12.4.4 Conversion conditionnelle en facteurs
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 = as.Date(c(
"2020-01-01", "2020-02-01",
"2020-03-01", "2020-04-01"
))
) |>
set_value_labels(
a = c(No = 1, Yes = 2),
165
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()
166
5 e — fct 0 No
Yes
9
6 f — int 0
7 g — date 0
167
unlabelled(df, levels = "prefixed") |>
look_for()
168
13 Valeurs manquantes
169
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 don-
nées depuis Stata, SAS et SPSS, le package {haven} propose
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)
[1] 1 2 3 NA NA NA
is.na(x)
170
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)
print_tagged_na(x)
format_tagged_na(x)
[1] " 1" " 2" " 3" "NA(a)" "NA(z)" " NA"
is.na(x)
is_regular_na(x)
is_tagged_na(x)
171
is_tagged_na(x, "a")
Ĺ Note
is_tagged_na(y)
format_tagged_na(y)
[1] "double"
format_tagged_na(z)
172
13.1.2 Valeurs uniques, doublons et tris
x |>
unique() |>
print_tagged_na()
[1] 1 2 NA(a)
x |>
unique_tagged_na() |>
print_tagged_na()
x |>
duplicated()
173
x |>
duplicated_tagged_na()
x |>
sort(na.last = TRUE) |>
print_tagged_na()
x |>
sort_tagged_na() |>
print_tagged_na()
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
174
<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
x |> to_factor()
x |>
to_factor(explicit_tagged_na = TRUE)
x |>
to_factor(
levels = "prefixed",
explicit_tagged_na = TRUE
)
175
[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)
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)
176
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()
ĺ Important
177
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
<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
178
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
<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)
) |>
179
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
<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 |> is.na()
180
v |> is_user_na()
v |> is_regular_na()
13.2.3 Conversion
<labelled_spss<integer>[10]>
[1] 1 2 3 4 5 11 12 13 14 15
Missing range: [10, Inf]
mean(x)
[1] 8
x |>
user_na_to_na()
<labelled<integer>[10]>
[1] 1 2 3 4 5 NA NA NA NA NA
181
x |>
user_na_to_na() |>
mean(na.rm = TRUE)
[1] 3
x |>
user_na_to_tagged_na() |>
print_tagged_na()
x |>
user_na_to_tagged_na() |>
mean(na.rm = TRUE)
[1] 3
x |>
remove_user_na()
<labelled<integer>[10]>
[1] 1 2 3 4 5 11 12 13 14 15
182
x |>
remove_user_na() |>
mean()
[1] 8
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)
x |>
to_factor(user_na_to_na = FALSE)
183
14 Import & Export de
données
184
• 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é.
185
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).
library(readr)
d <- read_delim(
"http://larmarange.github.io/analyse-R/data/exemple_texte_tabule.txt",
delim = "\t",
quote = "'"
)
186
Dans des manuels ou des exemples en ligne, vous trou-
verez parfois mention des fonctions utils::read.table(),
utils::read.csv(), utils::read.csv2(), utils::read.delim()
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.
library(readxl)
donnees <- read_excel("data/fichier.xlsx")
187
on pourra indiquer le type souhaité de chaque colonne avec
col_types.
RStudio propose également pour les fichiers Excel un assitant
d’importation, similaire à celui pour les fichiers texte, permet-
tant de faciliter l’import.
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/fichier.sav", user_na = TRUE)
188
Ď Valeurs manquantes
14.3.2 SAS
library(haven)
donnees <- read_sas("data/fichier.sas7bdat")
library(haven)
donnees <- read_sas(
"data/fichier.sas7bdat",
catalog_file = "data/fichier.sas7bcat"
)
Ĺ Note
189
library(foreign)
donnees <- read.xport("data/fichier.xpt")
14.3.3 Stata
library(haven)
donnees <- read_dta("data/fichier.dta")
ĺ Important
14.3.4 dBase
library(foreign)
donnees <- read.dbf("data/fichier.dbf")
190
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
export.RData :
load("export.RData")
¾ Mise en garde
save.image()
191
donnees <- readRDS("mes_donnees.rds")
192
15 Mettre en forme des
nombres
library(scales)
x <- c(0.0023, .123, 4.567, 874.44, 8957845)
number(x)
f <- label_number()
f(x)
label_number()(x)
193
[1] "0.00" "0.12" "4.57" "874.44" "8 957 845.00"
15.1 label_number()
label_number(accuracy = NULL)(x)
label_number(accuracy = .1)(x)
label_number(accuracy = .25)(x)
194
label_number(accuracy = 10)(x)
195
label_number(accuracy = 10^-9, small.mark = "|", small.interval = 3)(x)
label_number(style_negative = "parens")(y)
15.2.1 label_comma()
label_comma()(x)
196
15.2.2 label_percent()
label_percent()(x)
15.2.3 label_dollar()
label_dollar()(x)
label_dollar(prefix = "", suffix = " €", accuracy = .01, big.mark = " ")(x)
197
label_dollar()(c(12.5, -4, 21, -56.36))
15.2.4 label_pvalue()
15.2.5 label_number_si()
[1] "0.0000 g" "0.0034 g" "5.0000 g" "12 Kg" "15 Mg"
198
15.2.6 label_scientific()
15.2.7 label_bytes()
label_bytes(units = "auto_binary")(b)
15.2.8 label_ordinal()
label_ordinal()(1:5)
label_ordinal(rules = ordinal_french())(1:5)
199
label_ordinal(rules = ordinal_french(gender = "f", plural = TRUE))(1:5)
scales::label_date(), scales::label_date_short() et
scales::label_time() peuvent être utilisées pour la mise en
forme de dates.
label_date()(as.Date("2020-02-14"))
[1] "2020-02-14"
label_date(format = "%d/%m/%Y")(as.Date("2020-02-14"))
[1] "14/02/2020"
label_date_short()(as.Date("2020-02-14"))
[1] "14\nfévr.\n2020"
15.2.10 label_wrap()
200
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
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.
201
15.3.1 style_number()
library(gtsummary)
#Uighur
Ď Astuce
¾ Mise en garde
202
nalisés.
15.3.2 style_sigfig()
style_sigfig(x)
style_sigfig(x, digits = 3)
15.3.3 style_percent()
203
[1] "0.0%" "0.0%" "0.5%" "1.0%" "10.0%" "45.4%" "99.0%" "145.0%"
style_percent(v)
style_percent(v, digits = 1)
15.3.4 style_pvalue()
style_pvalue(p)
204
15.3.5 style_ratio()
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"
15.5
205
16 Couleurs & Palettes
library(tidyverse)
ggplot(iris) +
aes(x = Petal.Length) +
geom_histogram(colour = "red", fill = "blue")
206
20
count
10
0
2 4 6
Petal.Length
ggplot(iris) +
aes(x = Petal.Length) +
geom_histogram(colour = "#666666", fill = "#FF0000")
207
20
count
10
0
2 4 6
Petal.Length
[1] "#FF0000"
208
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
¾ Mise en garde
209
16.3.2 Palettes de Paul Tol
library(khroma)
plot_scheme(colour("bright")(7), colours = TRUE)
ggplot(mpg) +
aes(x = displ, y = hwy, colour = class) +
geom_point() +
khroma::scale_colour_bright()
210
40
class
2seater
compact
30 midsize
hwy
minivan
pickup
subcompact
20
suv
2 3 4 5 6 7
displ
211
#762A83 #C2A5CF #F7F7F7 #ACD39E #1B7837
#9970AB #E7D4E8 #D9F0D3 #5AAE61 #FFEE99
gt::info_paletteer()
212
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
paletteer_c("viridis::viridis", n = 6)
<colors>
#440154FF #414487FF #2A788EFF #22A884FF #7AD151FF #FDE725FF
213
ggplot(iris) +
aes(x = Sepal.Length, y = Sepal.Width, colour = Petal.Length) +
geom_point() +
scale_colour_paletteer_c("viridis::viridis", direction = -1)
4.5
4.0
Petal.Length
Sepal.Width
3.5 6
5
4
3.0
3
2
1
2.5
2.0
5 6 7 8
Sepal.Length
214
partie III
Analyses
215
17 Graphiques avec ggplot2
17.1 Ressources
216
tidy, c’est-à-dire avec une ligne par observation et les différentes
valeurs à représenter sous forme de variables du tableau.
217
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 = Petal.Length,
y = Petal.Width,
colour = Species
) +
geom_point()
p
2.5
2.0
Species
Petal.Width
1.5
setosa
versicolor
1.0 virginica
0.5
0.0
2 4 6
Petal.Length
218
ĺ Syntaxe additive
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(
219
labels = scales::label_number(decimal.mark = ",")
) +
coord_equal() +
facet_grid(cols = vars(Species)) +
guides(
color = guide_legend(nrow = 2)
) +
theme_light() +
theme(
legend.position = "bottom",
axis.title = element_text(face = "bold")
)
setosa virginica
Espèce
versicolor
220
Figure 17.4: Cheatsheet ggplot2
17.3 Cheatsheet
221
Figure 17.6: Import de données au lancement d’esquisse
222
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.
17.5 webin-R
223
17.6 Combiner plusieurs graphiques
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(axis.title = element_text(size = 20))
p4 <- ggplot(mtcars) +
aes(x = factor(cyl), y = mpg) +
geom_boxplot() +
ylab(NULL)
library(patchwork)
p1 + p2 + p3 + p4
224
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)
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
225
35
30
25
mpg
20
15
10
2 3 4 5
wt
10
count
0
4 6 8
factor(cyl)
(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
226
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)
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)
227
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)
p1 + p2 + p3 + p4 +
plot_annotation(
title = "Titre du graphique",
subtitle = "sous-titre",
caption = "notes additionelles",
tag_levels = "a",
tag_suffix = "."
)
228
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
229
18 Statistique univariée &
Intervalles de confiance
library(ggplot2)
ggplot(iris) +
aes(x = Petal.Length) +
geom_histogram()
230
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
20
count
10
0
2 4 6
Petal.Length
Ď Astuce
231
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 = Petal.Length) +
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
232
ggplot(iris) +
aes(x = Petal.Length) +
geom_histogram(bins = 10, colour = "black")
40
30
count
20
10
0
2 4 6
Petal.Length
ggplot(iris) +
aes(x = Petal.Length) +
geom_density(adjust = .5)
233
0.4
0.3
density
0.2
0.1
0.0
2 4 6
Petal.Length
234
1000
750
count
500
250
0
Exerce une profession
ChomeurEtudiant, eleve RetraiteRetire des affairesAu foyer Autre inactif
occup
Ď Astuce
library(ggstats)
ggplot(hdv2003) +
235
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
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() +
236
theme(
panel.grid = element_blank(),
axis.text.y = element_blank()
) +
xlab(NULL) + ylab(NULL) +
ggtitle("Occupation des personnes enquêtées")
19.6%
8.6%
6.7%
4.7% 4.2% 3.8%
237
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)
#BlackLivesMatter
hdv2003 |>
tbl_summary(include = c(age, occup))
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%)
238
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.
239
La fonction gtsummary::theme_gtsummary_language() per-
met de modifier la langue utilisée par défaut dans les tableaux.
Les options decimal.mark et big.mark 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",
decimal.mark = ",",
big.mark = " "
)
hdv2003 |>
tbl_summary(include = c(age, occup))
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%)
240
18.2.2 Étiquettes des variables
hdv2003 |>
labelled::set_variable_labels(
occup = "Occupation actuelle"
) |>
tbl_summary(
include = c(age, occup, heures.tv),
label = list(age ~ "Âge médian")
)
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%)
heures.tv 2,00 (1,00 – 3,00)
Manquant 5
241
Pour modifier les modalités d’une variable catégorielle, il faut
modifier en amont les niveaux du facteur correspondant.
trial |>
tbl_summary(label = age ~ "Âge")
trial |>
tbl_summary(label = list(age ~ "Âge", trt ~ "Traitement"))
trial |>
tbl_summary(label = age ~ "Âge")
trial |>
tbl_summary(label = "age" ~ "Âge")
v <- "age"
trial |>
tbl_summary(label = v ~ "Âge")
242
trial |>
tbl_summary(label = c("age", "trt") ~ "Une même étiquette")
trial |>
tbl_summary(label = c(age, trt) ~ "Une même étiquette")
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"
)
243
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"
)
)
trial |>
tbl_summary(label = ~ "Une même étiquette")
trial |>
tbl_summary(
label = everything() ~ "Une même étiquette"
)
244
hdv2003 |>
tbl_summary(
include = c(age, heures.tv),
statistic =
all_continuous() ~ "Moy. : {mean} [min-max : {min} - {max}]"
)
Caractéristique N = 2 000
age Moy. : 48 [min-max : 18 - 97]
heures.tv Moy. : 2,25 [min-max : 0,00 - 12,00]
Manquant 5
hdv2003 |>
tbl_summary(
include = c(age, heures.tv),
statistic = list(
age ~ "Méd. : {median} [{p25} - {p75}]",
heures.tv ~ "Moy. : {mean} ({sd})"
)
)
245
Table 18.5: statisques personnalisées pour une variable conti-
nue (2)
Caractéristique N = 2 000
age Méd. : 48 [35 - 60]
heures.tv Moy. : 2,25 (1,78)
Manquant 5
Caractéristique N = 2 000
heures.tv MC : 8,20
Manquant 5
246
hdv2003 |>
tbl_summary(
include = occup,
statistic = all_categorical() ~ "{p} % ({n}/{N})"
)
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)
hdv2003 |>
tbl_summary(
include = occup,
sort = all_categorical() ~ "frequency"
)
247
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%)
248
Table printed with `knitr::kable()`, not {gt}. Learn why at
https://www.danieldsjoberg.com/gtsummary/articles/rmarkdown.html
To suppress this message, include `message = FALSE` in code chunk header.
Caractéristique N = 2 000
age 48 (17)
heures.tv 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%)
tbl |>
add_stat_label()
Caractéristique N = 2 000
age, Moyenne (ET) 48 (17)
heures.tv, Médiane [EI] 2,00 [1,00 - 3,00]
Manquant 5
249
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")
250
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)
)
Caractéristique N = 200
Grade
I 68 (34%)
II 68 (34%)
III 64 (32%)
Age 47 (38 – 57)
Manquant 11
Patient Died 112 (56%)
trial |>
tbl_summary(
include = c(grade, death),
type = list(
grade ~ "dichotomous",
death ~ "categorical"
),
value = grade ~ "III",
label = grade ~ "Grade III"
251
)
Caractéristique N = 200
Grade III 64 (32%)
Patient Died
0 88 (44%)
1 112 (56%)
hdv2003 |>
tbl_summary(
include = c(age, heures.tv),
type = age ~ "continuous2",
statistic =
all_continuous2() ~ c(
"{median} ({p25} - {p75})",
"{mean} ({sd})",
"{min} - {max}"
)
)
252
https://www.danieldsjoberg.com/gtsummary/articles/rmarkdown.html
To suppress this message, include `message = FALSE` in code chunk header.
Caractéristique N = 2 000
age
Médiane (EI) 48 (35 - 60)
Moyenne (ET) 48 (17)
Étendue 18 - 97
heures.tv 2,00 (1,00 – 3,00)
Manquant 5
hdv2003 |>
tbl_summary(
include = c(age, occup),
digits = list(
all_continuous() ~ 1,
all_categorical() ~ c(0, 1)
)
)
253
Table 18.15: 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,4%)
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%)
hdv2003 |>
tbl_summary(
include = age,
digits =
all_continuous() ~ c(style_percent, style_sigfig, style_ratio)
)
254
https://www.danieldsjoberg.com/gtsummary/articles/rmarkdown.html
To suppress this message, include `message = FALSE` in code chunk header.
Caractéristique N = 2 000
age 4 800 (35 – 60,0)
trial |>
tbl_summary(
include = marker,
statistic = ~ "{mean} pour 100",
digits = ~ function(x){style_percent(x, digits = 1)}
)
Caractéristique N = 200
Marker Level (ng/mL) 91,6 pour 100
Manquant 10
trial |>
tbl_summary(
255
include = marker,
statistic = ~ "{mean} pour 100",
digits = ~ \(x){style_percent(x, digits = 1)}
)
Caractéristique N = 200
Marker Level (ng/mL) 91,6 pour 100
Manquant 10
trial |>
tbl_summary(
include = marker,
statistic = ~ "{mean} pour 100",
digits = ~ purrr::partial(style_percent, digits = 1)
)
256
Table 18.19: passer une fonction personnalisée à digits (syntaxe
3)
Caractéristique N = 200
Marker Level (ng/mL) 91,6 pour 100
Manquant 10
trial |>
tbl_summary(
include = marker,
statistic = ~ "{mean}",
digits = ~ scales::label_number(
accuracy = .01,
suffix = " ng/mL",
decimal.mark = ","
)
)
257
Table 18.20: passer une fonction personnalisée à digits (syntaxe
4)
Caractéristique N = 200
Marker Level (ng/mL) 0,92 ng/mL
Manquant 10
hdv2003 |>
tbl_summary(
include = c(age, heures.tv),
missing = "always",
missing_text = "Nbre observations manquantes"
)
Caractéristique N = 2 000
age 48 (35 – 60)
Nbre observations manquantes 0
heures.tv 2,00 (1,00 – 3,00)
Nbre observations manquantes 5
258
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(
trav.imp.explicit = trav.imp |>
forcats::fct_na_value_to_level("(non renseigné)")
) |>
tbl_summary(
include = c(trav.imp, trav.imp.explicit)
)
Caractéristique N = 2 000
trav.imp
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
trav.imp.explicit
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%)
259
hdv2003 |>
tbl_summary(
include = c(heures.tv, trav.imp),
missing = "no"
) |>
add_n()
Caractéristique N N = 2 000
heures.tv 1 995 2,00 (1,00 – 3,00)
trav.imp 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%)
260
hdv2003$heures.tv |> mean()
[1] NA
[1] 2.246566
[1] 1.775853
[1] 0
[1] 12
[1] 0 12
[1] 2
261
hdv2003$heures.tv |> quantile(na.rm = TRUE)
hdv2003$heures.tv |>
quantile(
probs = c(.2, .4, .6, .8),
na.rm = TRUE
)
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 meme
résultat.
262
trav.imp
Le plus important Aussi important que le reste
29 259
Moins important que le reste Peu important
708 52
prop.table(tbl)
trav.imp
Le plus important Aussi important que le reste
0.02767176 0.24713740
Moins important que le reste Peu important
0.67557252 0.04961832
hdv2003$trav.imp |>
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
263
18.4 Intervalles de confiance
Á Avertissement
hdv2003 |>
tbl_summary(
include = c(age, heures.tv, trav.imp),
statistic = age ~ "{mean} ({sd})"
) |>
add_ci(
method = heures.tv ~ "wilcox.test"
)
264
Table 18.24: ajouter les intervalles de confiance
hdv2003 |>
tbl_summary(
include = c(age, heures.tv),
statistic = ~ "{mean}"
) |>
add_ci(
statistic = ~ "entre {conf.low} et {conf.high}",
conf.level = .9,
style_fun = ~ purrr::partial(style_number, digits = 1)
)
265
Caractéristique N = 2 000 90% CI
Manquant 5
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
List of 10
$ statistic : Named num 127
..- attr(*, "names")= chr "t"
$ parameter : Named num 1999
..- attr(*, "names")= chr "df"
$ p.value : num 0
$ conf.int : num [1:2] 47.4 48.9
..- attr(*, "conf.level")= num 0.95
$ estimate : Named num 48.2
..- attr(*, "names")= chr "mean of x"
266
$ null.value : Named num 0
..- attr(*, "names")= chr "mean"
$ stderr : num 0.379
$ alternative: chr "two.sided"
$ method : chr "One Sample t-test"
$ data.name : chr "hdv2003$age"
- attr(*, "class")= chr "htest"
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 |>
wilcox.test(conf.int = TRUE) |>
purrr::pluck("conf.int")
267
[1] 47.00001 48.50007
attr(,"conf.level")
[1] 0.95
268
Ď Astuce
269
18.5 webin-R
270
19 Statistique bivariée & Tests
de comparaison
library(gtsummary)
theme_gtsummary_language("fr", decimal.mark = ',')
trial |>
tbl_summary(
include = stage,
by = grade
)
271
Table printed with `knitr::kable()`, not {gt}. Learn why at
https://www.danieldsjoberg.com/gtsummary/articles/rmarkdown.html
To suppress this message, include `message = FALSE` in code chunk header.
library(gtsummary)
trial |>
tbl_summary(
include = c(stage, trt),
by = grade,
statistic = ~ "{p}% ({n}/{N})",
percent = "row"
) |>
add_overall(last = TRUE)
272
Table 19.2: un tableau croisé avec des pourcentages en ligne
ĺ Important
273
trial |>
tbl_cross(
row = stage,
col = grade,
percent = "row"
)
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%)
library(ggplot2)
ggplot(trial) +
aes(x = stage, fill = grade) +
geom_bar() +
labs(x = "T Stage", fill = "Grade", y = "Effectifs")
274
40
Grade
Effectifs
I
II
20 III
0
T1 T2 T3 T4
T Stage
library(ggplot2)
ggplot(trial) +
aes(x = stage, fill = grade) +
geom_bar(position = "dodge") +
labs(x = "T Stage", fill = "Grade", y = "Effectifs")
275
20
15 Grade
Effectifs
I
II
10
III
0
T1 T2 T3 T4
T Stage
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)
276
100%
75%
Grade
Proportion
I
50%
II
III
25%
0%
T1 T2 T3 T4
T Stage
277
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
278
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%
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
279
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
280
p +
theme_light() +
xlab("") +
ylab("") +
labs(fill = "") +
ggtitle("Distribution selon le niveau, par grade") +
theme(
panel.grid = element_blank(),
panel.border = element_blank(),
axis.text.y = element_blank(),
axis.ticks = element_blank(),
legend.position = "top"
) +
scale_fill_brewer()
34%
30%
28%
26% 26%
25% 25% 25%
22% 22%
20%
16%
T1 T2 T3 T4
281
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
grade
stage I II III
T1 17 23 13
T2 18 17 19
T3 18 11 14
T4 15 17 18
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
282
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
283
19.1.4 Test du Chi² et dérivés
grade
stage I II III
T1 17 23 13
T2 18 17 19
T3 18 11 14
T4 15 17 18
chisq.test(tab)
data: tab
X-squared = 4.8049, df = 6, p-value = 0.5691
trial |>
tbl_summary(
include = stage,
by = grade
) |>
add_p()
284
Table printed with `knitr::kable()`, not {gt}. Learn why at
https://www.danieldsjoberg.com/gtsummary/articles/rmarkdown.html
To suppress this message, include `message = FALSE` in code chunk header.
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%)
data: tab
p-value = 0.5801
alternative hypothesis: two.sided
trial |>
tbl_summary(
include = stage,
by = grade
) |>
add_p(test = all_categorical() ~ "fisher.test")
285
Table printed with `knitr::kable()`, not {gt}. Learn why at
https://www.danieldsjoberg.com/gtsummary/articles/rmarkdown.html
To suppress this message, include `message = FALSE` in code chunk header.
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
286
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
data: tab
X-squared = 0.24047, df = 1, p-value = 0.6239
alternative hypothesis: two.sided
95 percent confidence interval:
-0.2217278 0.1175050
sample estimates:
prop 1 prop 2
0.4761905 0.5283019
fisher.test(tab)
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
287
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()
trial |>
tbl_summary(
by = trt,
include = grade
) |>
add_difference()
288
https://www.danieldsjoberg.com/gtsummary/articles/rmarkdown.html
To suppress this message, include `message = FALSE` in code chunk header.
trial |>
fastDummies::dummy_cols("grade") |>
tbl_summary(
by = trt,
include = starts_with("grade_"),
digits = ~ c(0, 1)
) |>
add_difference()
289
Table 19.8: différence entre proportions avec création de va-
riables dichotomiques
trial |>
tbl_summary(
include = age,
by = grade
)
290
Table 19.9: âge médian et intervalle interquartile selon le grade
trial |>
tbl_summary(
include = age,
by = grade
) |>
add_overall(last = TRUE) |>
modify_spanning_header(
all_stat_cols(stat_0 = FALSE) ~ "**Grade**"
)
291
trial |>
tbl_summary(
include = age,
by = grade,
statistic = all_continuous() ~ "{mean} ({sd})",
digits = all_continuous() ~ c(1, 1)
) |>
add_overall(last = TRUE)
ggplot(trial) +
aes(x = grade, y = age) +
geom_boxplot(fill = "lightblue") +
theme_light()
292
80
60
age
40
20
I II III
grade
Ď Astuce
ggplot(trial) +
aes(x = grade, y = age) +
geom_violin(fill = "lightblue") +
theme_light()
293
80
60
age
40
20
I II III
grade
ggplot(trial) +
aes(x = grade, y = age) +
geom_point(alpha = .25, colour = "blue") +
theme_light()
294
80
60
age
40
20
I II III
grade
ggplot(trial) +
aes(x = grade, y = age) +
geom_point(
alpha = .25,
colour = "blue",
position = position_jitter(height = 0, width = .2)
) +
theme_light()
295
80
60
age
40
20
I II III
grade
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()
296
48.0
47.5
age
47.0
46.5
I II III
grade
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()
297
Drug A Drug B
55
stage
T1
50
age
T2
T3
T4
45
I II III I II III
grade
library(dplyr)
trial |>
group_by(grade) |>
summarise(
age_moy = mean(age, na.rm = TRUE),
age_med = median(age, na.rm = 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
298
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.
I II III
46.15152 47.53226 48.11475
trial |>
tbl_summary(
include = age,
by = grade
) |>
add_p()
II, N = III, N = p-
Caractéristique
I, N = 68 68 64 valeur
Age 47 (37 – 48 (37 – 47 (38 – 0,8
56) 57) 58)
Manquant 2 6 3
299
Par défaut, pour les variables continues, un test de Kruskal-
Wallis calculé avec la fonction stats::kruskal.test() est uti-
lisé lorsqu’il y a trois groupes ou plus, et un test de Wilcoxon-
Mann-Whitney calculé avec stats::wilcox.test() (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 interquartiles.
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::t.test(), 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::oneway.test(). stats::mood.test().
trial |>
tbl_summary(
include = age,
by = grade,
statistic = all_continuous() ~ "{mean} ({sd})"
) |>
add_p(
test = all_continuous() ~ "oneway.test"
)
300
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
301
trial |>
tbl_summary(
include = age,
by = trt,
statistic = all_continuous() ~ "{mean} ({sd})"
) |>
add_p(
test = all_continuous() ~ "t.test",
test.args = all_continuous() ~ list(var.equal = TRUE)
)
Drug A, N Drug B, N p-
Caractéristique = 98 = 102 valeur
Age 47 (15) 47 (14) 0,8
Manquant 7 4
trial |>
tbl_summary(
include = age,
by = trt,
statistic = all_continuous() ~ "{mean} ({sd})"
) |>
add_difference()
302
Table printed with `knitr::kable()`, not {gt}. Learn why at
https://www.danieldsjoberg.com/gtsummary/articles/rmarkdown.html
To suppress this message, include `message = FALSE` in code chunk header.
ggplot(iris) +
aes(x = Petal.Length, y = Petal.Width) +
geom_point(colour = "blue", alpha = .25) +
theme_light()
303
2.5
2.0
Petal.Width
1.5
1.0
0.5
0.0
2 4 6
Petal.Length
ggplot(iris) +
aes(x = Petal.Length, y = Petal.Width) +
geom_smooth() +
geom_point(colour = "blue", alpha = .25) +
theme_light()
304
2.5
2.0
Petal.Width
1.5
1.0
0.5
0.0
2 4 6
Petal.Length
ggplot(iris) +
aes(x = Petal.Length, y = Petal.Width) +
geom_smooth(method = "lm") +
geom_point(colour = "blue", alpha = .25) +
theme_light()
305
2
Petal.Width
2 4 6
Petal.Length
ggplot(iris) +
aes(x = Petal.Length, y = Petal.Width) +
geom_smooth(method = "lm") +
geom_point(colour = "blue", alpha = .25) +
theme_light() +
expand_limits(x = 0, y = -0.5)
306
2
Petal.Width
0 2 4 6
Petal.Length
ggplot(iris) +
aes(x = Petal.Length, y = Petal.Width) +
geom_smooth(method = "lm", fullrange = TRUE) +
geom_point(colour = "blue", alpha = .25) +
theme_light() +
expand_limits(x = 0, y = -0.5)
307
2
Petal.Width
0 2 4 6
Petal.Length
ggplot(iris) +
aes(x = Petal.Length, y = Petal.Width) +
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)
308
2
Petal.Width
0 2 4 6
Petal.Length
ggplot(iris) +
aes(x = Petal.Length, y = Petal.Width) +
geom_smooth(method = "lm") +
geom_point(colour = "blue", alpha = .25) +
geom_rug() +
theme_light()
309
2
Petal.Width
2 4 6
Petal.Length
cor(iris$Petal.Length, iris$Petal.Width)
[1] 0.9628654
Call:
lm(formula = Petal.Length ~ Petal.Width, data = iris)
Residuals:
310
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 ***
Petal.Width 2.22994 0.05140 43.39 <2e-16 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
m |>
tbl_regression() |>
add_glance_source_note()
311
entre plusieurs variables, tant quantitatives que qualitatives.
library(GGally)
ggpairs(iris)
Sepal.Length
0.4
0.3 Corr: Corr: Corr:
0.2
0.1 −0.118 0.872*** 0.818***
0.0
Sepal.Width
4.5
4.0 Corr: Corr:
3.5
3.0
2.5 −0.428*** −0.366***
2.0
Petal.Length
6
Corr:
4
2 0.963***
Petal.Width 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 2.02.53.03.54.04.5 2 4 6 0.00.51.01.52.02.5 setosa
versicolor
virginica
312
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
age
40
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
19.5 webin-R
313
20 Échelles de Likert
library(tidyverse)
314
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"
)
set.seed(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"
)
library(gtsummary)
df |>
tbl_summary(include = q1:q6)
315
Table printed with `knitr::kable()`, not {gt}. Learn why at
https://www.danieldsjoberg.com/gtsummary/articles/rmarkdown.html
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%)
316
Characteristic N = 150
Ni d’accord, ni pas d’accord 50 (33%)
Plutôt d’accord 50 (33%)
Tout à fait d’accord 0 (0%)
ĺ Important
library(bstfun)
trial
df |>
tbl_likert(
include = q1:q6
)
317
Table printed with `knitr::kable()`, not {gt}. Learn why at
https://www.danieldsjoberg.com/gtsummary/articles/rmarkdown.html
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
df |>
tbl_likert(
include = q1:q6,
statistic = ~ "{p}%"
) |>
add_n() |>
add_continuous_stat(score_values = -2:2)
318
Table printed with `knitr::kable()`, not {gt}. Learn why at
https://www.danieldsjoberg.com/gtsummary/articles/rmarkdown.html
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
library(ggstats)
gglikert(df, include = q1:q6)
319
Première question 47% 26% 21% 17% 20% 16% 36%
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
df |>
gglikert(
include = q1:q6,
totals_include_center = TRUE,
sort = "ascending"
) +
guides(
fill = guide_legend(nrow = 2)
)
320
Seconde question 73% 37% 29% 13% 17% 27%
50% 0% 50%
df |>
gglikert(
include = q1:q6,
facet_cols = vars(groupe)
)
A B
Sixième question 28% 28% 29% 43% 43%38% 38% 37% 25% 25%
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
321
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
df |>
gglikert_stacked(
include = q1:q6,
sort = "ascending",
add_median_line = TRUE
)
322
Seconde question 37% 29% 13% 17%
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
323
21 Régression linéaire
library(tidyverse)
ggplot(iris) +
aes(x = Petal.Length, y = Petal.Width) +
geom_point(colour = "blue", alpha = .25) +
labs(x = "Longueur", y = "Largeur") +
theme_light()
324
2.5
2.0
1.5
Largeur
1.0
0.5
0.0
2 4 6
Longueur
ggplot(iris) +
aes(x = Petal.Length, y = Petal.Width) +
geom_point(colour = "blue", alpha = .25) +
geom_smooth(method = "lm") +
labs(x = "Longueur", y = "Largeur") +
theme_light()
325
2
Largeur
2 4 6
Longueur
Call:
lm(formula = Petal.Width ~ Petal.Length, data = iris)
Coefficients:
(Intercept) Petal.Length
-0.3631 0.4158
326
Le résultat comporte deux coefficients. Le premier, d’une valeur
de 0, 4158, est associé à la variable Petal.Length 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 Petal.Width
lorsque Petal.Length vaut 0. Nous pouvons rendre cela plus vi-
sible en élargissant notre graphique.
ggplot(iris) +
aes(x = Petal.Length, y = Petal.Width) +
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
327
Le modèle linéaire calculé estime donc que le relation entre nos
deux variables peut s’écrire sous la forme suivante :
328
Ď Astuce
Call:
lm(formula = Petal.Width ~ Petal.Length - 1, data = iris)
Coefficients:
Petal.Length
0.3365
library(labelled)
iris %>% look_for("Species")
329
mod <- lm(Petal.Width ~ Species, data = iris)
mod
Call:
lm(formula = Petal.Width ~ Species, data = iris)
Coefficients:
(Intercept) Speciesversicolor Speciesvirginica
0.246 1.080 1.780
mod %>%
tbl_regression(intercept = TRUE)
iris %>%
group_by(Species) %>%
summarise(mean(Petal.Width))
330
# A tibble: 3 x 2
Species `mean(Petal.Width)`
<fct> <dbl>
1 setosa 0.246
2 versicolor 1.33
3 virginica 2.03
Ď Astuce
Call:
lm(formula = Petal.Width ~ Species - 1, data = iris)
Coefficients:
Speciessetosa Speciesversicolor Speciesvirginica
0.246 1.326 2.026
331
viendrons plus en détail dans un chapitre dédié (cf. Cha-
pitre 24).
Call:
lm(formula = Petal.Width ~ Petal.Length + Sepal.Width + Sepal.Length +
Species, data = iris)
Coefficients:
(Intercept) Petal.Length Sepal.Width Sepal.Length
-0.47314 0.24220 0.24220 -0.09293
Speciesversicolor Speciesvirginica
0.64811 1.04637
mod %>%
tbl_regression(intercept = TRUE)
332
Table 21.3: régression linaire avec plusieurs variables explica-
tives
library(ggstats)
ggcoef_model(mod)
333
Petal.Length
(p<0.001***)
Sepal.Width
(p<0.001***)
Sepal.Length
(p=0.039*)
Species
setosa
versicolor (p<0.001***)
virginica (p<0.001***)
334
22 Régression logistique
binaire
335
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")
336
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")
library(tidyverse)
d <- d |>
mutate(sexe = sexe |> fct_relevel("Femme"))
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
337
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 heures.tv 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 <- d |>
mutate(
groupe_ages = age |>
cut(
c(18, 25, 45, 65, 99),
right = FALSE,
include.lowest = 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
338
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
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
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"
)
)
339
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
d <- d |>
set_variable_labels(
sport = "Pratique un sport ?",
sexe = "Sexe",
groupe_ages = "Groupe d'âges",
etudes = "Niveau d'études",
relig = "Rapport à la religion",
heures.tv = "Heures de télévision / jour"
)
340
Ĺ Code récapitulatif (préparation des données)
341
22.2 Statistiques descriptives
library(gtsummary)
theme_gtsummary_language("fr", decimal.mark = ",", big.mark = " ")
d |>
tbl_summary(
by = sport,
include = c(sexe, groupe_ages, etudes, relig, heures.tv)
) |>
add_overall(last = TRUE) |>
add_p() |>
bold_labels() |>
modify_spanning_header(
update = all_stat_cols() ~ "**Pratique un sport ?**"
)
342
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
343
22.3 Calcul de la régression logistique binaire
mod |>
tbl_regression(intercept = TRUE) |>
bold_labels()
344
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
345
selon l’échelle logit. Retraduisons cela en probabilité classique
avec la fonction logit inverse.
[1] 0.3100255
logit_inverse(-0.80 + 0.44)
[1] 0.4109596
[1] 0.3543437
346
individu3 <- d[1, ]
individu3$sexe[1] <- "Homme"
individu3$groupe_ages[1] <- "18-24 ans"
individu3$etudes[1] <- "Primaire"
individu3$relig[1] <- "Pratiquant regulier"
individu3$heures.tv[1] <- 2
library(breakDown)
logit <- function(x) exp(x) / (1 + exp(x))
plot(
broken(mod, individu3, predict.function = betas),
trans = logit
) +
scale_y_continuous(
labels = scales::label_percent(),
breaks = 0:5/5,
limits = c(0, 1)
)
final_prognosis −0.146
etudes = Primaire 0
heures.tv = 2 −0.057
(Intercept) −0.19
347
22.5 La notion d’odds ratio
Ď Astuce
questionr::odds.ratio(.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.
348
On le voit, par construction, l’odds ratio de B par rapport
à A est l’inverse de celui de A par rapport à B : 𝑂𝑅𝐵/𝐴 =
1/𝑂𝑅𝐴/𝐵 .
mod |>
tbl_regression(exponentiate = TRUE) |>
bold_labels()
349
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
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
mod |>
ggstats::ggcoef_table(exponentiate = TRUE)
350
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 1.03.010.0
OR
Ĺ Note
¾ Mise en garde
351
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.
352
bold_labels()
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
353
modelsummary::modelsummary() n’affiche pas les modalités
de référence, ni les étiquettes de variable.
heures.tv
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
mod |>
modelsummary::modelplot(exponentiate = TRUE) +
ggplot2::scale_x_log10()
354
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)
heures.tv −0.121***
(0.034)
Num.Obs. 1995
AIC 2236.2
BIC 2320.1
Log.Lik. −1103.086
F 21.691
RMSE 0.43
+ p < 0.1, * p < 0.05, ** p < 0.01, *** p < 0.001
355
heures.tv
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
mod |>
tbl_regression(
exponentiate = TRUE,
add_pairwise_contrasts = TRUE
) |>
bold_labels()
mod |>
ggstats::ggcoef_model(
exponentiate = TRUE,
add_pairwise_contrasts = TRUE,
pairwise_variables = c("groupe_ages", "etudes")
)
357
Table 22.6: Facteurs associés à la pratique d’un sport (pairwise
contrasts)
358
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
car::Anova(mod)
Response: sport
LR Chisq Df Pr(>Chisq)
sexe 17.309 1 3.176e-05 ***
359
groupe_ages 52.803 3 2.020e-11 ***
etudes 123.826 4 < 2.2e-16 ***
relig 4.232 5 0.5165401
heures.tv 13.438 1 0.0002465 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Ĺ Note
360
Table 22.7: Ajout des p-valeurs globales
361
interactions dans un prochain chapitre, cf. Chapitre 25).
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 contrast 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 24).
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.
362
peut encore améliorer le modèle et ainsi de suite. Lorsque le mo-
dèle ne peut plus être amélioré par la suppresion d’une variable,
on s’arrête.
Il faut également 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. Plus l’AIC sera faible, meilleure sera le mo-
dèle. 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 fonction step() permet justement de sélectionner le meilleur
modèle par une procédure pas à pas descendante basée sur la
minimisation de l’AIC. La fonction affiche à l’écran les diffé-
rentes étapes de la sélection et renvoie le modèle final.
Start: AIC=2236.17
sport ~ sexe + groupe_ages + etudes + relig + heures.tv
Df Deviance AIC
- relig 5 2210.4 2230.4
<none> 2206.2 2236.2
- heures.tv 1 2219.6 2247.6
- sexe 1 2223.5 2251.5
- groupe_ages 3 2259.0 2283.0
- etudes 4 2330.0 2352.0
Step: AIC=2230.4
sport ~ sexe + groupe_ages + etudes + heures.tv
Df Deviance AIC
<none> 2210.4 2230.4
- heures.tv 1 2224.0 2242.0
- sexe 1 2226.4 2244.4
- groupe_ages 3 2260.6 2274.6
- etudes 4 2334.3 2346.3
363
apparait que la suppression de la variable religion permet dimi-
nuer l’AIC à 2210,4. Lors de la seconde étape, toute suppres-
sion d’une autre variable ferait augmenter l’AIC. La procédure
s’arrête donc.
Pour obtenir directement l’AIC d’un modèle donné, on peut
utiliser la fonction AIC().
AIC(mod)
[1] 2236.173
AIC(mod2)
[1] 2230.404
Ď Astuce
364
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 stats::step() ne soit pas disponible,
mais que MASS::stepAIC() puisse être utilisée à la place.
library(MASS)
select
select
Start: AIC=2236.17
sport ~ sexe + groupe_ages + etudes + relig + heures.tv
Df Deviance AIC
- relig 5 2210.4 2230.4
<none> 2206.2 2236.2
- heures.tv 1 2219.6 2247.6
- sexe 1 2223.5 2251.5
- groupe_ages 3 2259.0 2283.0
- etudes 4 2330.0 2352.0
Step: AIC=2230.4
sport ~ sexe + groupe_ages + etudes + heures.tv
Df Deviance AIC
<none> 2210.4 2230.4
- heures.tv 1 2224.0 2242.0
- sexe 1 2226.4 2244.4
- groupe_ages 3 2260.6 2274.6
- etudes 4 2334.3 2346.3
365
Un critère similaire à l’AIC est le critère BIC (Baye-
sian Information Criterion) appelé aussi SBC (Schwarz
information criterion). Il s’obtient avec stats::step()
en ajoutant l’argument k = log(n) où n est le nombre
d’observations inclues dans le modèle que l’on peut obte-
nir avec nrow(model.matrix(reg)) (pour tenir compte
des éventuelles observations manquantes retirées des don-
nées pour le calcul du modèle).
Start: AIC=2320.15
sport ~ sexe + groupe_ages + etudes + relig + heures.tv
Df Deviance AIC
- relig 5 2210.4 2286.4
<none> 2206.2 2320.2
- heures.tv 1 2219.6 2326.0
- sexe 1 2223.5 2329.9
- groupe_ages 3 2259.0 2350.2
- etudes 4 2330.0 2413.6
Step: AIC=2286.39
sport ~ sexe + groupe_ages + etudes + heures.tv
Df Deviance AIC
<none> 2210.4 2286.4
- heures.tv 1 2224.0 2292.4
- sexe 1 2226.4 2294.8
- groupe_ages 3 2260.6 2313.8
- etudes 4 2334.3 2379.8
# A tibble: 1 x 8
366
null.deviance df.null logLik AIC BIC deviance df.residual nobs
<dbl> <int> <dbl> <dbl> <dbl> <dbl> <int> <int>
1 2609. 1994 -1103. 2236. 2320. 2206. 1980 1995
Name | Model | AIC (weights) | AICc (weights) | BIC (weights) | Tjur's R2 | RMSE | Sig
---------------------------------------------------------------------------------------------
mod | glm | 2236.2 (0.027) | 2236.4 (0.026) | 2320.1 (<.001) | 0.190 | 0.432 | 1.0
mod2 | glm | 2230.4 (0.486) | 2230.5 (0.487) | 2286.4 (0.500) | 0.188 | 0.433 | 1.0
mod2_bic | glm | 2230.4 (0.486) | 2230.5 (0.487) | 2286.4 (0.500) | 0.188 | 0.433 | 1.0
library(ggstats)
ggcoef_compare(
list("modèle complet" = mod, "modèle réduit" = mod2),
exponentiate = TRUE
)
367
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
Heures de télévision / jour
0.3 1.0 3.0 10.0
OR
ggcoef_compare(
list("modèle complet" = mod, "modèle réduit" = mod2),
type = "faceted",
exponentiate = TRUE
)
368
Table 22.8: Modèle obtenu après réduction du nombre de va-
riables
mod2 |>
tbl_regression(exponentiate = TRUE) |>
bold_labels() |>
add_glance_source_note()
369
d’observations dans le modèle va changer si on retire
du modèle une variable explicative avec des valeurs man-
quantes.
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"
)
step(mod_na)
Start: AIC=2077.34
sport ~ sexe + groupe_ages + etudes + relig_na + heures.tv
Df Deviance AIC
- relig_na 3 2053.8 2073.8
<none> 2051.3 2077.3
- heures.tv 1 2064.7 2088.7
- sexe 1 2068.1 2092.1
- groupe_ages 3 2098.8 2118.8
- etudes 4 2173.5 2191.5
Error in step(mod_na): le nombre de lignes utilisées a changé : supprimer les valeurs manquan
370
1. créer une copie du jeu de données avec uniquement
des observations sans valeur manquante pour nos
variables explicatives ;
3. appliquer step() ;
Start: AIC=2077.34
sport ~ sexe + groupe_ages + etudes + relig_na + heures.tv
Df Deviance AIC
- relig_na 3 2053.8 2073.8
371
<none> 2051.3 2077.3
- heures.tv 1 2064.7 2088.7
- sexe 1 2068.1 2092.1
- groupe_ages 3 2098.8 2118.8
- etudes 4 2173.5 2191.5
Step: AIC=2073.8
sport ~ sexe + groupe_ages + etudes + heures.tv
Df Deviance AIC
<none> 2053.8 2073.8
- heures.tv 1 2067.0 2085.0
- sexe 1 2069.8 2087.8
- groupe_ages 3 2099.5 2113.5
- etudes 4 2176.7 2188.7
372
step_with_na <- function(model, ...) {
# refit the model without NAs
model_no_na <- update(model, data = model.frame(model))
# apply step()
model_simplified <- step(model_no_na, ...)
# recompute simplified model using full data
update(model, formula = terms(model_simplified))
}
anova(mod_na_reduit2, mod_na_reduit_direct)
373
autre_modele <- glm(
sport ~ sexe + relig + heures.tv + cinema + lecture.bd,
family = binomial,
data = d
)
step(autre_modele)
Start: AIC=2350.99
sport ~ sexe + relig + heures.tv + cinema + lecture.bd
Df Deviance AIC
- relig 5 2333.5 2343.5
- lecture.bd 1 2332.6 2350.6
<none> 2331.0 2351.0
- sexe 1 2352.4 2370.4
- heures.tv 1 2364.1 2382.1
- cinema 1 2516.7 2534.7
Step: AIC=2343.52
sport ~ sexe + heures.tv + cinema + lecture.bd
Df Deviance AIC
- lecture.bd 1 2335.1 2343.1
<none> 2333.5 2343.5
- sexe 1 2355.9 2363.9
- heures.tv 1 2367.2 2375.2
- cinema 1 2522.3 2530.3
Step: AIC=2343.1
sport ~ sexe + heures.tv + cinema
Df Deviance AIC
<none> 2335.1 2343.1
- sexe 1 2357.1 2363.1
- heures.tv 1 2369.3 2375.3
- cinema 1 2526.2 2532.2
374
data = d)
Coefficients:
(Intercept) sexeHomme heures.tv cinemaOui
-1.0296 0.4708 -0.1807 1.3658
step(
autre_modele,
scope = list(lower = ~ lecture.bd)
)
Start: AIC=2350.99
sport ~ sexe + relig + heures.tv + cinema + lecture.bd
Df Deviance AIC
- relig 5 2333.5 2343.5
<none> 2331.0 2351.0
- sexe 1 2352.4 2370.4
- heures.tv 1 2364.1 2382.1
- cinema 1 2516.7 2534.7
Step: AIC=2343.52
sport ~ sexe + heures.tv + cinema + lecture.bd
Df Deviance AIC
<none> 2333.5 2343.5
- sexe 1 2355.9 2363.9
- heures.tv 1 2367.2 2375.2
- cinema 1 2522.3 2530.3
375
Call: glm(formula = sport ~ sexe + heures.tv + cinema + lecture.bd,
family = binomial, data = d)
Coefficients:
(Intercept) sexeHomme heures.tv cinemaOui lecture.bdOui
-1.0418 0.4766 -0.1795 1.3598 0.4017
d |>
tbl_uvregression(
y = sport,
include = c(sexe, groupe_ages, etudes, relig, heures.tv),
method = glm,
method.args = list(family = binomial),
exponentiate = TRUE
) |>
bold_labels()
376
Table 22.9: Régressions logistiques univariées
377
tbl_desc <-
d |>
tbl_summary(
by = sport,
include = c(sexe, groupe_ages, etudes, relig, heures.tv),
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, heures.tv),
method = glm,
method.args = list(family = binomial),
exponentiate = TRUE
) |>
modify_column_hide("stat_n")
tbl_multi <-
mod2 |>
tbl_regression(exponentiate = TRUE)
378
Table 22.10: tableau synthétique de l’analyse
22.12 webin-R
379
https://youtu.be/BUo9i7XTLYQ
380
23 Prédictions marginales,
contrastes marginaux &
effets marginaux
Á Avertissement
381
Ĺ Note
23.1 Terminologie
382
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 {broom.helpers} 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 broom.helpers::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().
library(tidyverse)
library(labelled)
library(gtsummary)
theme_gtsummary_language(
"fr",
decimal.mark = ",",
big.mark = " "
)
d <-
hdv2003 |>
mutate(
sexe = sexe |> fct_relevel("Femme"),
groupe_ages = age |>
cut(
c(18, 25, 45, 65, 99),
383
right = FALSE,
include.lowest = 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",
heures.tv = "Heures de télévision / jour"
)
mod |>
tbl_regression(exponentiate = TRUE) |>
bold_labels()
384
Table 23.1: Odds Ratios du modèle logistique
[1] 1995
colnames(mf)
385
23.3 Prédictions marginales
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.
[1] 0.324814
[1] 0.4036624
386
library(marginaleffects)
mod |>
predictions(variables = "sexe", by = "sexe", type = "response")
mod |>
predictions(variables = "heures.tv", by = "heures.tv", type = "response")
387
Ĺ Note
mod |>
tbl_regression(
tidy_fun = broom.helpers::tidy_marginal_predictions,
type = "response",
estimate_fun = scales::label_percent(accuracy = 0.1)
) |>
bold_labels() |>
modify_column_hide("p.value")
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%
388
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 broom.helpers::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.
389
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
p & coord_flip()
390
65 ans et plus
Groupe d'âges
Homme
45−64 ans
Sexe
25−44 ans
Femme
18−24 ans
10.0
Supérieur
7.5
Technique / Professionnel
5.0
Secondaire
2.5
Primaire
0.0
0% 20% 40% 60% 80% 0% 20% 40% 60% 80%
mod |>
ggstats::ggcoef_model(
tidy_fun = broom.helpers::tidy_marginal_predictions,
tidy_args = list(type = "response"),
show_p_values = FALSE,
signif_stars = FALSE,
significance = NULL,
vline = FALSE
) +
scale_x_continuous(labels = scales::label_percent())
391
Sexe Femme
Homme
[1] -0.910525
392
mod |> predict(type = "link", newdata = mf_hommes) |> mean()
[1] -0.4928844
mod |>
predictions(variables = "sexe", by = "sexe", type = "link")
[1] 0.2868924
mod |> predict(type = "link", newdata = mf_hommes) |> mean() |> logit_inverse()
[1] 0.3792143
mod |>
predictions(variables = "sexe", by = "sexe")
393
Or, la plupart du temps, le logit inverse de la moyenne des
prédictions est différent de la moyenne des logit inverse des
prédictions !
Les résultats seront similaires et du même ordre de gran-
deur, mais pas identiques.
Columns: rowid, rowidcf, estimate, p.value, s.value, conf.low, conf.high, sport, groupe_ages, e
394
pour la variable etudes). On fait juste varier les modalités de
sexe puis on calculer la probabilité de faire du sport de ces
individus moyens.
On peut également passer le paramètre newdata = "mean" à
broom.helpers::tidy_marginal_predictions() ou même à
gtsummary::tbl_regression()39 . 39
Les paramètres ad-
ditionnels indiqués à
gtsummary::tbl_regression()
mod |>
sont transmis en cascade à
tbl_regression( broom.helpers::tidy_plus_plus()
tidy_fun = broom.helpers::tidy_marginal_predictions,
puis à
newdata = "mean", broom.helpers::tidy_marginal_predictions()
et
estimate_fun = scales::label_percent(accuracy = 0.1) enfin à
marginaleffects::predictions().
) |>
bold_labels() |>
modify_column_hide("p.value")
Prédictions Marginales
Caractéristique à la Moyenne 95% IC
Sexe
Femme 23.9% 19.6% –
28.9%
Homme 32.3% 27.3% –
37.8%
Groupe d’âges
25-44 ans 37.3% 32.1% –
42.8%
18-24 ans 46.8% 36.1% –
57.8%
45-64 ans 23.9% 19.6% –
28.9%
65 ans et plus 19.2% 14.0% –
25.6%
395
Prédictions Marginales
Caractéristique à la Moyenne 95% IC
Niveau d’études
Supérieur 42.3% 36.0% –
48.9%
Non documenté 48.9% 34.7% –
63.2%
Primaire 10.1% 7.3% –
13.7%
Technique / 23.9% 19.6% –
Professionnel 28.9%
Secondaire 22.1% 17.7% –
27.2%
Heures de
télévision / jour
0 29.2% 23.6% –
35.5%
1 26.8% 21.9% –
32.3%
2 24.5% 20.1% –
29.5%
3 22.3% 18.1% –
27.2%
12 8.8% 4.6% –
16.4%
396
65 ans et plus
Groupe d'âges
Homme
45−64 ans
Sexe
25−44 ans
Femme
18−24 ans
10.0
Supérieur
7.5
Technique / Professionnel
5.0
Secondaire
2.5
Primaire
0.0
0% 20% 40% 60% 80% 0% 20% 40% 60% 80%
mod |>
ggstats::ggcoef_model(
tidy_fun = broom.helpers::tidy_marginal_predictions,
tidy_args = list(newdata = "mean"),
show_p_values = FALSE,
signif_stars = FALSE,
significance = NULL,
vline = FALSE
) +
scale_x_continuous(labels = scales::label_percent())
397
Sexe Femme
Homme
sexe effect
sexe
Femme Homme
0.2868924 0.3792143
e$model.matrix
398
(Intercept) sexeHomme groupe_ages25-44 ans groupe_ages45-64 ans
1 1 0 0.3533835 0.3719298
2 1 1 0.3533835 0.3719298
groupe_ages65 ans et plus etudesSecondaire etudesTechnique / Professionnel
1 0.1904762 0.193985 0.2962406
2 0.1904762 0.193985 0.2962406
etudesSupérieur etudesNon documenté heures.tv
1 0.2205514 0.05614035 2.246566
2 0.2205514 0.05614035 2.246566
attr(,"assign")
[1] 0 1 2 2 2 3 3 3 3 4
attr(,"contrasts")
attr(,"contrasts")$sexe
[1] "contr.treatment"
attr(,"contrasts")$groupe_ages
[1] "contr.treatment"
attr(,"contrasts")$etudes
[1] "contr.treatment"
mod |>
tbl_regression(
tidy_fun = broom.helpers::tidy_all_effects,
estimate_fun = scales::label_percent(accuracy = 0.1)
) |>
bold_labels()
399
Table printed with `knitr::kable()`, not {gt}. Learn why at
https://www.danieldsjoberg.com/gtsummary/articles/rmarkdown.html
To suppress this message, include `message = FALSE` in code chunk header.
Prédictions Marginales
Caractéristique à la Moyenne 95% IC
Sexe
Femme 28.7% 25.8% –
31.8%
Homme 37.9% 34.4% –
41.6%
Groupe d’âges
18-24 ans 51.2% 41.0% –
61.3%
25-44 ans 41.5% 37.4% –
45.7%
45-64 ans 27.3% 23.9% –
30.9%
65 ans et plus 22.0% 17.4% –
27.5%
Niveau d’études
Primaire 14.9% 11.3% –
19.3%
Secondaire 30.7% 26.2% –
35.7%
Technique / 32.9% 29.1% –
Professionnel 37.0%
Supérieur 53.4% 48.3% –
58.4%
Non documenté 59.9% 46.6% –
71.8%
Heures de
télévision / jour
0 38.9% 34.8% –
43.2%
3 30.7% 28.1% –
33.4%
400
Prédictions Marginales
Caractéristique à la Moyenne 95% IC
6 23.6% 18.9% –
28.9%
9 17.7% 11.9% –
25.4%
10 16.0% 10.1% –
24.4%
mod |>
effects::allEffects() |>
plot()
0.6
sport
sport
0.40 0.5
0.35 0.4
0.3
0.30 0.2
Femme Homme 18−2425−44
ans 45−64
ans65 ans
ans et plus
sexe groupe_ages
0.7 0.40
sport
sport
0.6
0.5
0.4 0.35
0.30
0.25
0.3
0.2 0.20
0.15
0.10
Primaire
Technique
Secondaire
/ Professionnel
Supérieur
Non documenté 0 2 4 6 8 10
etudes heures.tv
401
mod |>
ggstats::ggcoef_model(
tidy_fun = broom.helpers::tidy_all_effects,
vline = FALSE
) +
scale_x_continuous(labels = scales::label_percent())
Sexe Femme
Homme
23.3.3 Variantes
mod |>
tbl_regression(
tidy_fun = broom.helpers::tidy_ggpredict,
estimate_fun = scales::label_percent(accuracy = 0.1)
) |>
402
bold_labels()
Prédictions
Caractéristique Marginales 95% IC
Sexe
Femme 23.8% 15.3% –
35.1%
Homme 32.2% 21.4% –
45.4%
Groupe d’âges
18-24 ans 23.8% 15.3% –
35.1%
25-44 ans 17.5% 12.7% –
23.5%
45-64 ans 10.1% 7.3% –
13.7%
65 ans et plus 7.8% 5.5% –
10.9%
Niveau d’études
Primaire 23.8% 15.3% –
35.1%
Secondaire 44.2% 33.0% –
56.1%
Technique / 46.8% 36.1% –
Professionnel 57.8%
Supérieur 67.2% 56.2% –
76.6%
Non documenté 72.8% 62.8% –
80.9%
403
Prédictions
Caractéristique Marginales 95% IC
Heures de télévision
/ jour
0 29.1% 18.7% –
42.3%
1 26.7% 17.2% –
38.9%
2 24.4% 15.7% –
35.8%
3 22.2% 14.2% –
33.0%
4 20.2% 12.8% –
30.4%
5 18.3% 11.4% –
28.2%
6 16.6% 10.0% –
26.1%
7 15.0% 8.8% –
24.3%
8 13.5% 7.7% –
22.7%
9 12.1% 6.6% –
21.2%
10 10.9% 5.7% –
19.9%
11 9.8% 4.9% –
18.7%
12 8.8% 4.2% –
17.6%
mod |>
ggeffects::ggpredict() |>
plot() |>
patchwork::wrap_plots()
404
Predicted probabilities of PratiquePredicted
un sport ?probabilities of Pratique un sport ?
Pratique un sport ?
Pratique un sport ?
40% 30%
30% 20%
20% 10%
Pratique un sport ?
80% 40%
60% 30%
40% 20%
10%
20%
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
405
Le contraste entre les hommes et les femmes est tout simple-
ment la différence et les deux prédictions marginales.
pred$estimate[2] - pred$estimate[1]
[1] 0.07884839
Columns: term, contrast, estimate, std.error, statistic, p.value, s.value, conf.low, conf.high
Ď Astuce
Les contrastes calculés ici ont été moyennés sur l’ensemble des
valeurs observées. On parle donc de contrastes marginaux
moyens (average marginal contrasts).
Par défaut, chaque modalité est contrastée avec la première
modalité prise comme référence (voir exemple ci-dessous avec
la variable groupe_ages.
Regardons maintenant une variable continue.
406
Term Contrast Estimate Std. Error z Pr(>|z|) S 2.5 % 97.5 %
heures.tv +1 -0.0227 0.0062 -3.66 <0.001 11.9 -0.0348 -0.0105
Columns: term, contrast, estimate, std.error, statistic, p.value, s.value, conf.low, conf.high
avg_comparisons(mod)
Columns: term, contrast, estimate, std.error, statistic, p.value, s.value, conf.low, conf.high
407
Il est important de noter que le nom des colonnes n’est
pas compatible avec les fonctions de {broom.helpers}
et par extension avec gtsummary::tbl_regression() et
42 42
ggstats::ggcoef_model(). On utilisera donc broom.helpers::tidy_marginal_contrasts()
Il existe également une fonction
qui remets en forme le tableau de résultats dans un format broom.helpers::tidy_avg_comparisons()
mais on lui préférera
compatible. On pourra ainsi produire un tableau propre des
43 broom.helpers::tidy_marginal_contrasts().
résultats . Pour un modèle sans interaction,
les résultats sont identiques. Mais
mod |> broom.helpers::tidy_marginal_contrasts()
tbl_regression( peut gérer des termes d’interactions,
ce qui sera utile dans un prochain
tidy_fun = broom.helpers::tidy_marginal_contrasts,chapitre (cf. Chapitre 25).
estimate_fun = scales::label_percent( 43
Notez l’utilisation de
accuracy = 0.1, style_positive = "plus" dans
style_positive = "plus" l’appel de scales::label_percent()
) pour ajouter un signe + devant les va-
) |> leurs positives, afin de bien indiquer
que l’on représente le résultat d’une
bold_labels()
différence.
Contrastes
Marginaux p-
Caractéristique Moyens 95% IC valeur
Sexe
Homme - Femme +7.9% +4.0% – <0,001
+11.7%
Groupe d’âges
25-44 ans - 18-24 -8.4% -18.1% – 0,086
ans +1.2%
45-64 ans - 18-24 -21.3% -31.2% – <0,001
ans -11.3%
65 ans et plus - -26.3% -37.2% – <0,001
18-24 ans -15.4%
Niveau d’études
408
Contrastes
Marginaux p-
Caractéristique Moyens 95% IC valeur
Secondaire - +15.7% +9.5% – <0,001
Primaire +21.8%
Technique / +17.8% +12.0% <0,001
Professionnel - – +23.6%
Primaire
Supérieur - +37.0% +30.4% <0,001
Primaire – +43.6%
Non documenté - +43.1% +29.5% <0,001
Primaire – +56.6%
Heures de
télévision / jour
+1 -2.3% -3.5% – <0,001
-1.1%
ggstats::ggcoef_model(
mod,
tidy_fun = broom.helpers::tidy_marginal_contrasts
) +
ggplot2::scale_x_continuous(
labels = scales::label_percent(style_positive = "plus")
)
409
Sexe Homme − Femme (p<0.001***)
Ď Astuce
mod |>
tbl_regression(
tidy_fun = broom.helpers::tidy_marginal_contrasts,
variables_list = list(
list(heures.tv = 2),
list(groupe_ages = "pairwise"),
list(etudes = "sequential")
),
estimate_fun = scales::label_percent(
accuracy = 0.1,
style_positive = "plus"
)
) |>
bold_labels()
410
Table printed with `knitr::kable()`, not {gt}. Learn why at
https://www.danieldsjoberg.com/gtsummary/articles/rmarkdown.html
To suppress this message, include `message = FALSE` in code chunk header.
Contrastes
Marginaux 95% p-
Caractéristique Moyens IC valeur
Heures de
télévision / jour
+2 -4.5% -7.0% – <0,001
-2.1%
Groupe d’âges
25-44 ans - 18-24 -8.4% -18.1% 0,086
ans – +1.2%
45-64 ans - 18-24 -21.3% -31.2% <0,001
ans – -11.3%
65 ans et plus - -26.3% -37.2% <0,001
18-24 ans – -15.4%
45-64 ans - 25-44 -12.8% -17.6% <0,001
ans – -8.1%
65 ans et plus - -17.9% -24.3% <0,001
25-44 ans – -11.4%
65 ans et plus - -5.0% -11.0% 0,10
45-64 ans – +0.9%
Niveau d’études
Secondaire - +15.7% +9.5% – <0,001
Primaire +21.8%
Technique / +2.1% -3.8% – 0,5
Professionnel - +8.0%
Secondaire
Supérieur - +19.2% +13.3% <0,001
Technique / –
Professionnel +25.1%
Non documenté - +6.1% -7.0% – 0,4
Supérieur +19.2%
411
On peut obtenir le même résultat avec
broom.helpers::tidy_avg_comparison() avec une
syntaxe un peu plus simple (en passant une liste
via variables au lieu d’une liste de listes via
variables_list).
mod |>
tbl_regression(
tidy_fun = broom.helpers::tidy_avg_comparisons,
variables = list(
heures.tv = 2,
groupe_ages = "pairwise",
etudes = "sequential"
),
estimate_fun = scales::label_percent(
accuracy = 0.1,
style_positive = "plus"
)
) |>
bold_labels()
Contrastes
Marginaux 95% p-
Caractéristique Moyens IC valeur
Heures de
télévision / jour
+2 -4.5% -7.0% – <0,001
-2.1%
Groupe d’âges
25-44 ans - 18-24 -8.4% -18.1% 0,086
ans – +1.2%
45-64 ans - 18-24 -21.3% -31.2% <0,001
ans – -11.3%
65 ans et plus - -26.3% -37.2% <0,001
18-24 ans – -15.4%
412
45-64 ans - 25-44 -12.8% -17.6% <0,001
ans – -8.1%
65 ans et plus - -17.9% -24.3% <0,001
25-44 ans – -11.4%
65 ans et plus - -5.0% -11.0% 0,10
45-64 ans – +0.9%
Niveau d’études
Secondaire - +15.7% +9.5% – <0,001
Primaire +21.8%
Technique / +2.1% -3.8% – 0,5
Professionnel - +8.0%
Secondaire
Supérieur - +19.2% +13.3% <0,001
Technique / –
Professionnel +25.1%
Non documenté - +6.1% -7.0% – 0,4
Supérieur +19.2%
mod |>
tbl_regression(
tidy_fun = broom.helpers::tidy_marginal_contrasts,
newdata = "mean",
estimate_fun = scales::label_percent(
413
accuracy = 0.1,
style_positive = "plus"
)
) |>
bold_labels()
Contrastes
Marginaux à la p-
Caractéristique Moyenne 95% IC valeur
Sexe
Homme - Femme +8.4% +4.3% – <0,001
+12.5%
Groupe d’âges
25-44 ans - 18-24 -9.5% -20.5% – 0,090
ans +1.5%
45-64 ans - 18-24 -22.9% -33.8% – <0,001
ans -11.9%
65 ans et plus - -27.6% -39.2% – <0,001
18-24 ans -16.1%
Niveau d’études
Secondaire - +12.0% +7.0% – <0,001
Primaire +17.1%
Technique / +13.9% +9.0% – <0,001
Professionnel - +18.7%
Primaire
Supérieur - +32.2% +25.7% <0,001
Primaire –
+38.7%
Non documenté - +38.8% +24.1% <0,001
Primaire –
+53.5%
Heures de
télévision / jour
414
Contrastes
Marginaux à la p-
Caractéristique Moyenne 95% IC valeur
+1 -2.2% -3.4% – <0,001
-1.0%
ggstats::ggcoef_model(
mod,
tidy_fun = broom.helpers::tidy_marginal_contrasts,
tidy_args = list(newdata = "mean")
) +
ggplot2::scale_x_continuous(
labels = scales::label_percent(style_positive = "plus")
)
415
23.5 Pentes marginales / Effets marginaux
avg_slopes(mod)
416
groupe_ages 65 ans et plus - 18-24 ans -0.2631 0.0556 -4.73
etudes Secondaire - Primaire 0.1568 0.0314 4.99
etudes Technique / Professionnel - Primaire 0.1781 0.0295 6.04
etudes Supérieur - Primaire 0.3701 0.0337 10.98
etudes Non documenté - Primaire 0.4309 0.0691 6.23
heures.tv dY/dX -0.0227 0.0062 -3.66
Pr(>|z|) S 2.5 % 97.5 %
<0.001 14.0 0.0402 0.1175
0.0865 3.5 -0.1808 0.0121
<0.001 15.2 -0.3121 -0.1133
<0.001 18.8 -0.3720 -0.1541
<0.001 20.6 0.0952 0.2184
<0.001 29.3 0.1203 0.2359
<0.001 90.8 0.3040 0.4361
<0.001 31.0 0.2954 0.5665
<0.001 11.9 -0.0348 -0.0105
Columns: term, contrast, estimate, std.error, statistic, p.value, s.value, conf.low, conf.high
mod |>
tbl_regression(
tidy_fun = broom.helpers::tidy_avg_slopes,
estimate_fun = scales::label_percent(
accuracy = 0.1,
style_positive = "plus"
)
) |>
bold_labels()
417
Table 23.10: Effets marginaux moyens
Effets
Marginaux p-
Caractéristique Moyens 95% IC valeur
Sexe
Homme - Femme +7.9% +4.0% – <0,001
+11.7%
Groupe d’âges
25-44 ans - 18-24 ans -8.4% -18.1% – 0,086
+1.2%
45-64 ans - 18-24 ans -21.3% -31.2% – <0,001
-11.3%
65 ans et plus - -26.3% -37.2% – <0,001
18-24 ans -15.4%
Niveau d’études
Secondaire - +15.7% +9.5% – <0,001
Primaire +21.8%
Technique / +17.8% +12.0% – <0,001
Professionnel - +23.6%
Primaire
Supérieur - Primaire +37.0% +30.4% – <0,001
+43.6%
Non documenté - +43.1% +29.5% – <0,001
Primaire +56.6%
Heures de
télévision / jour
dY/dX -2.3% -3.5% – <0,001
-1.1%
ggstats::ggcoef_model(
mod,
tidy_fun = broom.helpers::tidy_avg_slopes
) +
ggplot2::scale_x_continuous(
labels = scales::label_percent(style_positive = "plus")
)
418
Sexe Homme − Femme (p<0.001***)
# A tibble: 9 x 5
term estimate std.error statistic p.value
<chr> <dbl> <dbl> <dbl> <dbl>
1 etudesNon documenté 0.431 0.0691 6.23 4.60e-10
2 etudesSecondaire 0.157 0.0314 4.99 6.10e- 7
3 etudesSupérieur 0.370 0.0337 11.0 4.69e-28
4 etudesTechnique / Professionnel 0.178 0.0295 6.04 1.53e- 9
5 groupe_ages25-44 ans -0.0844 0.0492 -1.71 8.65e- 2
6 groupe_ages45-64 ans -0.213 0.0507 -4.20 2.73e- 5
7 groupe_ages65 ans et plus -0.263 0.0556 -4.73 2.21e- 6
8 heures.tv -0.0227 0.00620 -3.66 2.56e- 4
9 sexeHomme 0.0788 0.0197 4.00 6.26e- 5
419
mod |>
tbl_regression(
tidy_fun = broom.helpers::tidy_margins,
estimate_fun = scales::label_percent(
accuracy = 0.1,
style_positive = "plus"
)
) |>
bold_labels()
Effets
Marginaux p-
Caractéristique Moyens 95% IC valeur
Niveau d’études
Primaire — —
Non documenté +43.1% +29.5% – <0,001
+56.6%
Secondaire +15.7% +9.5% – <0,001
+21.8%
Supérieur +37.0% +30.4% – <0,001
+43.6%
Technique / +17.8% +12.0% – <0,001
Professionnel +23.6%
Groupe d’âges
18-24 ans — —
25-44 ans -8.4% -18.1% – 0,086
+1.2%
45-64 ans -21.3% -31.2% – <0,001
-11.3%
65 ans et plus -26.3% -37.2% – <0,001
-15.4%
Heures de -2.3% -3.5% – <0,001
télévision / jour -1.1%
420
Effets
Marginaux p-
Caractéristique Moyens 95% IC valeur
Sexe
Femme — —
Homme +7.9% +4.0% – <0,001
+11.7%
ggstats::ggcoef_model(
mod,
tidy_fun = broom.helpers::tidy_margins
) +
ggplot2::scale_x_continuous(
labels = scales::label_percent(style_positive = "plus")
)
Sexe Femme
Homme (p<0.001***)
−40%
−20%0%+20%
+40%
+60%
Average Marginal Effects
421
23.6 Lectures complémenaires (en anglais)
422
24 Contrastes (variables
catégorielles)
423
Regardons la moyenne de marker pour chaque valeur de
grade.
library(tidyverse)
library(gtsummary)
trial |>
select(marker, grade) |>
tbl_summary(
by = grade,
statistic = marker ~ "{mean}",
digits = marker ~ 4
) |>
add_overall(last = TRUE)
Call:
lm(formula = marker ~ grade, data = trial)
Coefficients:
(Intercept) gradeII gradeIII
1.0669 -0.3864 -0.0711
424
Pour bien interpréter ces coefficients, il faut comprendre
comment la variable grade a été transformée avant d’être
inclue dans le modèle. Nous pouvons voir cela avec la fonction
contrasts().
contrasts(trial$grade)
II III
I 0 0
II 1 0
III 0 1
425
être fait avec gtsummary::tbl_regression() avec l’option
add_estimate_to_reference_rows = TRUE.
mod1_trt |>
tbl_regression(
intercept = TRUE,
add_estimate_to_reference_rows = TRUE
)
library(questionr)
data("hdv2003")
library(tidyverse)
426
right = FALSE,
include.lowest = TRUE
) |>
fct_recode(
"16-24" = "[16,25)",
"25-44" = "[25,45)",
"45-64" = "[45,65)",
"65+" = "[65,99]"
)
) |>
labelled::set_variable_labels(
groupe_ages = "Groupe d'âges",
sexe = "Sexe"
)
Coefficients:
(Intercept) sexeFemme groupe_ages25-44 groupe_ages45-64
0.9021 -0.4455 -0.6845 -1.6535
groupe_ages65+
-2.3198
427
Le modèle contient 5 termes : 1 intercept, 1 coefficient pour
la variable sexe et 3 coefficients pour la variable groupe_ages.
Comme précédemment, nous pouvons constater que les va-
riables à n modalités sont remplacées par défaut (contrastes
de type traitement) par n-1 variables binaires, la première
modalité jouant à chaque fois le rôle de modalité de référence.
contrasts(hdv2003$sexe)
Femme
Homme 0
Femme 1
contrasts(hdv2003$groupe_ages)
[1] 0.7113809
428
variables, soit pour les 16-24 ans ici) la correction à appliquer à
l’intercept pour obtenir la probabilité de faire du sport. Il s’agit
donc de la différence entre les femmes et les hommes pour le
groupe des 16-24 ans.
inv_logit(0.9021 - 0.4455)
[1] 0.6122073
library(ggeffects)
ggpredict(mod2_trt, "sexe") |> plot()
70%
sport
60%
Homme Femme
Sexe
429
mod2_trt |>
tbl_regression(
exponentiate = TRUE,
intercept = TRUE,
add_estimate_to_reference_rows = TRUE
) |>
bold_labels()
Or, 0,64 correspond bien à l’odds ratio entre 61% et 71% (que
l’on peut calculer avec questionr::odds.ratio()).
questionr::odds.ratio(0.6122, 0.7114)
[1] 0.6404246
430
Pour prédire la probabilité de faire du sport pour un profil
particulier, il faut prendre en compte toutes les termes qui
s’appliquent et qui s’ajoutent à l’intercept. Par exemple, pour
une femme de 50 ans il faut considérer l’intercept (0.9021), le
coefficient sexeFemme (-0.4455) et le coefficient groupe_ages45-
64 (-1.6535). Sa probabilité de faire du sport est donc de 23%.
[1] 0.2320271
contr.treatment(4, base = 2)
1 3 4
1 1 0 0
2 0 0 0
3 0 1 0
4 0 0 1
contr.SAS(4)
1 2 3
1 1 0 0
2 0 1 0
3 0 0 1
4 0 0 0
431
Les contrastes peuvent être modifiés de deux manières : au mo-
ment de la construction du modèle (via l’option contrasts) ou
comme attribut des variables (via la fonction contrasts()).
ggstats::ggcoef_compare(
list(mod2_trt, mod2_trt_bis),
exponentiate = TRUE,
type = "faceted"
432
)
1 2
Sexe
Homme
Femme
Groupe d'âges
16−24
25−44
45−64
65+
ggstats::ggcoef_compare(
list(mod2_trt, mod2_trt_bis),
tidy_fun = broom.helpers::tidy_marginal_predictions,
type = "dodge",
vline = FALSE
433
)
Sexe Femme
Homme
16−24
45−64
65+
1 2
434
mod1_sum
Call:
lm(formula = marker ~ grade, data = trial)
Coefficients:
(Intercept) grade1 grade2
0.9144 0.1525 -0.2339
[1] 0.9159895
moy_groupe <-
trial |>
dplyr::group_by(grade) |>
dplyr::summarise(moyenne_marker = mean(marker, na.rm = TRUE))
moy_groupe
# A tibble: 3 x 2
grade moyenne_marker
<fct> <dbl>
1 I 1.07
2 II 0.681
3 III 0.996
mean(moy_groupe$moyenne_marker)
435
[1] 0.9144384
contrasts(trial$grade)
[,1] [,2]
I 1 0
II 0 1
III -1 -1
mod1_sum |>
tbl_regression(
intercept = TRUE,
436
add_estimate_to_reference_rows = TRUE
) |>
bold_labels()
ggstats::ggcoef_model(mod1_sum)
Grade
I (p=0.078)
II (p=0.008**)
III (p=0.355)
437
de variance expliquée, la somme des résidus ou encore l’AIC
sont identiques. En un sens, il s’agit du même modèle. C’est
seulement la manière d’interpréter les coefficients du modèle
qui change.
438
Characteristic OR 95% CI p-value
(Intercept) 0.62 0.55, 0.69 <0.001
Sexe
Homme 1.25 1.13, 1.38 <0.001
Femme 0.80 0.72, 0.89 <0.001
Groupe d’âges
16-24 3.20 2.49, 4.15 <0.001
25-44 1.62 1.38, 1.89 <0.001
45-64 0.61 0.52, 0.72 <0.001
65+ 0.31 0.24, 0.42 <0.001
Sexe
Homme (p<0.001***)
Femme (p<0.001***)
Groupe d'âges
16−24 (p<0.001***)
25−44 (p<0.001***)
45−64 (p<0.001***)
65+ (p<0.001***)
439
anova(mod2_trt, mod2_sum, test = "Chisq")
ggstats::ggcoef_compare(
list(mod2_trt, mod2_sum),
tidy_fun = broom.helpers::tidy_marginal_predictions,
type = "dodge",
vline = FALSE
)
Sexe Femme
Homme
16−24
45−64
65+
1 2
440
dalité à la seconde, etc. Ils sont disponibles avec la fonction
MASS::contr.sdif().
Illustrons cela avec un exemple.
mean(moy_groupe$moyenne_marker)
[1] 0.9144384
Cela est lié au fait que la somme des coefficients dans ce type
de contrastes est égale à 0.
441
MASS::contr.sdif(3)
2-1 3-2
1 -0.6666667 -0.3333333
2 0.3333333 -0.3333333
3 0.3333333 0.6666667
[1] -0.3863997
moy_groupe$moyenne_marker[3] - moy_groupe$moyenne_marker[2]
[1] 0.3152964
442
sexe = MASS::contr.sdif,
groupe_ages = MASS::contr.sdif
)
)
mod2_sdif |>
tbl_regression(
exponentiate = TRUE,
intercept = TRUE
) |>
bold_labels()
443
Ď Astuce
mod2_trt |>
tbl_regression(
exponentiate = TRUE,
add_pairwise_contrasts = TRUE
) |>
bold_labels()
444
24.4 Autres types de contrastes
Call:
lm(formula = marker ~ stage, data = trial)
Coefficients:
(Intercept) stage1 stage2 stage3
0.91661 0.19956 0.03294 -0.02085
445
m <- trial |>
dplyr::group_by(stage) |>
dplyr::summarise(moy = mean(marker, na.rm = TRUE))
mean(m$moy)
[1] 0.9166073
m <- m |>
dplyr::mutate(
moy_cum = dplyr::cummean(moy),
moy_cum_prec = dplyr::lag(moy_cum),
ecart = moy_cum - moy_cum_prec
)
m
# A tibble: 4 x 5
stage moy moy_cum moy_cum_prec ecart
<fct> <dbl> <dbl> <dbl> <dbl>
1 T1 0.705 0.705 NA NA
2 T2 1.10 0.905 0.705 0.200
3 T3 1.00 0.937 0.905 0.0329
4 T4 0.854 0.917 0.937 -0.0208
446
Le premier terme stage1 compare la deuxième modalité (T2)
à la première (T1) et indique l’écart entre la moyenne des
moyennes de T1 et T2 et la moyenne de T1.
Le second terme stage2 compare la troisième modalité (T3) aux
deux premières (T1 et T2) et indique l’écart entre la moyenne
des moyennes de T1, T2 et T3 par rapport à la moyenne des
moyennes de T1 et T2.
Le troisième terme stage3 compare la quatrième modalité (T4)
aux trois premières (T1, T2 et T3) et indique l’écart entre la
moyenne des moyennes de T1, T2, T3 et T4 par rapport à la
moyenne des moyennes de T1, T2 et T3.
Les contrastes de Helmert sont ainsi un peu plus complexes à
interpréter et à réserver à des cas particuliers où ils prennent
tout leur sens.
.L .Q .C
T1 -0.6708204 0.5 -0.2236068
T2 -0.2236068 -0.5 0.6708204
T3 0.2236068 -0.5 -0.6708204
T4 0.6708204 0.5 0.2236068
447
Call:
lm(formula = marker ~ stage, data = trial)
Coefficients:
(Intercept) stage.L stage.Q stage.C
0.91661 0.07749 -0.27419 0.10092
448
25 Interactions
library(tidyverse)
library(labelled)
d <-
hdv2003 |>
mutate(
sexe = sexe |> fct_relevel("Femme"),
groupe_ages = age |>
cut(
c(18, 25, 45, 65, 99),
right = FALSE,
include.lowest = TRUE,
labels = c("18-24 ans", "25-44 ans",
"45-64 ans", "65 ans et plus")
),
etudes = nivetud |>
449
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",
heures.tv = "Heures de télévision / jour"
)
450
mod |>
tbl_regression(exponentiate = TRUE) |>
bold_labels()
mod |>
broom.helpers::plot_marginal_predictions(type = "response") |>
451
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
452
mod2 |>
broom.helpers::plot_marginal_predictions(type = "response") |>
patchwork::wrap_plots(ncol = 1) &
scale_y_continuous(
labels = scales::label_percent()
)
60%
40%
20%
Primaire Secondaire
Technique / ProfessionnelSupérieur Non documenté
Niveau d'études
40%
30%
20%
10%
0.0 2.5 5.0 7.5 10.0 12.5
Heures de télévision / jour
70%
50%
30%
453
Ď Astuce
mod2 |>
broom.helpers::plot_marginal_predictions(
variables_list = "no_interaction",
type = "response"
) |>
patchwork::wrap_plots() &
scale_y_continuous(
labels = scales::label_percent()
)
60%
40% 50%
40%
35%
30%
30% 20%
Femme Homme 18−24 ans
25−44 ans
45−64 ans
65 ans et plus
Sexe Groupe d'âges
40%
60%
30%
40%
20%
20%
10%
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
454
nombre de coefficients (et donc de degrés de liberté). La ques-
tion se pose donc de savoir si l’ajout d’un terme d’interaction
améliore notre modèle.
En premier lieu, nous pouvons comparer les AIC des modèles
avec et sans interaction.
AIC(mod)
[1] 2230.404
AIC(mod2)
[1] 2223.382
L’AIC du modèle avec interaction est plus faible que celui sans
interaction, nous indiquant un gain : notre modèle avec inter-
action est donc meilleur.
On peut tester avec car::Anova() si l’interaction est statisti-
quement significative45 . 45
Lorsqu’il y a une interaction, il
est préférable d’utiliser le type III,
cf. Section 22.8. En toute rigueur,
car::Anova(mod2, type = "III")
il serait préférable de coder nos va-
riables catégorielles avec un contraste
de type somme (cf. Chapitre 24). En
Analysis of Deviance Table (Type III tests) pratique, nous pouvons nous en pas-
ser ici.
Response: sport
LR Chisq Df Pr(>Chisq)
sexe 19.349 1 1.089e-05 ***
groupe_ages 15.125 3 0.0017131 **
etudes 125.575 4 < 2.2e-16 ***
heures.tv 12.847 1 0.0003381 ***
sexe:groupe_ages 13.023 3 0.0045881 **
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
455
Nous pouvons également utiliser gtsummary::add_global_p().
mod2 |>
tbl_regression(exponentiate = TRUE) |>
add_global_p() |>
bold_labels()
456
ggtstats::ggcoef_model().
mod2 |>
ggstats::ggcoef_model(exponentiate = TRUE)
Sexe Femme
Homme (p<0.001***)
Secondaire (p<0.001***)
Supérieur (p<0.001***)
457
Supposons une femme de 60 ans, dont toutes les autres va-
riables correspondent aux modalités de référence (i.e. de ni-
veau primaire, qui ne regarde pas la télévision). Regardons ce
que prédit le modèle quant à sa probabilité de faire du sport
au travers d’une représentation graphique, grâce au package
{breakDown}.
library(breakDown)
logit <- function(x) exp(x)/(1+exp(x))
nouvelle_observation <- d[1, ]
nouvelle_observation$sexe[1] = "Femme"
nouvelle_observation$groupe_ages[1] = "45-64 ans"
nouvelle_observation$etud[1] = "Primaire"
nouvelle_observation$heures.tv[1] = 0
plot(
broken(mod2, nouvelle_observation, predict.function = betas),
trans = logit
) +
ylim(0, 1) +
ylab("Probabilité de faire du sport")
final_prognosis 0.021
heures.tv = 0 0
sexe = Femme 0
(Intercept) −0.298
458
En premier lieu, l’intercept s’applique et permet de déterminer
la probabilité de base de faire du sport à la référence. Femme
étant la modalité de référence pour la variable sexe, cela ne
modifie pas le calcul de la probabilité de faire du sport. Par
contre, il y a une modification induite par la modalité 45-64
ans de la variable groupe_ages.
Regardons maintenant la situation d’un homme de 20 ans.
nouvelle_observation$sexe[1] = "Homme"
nouvelle_observation$groupe_ages[1] = "18-24 ans"
plot(
broken(mod2, nouvelle_observation, predict.function = betas),
trans = logit
) +
ylim(0, 1.2) +
ylab("Probabilité de faire du sport")
final_prognosis 0.396
heures.tv = 0 0
(Intercept) −0.298
459
Regardons enfin la situation d’un homme de 60 ans.
final_prognosis 0.067
heures.tv = 0 0
(Intercept) −0.298
460
25.6 Définition alternative de l’interaction
ggstats::ggcoef_compare(
list("sexe * groupe_ages" = mod2, "sexe : groupe_ages" = mod3),
tidy_fun = broom.helpers::tidy_marginal_predictions,
significance = NULL,
461
vline = FALSE
) +
scale_x_continuous(labels = scales::label_percent())
Secondaire
Technique / Professionnel
Supérieur
Non documenté
12
462
Par contre, regardons d’un peu plus près les coefficients de ce
nouveau modèle. Nous allons voir que leur interprétation est
légèrement différente.
mod3 |>
ggstats::ggcoef_model(exponentiate = TRUE)
Niveau d'études
Primaire
Secondaire (p<0.001***)
Supérieur (p<0.001***)
1 3 10
OR
463
plot(
broken(mod3, nouvelle_observation, predict.function = betas),
trans = logit
) +
ylim(0, 1.2) +
ylab("Probabilité de faire du sport")
final_prognosis 0.067
heures.tv = 0 0
(Intercept) −0.372
464
Ď Astuce
25.8 webin-R
465
26 Multicolinéarité
26.1 Définition
466
modèle linéaire et, par extension, un modèle linéaire généralisé
(dont les modèles de régression logistique).
Dans les faits, une multicolinéarité parfaite n’est quasiment
jamais observée. Mais une forte multicolinéarité entre plu-
sieurs variables peut poser problème dans l’estimation et
l’interprétation d’un modèle.
Une erreur fréquente est de confondre multicolinéarité et cor-
rélation. Si des variables colinéaires sont de facto fortement
corrélées entre elles, deux variables corrélées ne sont pas forcé-
ment colinéaires. En termes non statistiques, il y a colinéarité
lorsque deux ou plusieurs variables mesurent la même chose.
Prenons un exemple. Nous étudions les complications après
l’accouchement dans différentes maternités d’un pays en déve-
loppement. On souhaite mettre dans le modèle, à la fois le mi-
lieu de résidence (urbain ou rural) et le fait qu’il y ait ou non
un médecin dans la clinique. Or, dans la zone d’enquête, les
maternités rurales sont dirigées seulement par des sage-femmes
tandis que l’on trouve un médecin dans toutes les maternités
urbaines sauf une. Dès lors, dans ce contexte précis, le milieu de
résidence prédit presque totalement la présence d’un médecin et
on se retrouve face à une multicolinéarité (qui serait même par-
faite s’il n’y avait pas une clinique urbaine sans médecin). On
ne peut donc distinguer l’effet de la présence d’un médecin de
celui du milieu de résidence et il ne faut mettre qu’une seule de
ces deux variables dans le modèle, sachant que du point de vue
de l’interprétation elle capturera à la fois l’effet de la présence
d’un médecin et celui du milieu de résidence.
Par contre, si dans notre région d’étude, seule la moitié des
maternités urbaines disposait d’un médecin, alors le milieu de
résidence n’aurait pas été suffisant pour prédire la présence d’un
médecin. Certes, les deux variables seraient corrélées mais pas
colinéaires. Un autre exemple de corrélation sans colinéarité,
c’est la relation entre milieu de résidence et niveau d’instruction.
Il y a une corrélation entre ces deux variables, les personnes
résidant en ville étant généralement plus instruites. Cependant,
il existe également des personnes non instruites en ville et des
personnes instruites en milieu rural. Le milieu de résidence n’est
donc pas suffisant pour prédire le niveau d’instruction.
467
26.2 Mesure de la colinéarité
library(tidyverse)
library(labelled)
d <-
hdv2003 |>
mutate(
sexe = sexe |> fct_relevel("Femme"),
468
groupe_ages = age |>
cut(
c(18, 25, 45, 65, 99),
right = FALSE,
include.lowest = 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",
heures.tv = "Heures de télévision / jour"
)
469
GVIF Df GVIF^(1/(2*Df))
sexe 1.024640 1 1.012245
groupe_ages 1.745492 3 1.097285
etudes 1.811370 4 1.077087
heures.tv 1.057819 1 1.028503
library(gtsummary)
theme_gtsummary_language(
"fr",
decimal.mark = ",",
big.mark = " "
)
mod |>
tbl_regression(exponentiate = TRUE) |>
bold_labels() |>
add_vif()
95% p- Adjusted
Caractéristique OR IC valeur GVIF GVIF
Sexe 1,0 1,0
Femme — —
Homme 1,52 1,24 – <0,001
1,87
Groupe d’âges 1,7 1,1
18-24 ans — —
470
95% p- Adjusted
Caractéristique OR IC valeur GVIF GVIF
25-44 ans 0,68 0,43 – 0,084
1,06
45-64 ans 0,36 0,23 – <0,001
0,57
65 ans et plus 0,27 0,16 – <0,001
0,46
Niveau 1,8 1,1
d’études
Primaire — —
Secondaire 2,54 1,73 – <0,001
3,75
Technique / 2,81 1,95 – <0,001
Professionnel 4,10
Supérieur 6,55 4,50 – <0,001
9,66
Non documenté 8,54 4,51 – <0,001
16,5
Heures de 0,89 0,83 – <0,001 1,1 1,0
télévision / 0,95
jour
Low Correlation
471
Les variables avec un FIV entre 5 et 10 sont présentées comme
ayant une corrélation moyenne et celles avec un FIV de 10 ou
plus une corrélation forte. Prenons un autre exemple.
mc
Low Correlation
Moderate Correlation
High Correlation
plot(mc)
472
Variable `Component` is not in your data frame :/
Collinearity
High collinearity (VIF) may inflate parameter uncertainty
Variance Inflation Factor (VIF, log−scaled)
30
10
1
am cyl gear vs vs:cyl wt
Low (< 5) Moderate (< 10) High (= 10)
mc |> performance::print_md()
473
26.3 La multicolinéarité est-elle toujours un
problème ?
474
Si vous spécifiez un modèle de régression avec x et x2 , il y a de
bonnes chances que ces deux variables soient fortement corré-
lées. De même, si votre modèle a x, z et xz, x et z sont suscep-
tibles d’être fortement corrélés avec leur produit. Il n’y a pas
de quoi s’inquiéter, car la valeur p de xz n’est pas affectée par
la multicolinéarité. Ceci est facile à démontrer : vous pouvez ré-
duire considérablement les corrélations en centrant les variables
(c’est-à-dire en soustrayant leurs moyennes) avant de créer les
puissances ou les produits. Mais la valeur p pour x2 ou pour xz
sera exactement la même, que l’on centre ou non. Et tous les
résultats pour les autres variables (y compris le R2 mais sans
les termes d’ordre inférieur) seront les mêmes dans les deux cas.
La multicolinéarité n’a donc pas de conséquences négatives.
3. Les variables avec des FIV élevés sont des variables
indicatrices (factices) qui représentent une variable ca-
tégorielle avec trois catégories ou plus.
Si la proportion de cas dans la catégorie de référence est faible,
les variables indicatrices auront nécessairement des FIV élevés,
même si la variable catégorielle n’est pas associée à d’autres
variables dans le modèle de régression.
Supposons, par exemple, qu’une variable de l’état matrimonial
comporte trois catégories : actuellement marié, jamais marié
et anciennement marié. Vous choisissez anciennement marié
comme catégorie de référence, avec des variables d’indicateur
pour les deux autres. Ce qui se passe, c’est que la corrélation
entre ces deux indicateurs devient plus négative à mesure que
la fraction de personnes dans la catégorie de référence diminue.
Par exemple, si 45 % des personnes ne sont jamais mariées, 45 %
sont mariées et 10 % sont anciennement mariées, les valeurs du
FIV pour les personnes mariées et les personnes jamais mariées
seront d’au moins 3,0.
Est-ce un problème ? Eh bien, cela signifie que les valeurs p
des variables indicatrices peuvent être élevées. Mais le test glo-
bal selon lequel tous les indicateurs ont des coefficients de zéro
n’est pas affecté par des FIV élevés. Et rien d’autre dans la
régression n’est affecté. Si vous voulez vraiment éviter des FIV
élevés, il suffit de choisir une catégorie de référence avec une
plus grande fraction des cas. Cela peut être souhaitable pour
éviter les situations où aucun des indicateurs individuels n’est
475
statistiquement significatif, même si l’ensemble des indicateurs
est significatif.
26.4 webin-R
476
partie IV
477
27 Définir un plan
d’échantillonnage
478
tillons possibles (de même taille) ont la même probabilité d’être
choisis et tous les éléments de la population ont une chance
égale de faire partie de l’échantillon. C’est l’échantillonnage le
plus simple : chaque individu à la même probabilité d’être sé-
lectionné.
L’échantillonnage stratifié est une méthode qui consiste
d’abord à subdiviser la population en groupes homogènes
(strates) pour ensuite extraire un échantillon aléatoire de
chaque strate. Cette méthode suppose une connaissance de la
structure de la population. Pour estimer les paramètres, les
résultats doivent être pondérés par l’importance relative de
chaque strate dans la population.
L’échantillonnage par grappes est une méthode qui consiste à
choisir un échantillon aléatoire d’unités qui sont elles-mêmes
des sous-ensembles de la population (grappes ou clusters en an-
glais). Cette méthode suppose que les unités de chaque grappe
sont représentatives. Elle possède l’avantage d’être souvent plus
économique.
Il est possible de combiner plusieurs de ces approches. Par
exemple, les Enquêtes Démographiques et de Santé 48 (EDS) 48
Vaste programme d’enquêtes réa-
sont des enquêtes stratifiées en grappes à deux degrés. Dans un lisées à intervalles réguliers dans les
pays à faible et moyen revenu, dispo-
premier temps, la population est divisée en strates par région et
nibles sur https://dhsprogram.com/.
milieu de résidence. Dans chaque strate, des zones d’enquêtes,
correspondant à des unités de recensement, sont tirées au sort
avec une probabilité proportionnelle au nombre de ménages de
chaque zone au dernier recensement de population. Enfin, au
sein de chaque zone d’enquête sélectionnée, un recensement de
l’ensemble des ménages est effectué puis un nombre identique
de ménages par zone d’enquête est tiré au sort de manière aléa-
toire simple.
479
L’agument data permet de spécifier le tableau de données conte-
nant les observations.
L’argument ids est obligatoire et spécifie sous la forme d’une
formule les identifiants des différents niveaux d’un tirage en
grappe. S’il s’agit d’un échantillon aléatoire simple, on entrera
ids = ~ 1. Autre situation : supposons une étude portant sur
la population française. Dans un premier temps, on a tiré au
sort un certain nombre de départements français. Dans un se-
cond temps, on tire au sort dans chaque département des com-
munes. Dans chaque commune sélectionnée, on tire au sort
des quartiers. Enfin, on interroge de manière exhaustive toutes
les personnes habitant les quartiers enquêtés. Notre fichier de
données devra donc comporter pour chaque observation les va-
riables id_departement, id_commune et id_quartier. On écrira
alors pour l’argument ids la valeur suivante :
ids = ~ id_departement + id_commune + id_quartier.
Si l’échantillon est stratifié, on spécifiera les strates à
l’aide de l’argument strata en spécifiant la variable conte-
nant l’identifiant des strates. Par exemple : strata = ~
id_strate.
Il faut encore spécifier les probabilités de tirage de chaque clus-
ter /grappe ou bien la pondération des individus. Si l’on dis-
pose de la probabilité de chaque observation d’être sélection-
née, on utilisera l’argument probs. Si, par contre, on connaît
la pondération de chaque observation (qui doit être proportion-
nelle à l’inverse de cette probabilité), on utilisera l’argument
weights.
Si l’échantillon est stratifié, qu’au sein de chaque strate les in-
dividus ont été tirés au sort de manière aléatoire et que l’on
connaît la taille de chaque strate, il est possible de ne pas avoir
à spécifier la probabilité de tirage ou la pondération de chaque
observation. Il est préférable de fournir une variable contenant
la taille de chaque strate à l’argument fpc. De plus, dans ce cas-
là, une petite correction sera appliquée au modèle pour prendre
en compte la taille finie de chaque strate.
On peut tout à fait définir un échantillonnage aléatoire
simple (on considère donc que toutes les observations ont
480
le même poids, égal à 1). Pour rappel, en l’absence de clus-
ters/grappes, il faut préciser ids = ~ 1, ce paramètre n’ayant
pas de valeur par défaut.
p_iris
481
Independent Sampling design (with replacement)
survey::svydesign(ids = ~1, data = titanic, weights = ~n)
482
la taille des grappes est connue et renseignée dans les variables
fpc1 et fpc2 que nous pourrons donc transmettre via l’argument
fpc.
483
strata = ~ V022,
weights = ~ poids
)
class(t_iris)
484
[1] "tbl_svy" "survey.design2" "survey.design"
485
Pour une enquête en grappes à 1 degré, pour laquelle
l’identifiant des grappes (clusters) est indiqué par la variable
dnum.
486
Called via srvyr
Sampling variables:
- ids: `dnum + snum`
- fpc: `fpc1 + fpc2`
Data variables: cds (chr), stype (fct), name (chr), sname (chr), snum (dbl),
dname (chr), dnum (int), cname (chr), cnum (int), flag (int), pcttest (int),
api00 (int), api99 (int), target (int), growth (int), sch.wide (fct),
comp.imp (fct), both (fct), awards (fct), meals (int), ell (int), yr.rnd
(fct), mobility (int), acs.k3 (int), acs.46 (int), acs.core (int), pct.resp
(int), not.hsg (int), hsg (int), some.col (int), col.grad (int), grad.sch
(int), avg.ed (dbl), full (int), emer (int), enroll (int), api.stu (int), pw
(dbl), fpc1 (dbl), fpc2 (int[1d])
487
27.4 webin-R
488
28 Manipulation de données
pondérées
Rows: 32
Columns: 5
$ Class <chr> "1st", "2nd", "3rd", "Crew", "1st", "2nd", "3rd", "Crew", "1s~
$ Sex <chr> "Male", "Male", "Male", "Male", "Female", "Female", "Female",~
$ Age <chr> "Child", "Child", "Child", "Child", "Child", "Child", "Child"~
$ Survived <chr> "No", "No", "No", "No", "No", "No", "No", "No", "No", "No", "~
$ n <dbl> 0, 0, 35, 0, 0, 0, 17, 0, 118, 154, 387, 670, 4, 13, 89, 3, 5~
489
28.1 Utilisation de {srvyr}
library(srvyr)
filter
490
Á Avertissement
t_titanic |>
group_by(Sex, Class, Survived) |>
summarise(taux_survie = survey_prop()) |>
filter(Survived == "Yes")
# A tibble: 8 x 5
# Groups: Sex, Class [8]
491
Sex Class Survived taux_survie taux_survie_se
<chr> <chr> <chr> <dbl> <dbl>
1 Female 1st Yes 0.972 0.0384
2 Female 2nd Yes 0.877 0.145
3 Female 3rd Yes 0.459 0.306
4 Female Crew Yes 0.870 0.163
5 Male 1st Yes 0.344 0.312
6 Male 2nd Yes 0.140 0.150
7 Male 3rd Yes 0.173 0.183
8 Male Crew Yes 0.223 0.249
492
pos variable label col_type missing values
4 Survived A survécu au naufrage ? chr 0
493
29 Analyses uni- et bivariées
pondérées
494
filter
library(gtsummary)
theme_gtsummary_language(
language = "fr",
decimal.mark = ",",
big.mark = " "
)
495
dp |>
tbl_svysummary(
by = milieu,
include = c(age, educ, travail)
) |>
add_overall(last = TRUE) |>
bold_labels()
urbain,
N=1 rural, N Total, N
Caractéristique 026 = 1 002 = 2 027
Âge révolu (en années) 26 (20 – 28 (22 – 27 (21 –
à la date de passation 33) 36) 35)
du questionnaire
Niveau d’éducation
aucun 414 681 1 095
(40%) (68%) (54%)
primaire 251 257 507
(24%) (26%) (25%)
secondaire 303 61 364
(30%) (6,1%) (18%)
supérieur 58 3 (0,3%) 61
(5,7%) (3,0%)
A un emploi ?
non 401 269 670
(39%) (27%) (33%)
oui 621 731 1 351
(61%) (73%) (67%)
Manquant 5 1 6
496
ĺ Important
497
set_gtsummary_theme(
list("tbl_summary-str:missing_stat" = "{N_miss_unweighted} obs.")
)
dp |>
tbl_svysummary(
by = milieu,
include = c(educ, travail),
statistic = all_categorical() ~ "{p}% ({n_unweighted} obs.)",
digits = all_categorical() ~ c(1, 0)
) |>
modify_header(
all_stat_cols() ~ "**{level}** ({n_unweighted} obs.)"
) |>
bold_labels()
urbain (912
Caractéristique obs.) rural (1088 obs.)
Niveau
d’éducation
aucun 40,3% (375 obs.) 68,0% (763 obs.)
primaire 24,4% (213 obs.) 25,6% (247 obs.)
secondaire 29,5% (275 obs.) 6,1% (73 obs.)
supérieur 5,7% (49 obs.) 0,3% (5 obs.)
A un emploi ?
non 39,2% (370 obs.) 26,9% (296 obs.)
oui 60,8% (537 obs.) 73,1% (790 obs.)
Manquant 5 obs. 2 obs.
498
29.2 Calcul manuel avec {survey}
mean SE
age 28.468 0.2697
2.5 % 97.5 %
age 27.93931 28.99653
499
$age
quantile ci.2.5 ci.97.5 se
0.25 21 21 22 0.2549523
0.5 27 27 28 0.2549523
0.75 35 35 37 0.5099045
attr(,"hasci")
[1] TRUE
attr(,"class")
[1] "newsvyquantile"
region
Nord Est Sud Ouest
611.0924 175.7404 329.2220 911.2197
educ
milieu aucun primaire secondaire supérieur
urbain 413.608780 250.665214 303.058978 58.412688
rural 681.131096 256.694363 61.023980 2.679392
500
n % val%
Nord 611.1 30.1 30.1
Est 175.7 8.7 8.7
Sud 329.2 16.2 16.2
Ouest 911.2 44.9 44.9
Total 2027.3 100.0 100.0
educ
milieu aucun primaire secondaire supérieur Ensemble
urbain 37.8 49.4 83.2 95.6 50.6
rural 62.2 50.6 16.8 4.4 49.4
Total 100.0 100.0 100.0 100.0 100.0
region age se
Nord Nord 29.03299 0.4753268
Est Est 27.54455 0.5261669
Sud Sud 28.96830 0.6148223
Ouest Ouest 28.08626 0.4458201
501
méthodes utilisées sont adaptées à la prise en compte d’un
plan d’échantillonnage. On se référera à la document de la fonc-
tion pour plus de détails sur les méthodes statistiques utilisées.
Rappel : pour les variables continues, on sera vigilant à ce
que la statistique affichée (médiane par défaut) corresponde au
type d’intervalle de confiance calculé (moyenne par défaut).
dp |>
tbl_svysummary(
include = c(age, region),
statistic = all_continuous() ~ "{mean} ({sd})"
) |>
add_ci() |>
bold_labels()
N=2 95%
Caractéristique 027 CI
Âge révolu (en années) à la date de 28 (9) 28, 29
passation du questionnaire
Région de résidence
Nord 611 28%,
(30%) 33%
Est 176 7,7%,
(8,7%) 9,8%
Sud 329 14%,
(16%) 18%
Ouest 911 42%,
(45%) 48%
502
adaptations des tests classiques avec différentes corrections
pour tenir compte à la fois de la pondération et du plan
d’échantillonnage.
dp |>
tbl_svysummary(
include = c(age, region),
by = milieu
) |>
add_p() |>
bold_labels()
urbain,
N=1 rural, N p-
Caractéristique 026 = 1 002 valeur
Âge révolu (en années) à 26 (20 – 28 (22 – <0,001
la date de passation du 33) 36)
questionnaire
Région de résidence <0,001
Nord 265 346
(26%) (35%)
Est 48 (4,7%) 128
(13%)
Sud 79 (7,7%) 250
(25%)
Ouest 633 278
(62%) (28%)
503
29.4 Impact du plan d’échantillonnage
[1] 200
summary(apistrat$pw)
sum(apistrat$pw)
[1] 6194
504
tbl
yr.rnd
awards No Yes
No 2068.34 168.09
Yes 3274.06 683.51
data: tbl
X-squared = 113.84, df = 1, p-value < 2.2e-16
data: NextMethod()
F = 2.9162, ndf = 1, ddf = 199, p-value = 0.08926
Le résultat est ici tout autre et notre test n’est plus significatif
au seuil de 5% ! Ici, les corrections de Rao & Scott permettent
justement de tenir compte que nous avons un échantillon de
seulement 200 observations.
505
Regardons maintenant si, à poids égal, il y a une différence
entre une enquête stratifiée et une enquête en grappes.
# Pondération simple
survey::svytable(~ awards + yr.rnd, design = d_ponderation_simple)
yr.rnd
awards No Yes
No 2068.34 168.09
Yes 3274.06 683.51
data: NextMethod()
F = 2.9162, ndf = 1, ddf = 199, p-value = 0.08926
# Enquête stratifiée
d_strates <- apistrat |>
as_survey_design(weights = pw, strata = stype)
survey::svytable(~ awards + yr.rnd, design = d_strates)
yr.rnd
awards No Yes
No 2068.34 168.09
Yes 3274.06 683.51
data: NextMethod()
F = 2.9007, ndf = 1, ddf = 197, p-value = 0.09012
506
# Enquête en grappes
d_grappes <- apistrat |>
as_survey_design(weights = pw, ids = dnum)
survey::svytable(~ awards + yr.rnd, design = d_grappes)
yr.rnd
awards No Yes
No 2068.34 168.09
Yes 3274.06 683.51
data: NextMethod()
F = 3.1393, ndf = 1, ddf = 134, p-value = 0.0787
507
30 Graphiques pondérés
Á Avertissement
508
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 (<http://conflicted.r-lib.org/>) to force all conflicts to become
d <- labelled::unlabelled(femmes)
ggplot(d) +
aes(x = region, fill = test, weight = poids) +
geom_bar(position = "fill")
1.00
0.75
test
count
non
0.50
oui
manquant
0.25
0.00
Nord Est Sud Ouest
region
library(srvyr)
509
Attachement du package : 'srvyr'
filter
ggplot(dp$variables) +
aes(x = region, fill = test, weight = weights(dp)) +
geom_bar(position = "fill")
1.00
0.75
test
count
non
0.50
oui
manquant
0.25
0.00
Nord Est Sud Ouest
region
510
Ainsi, le code de notre graphique précédent s’écrit tout simple-
ment51 : 51
Notez que les poids ont déjà été
associés à la bonne esthétique et qu’il
n’est donc pas nécessaire de le refaire
ggstats::ggsurvey(dp) +
dans l’appel à aes().
aes(x = region, fill = test) +
geom_bar(position = "fill")
1.00
0.75
test
count
non
0.50
oui
manquant
0.25
0.00
Nord Est Sud Ouest
region
511
31 Régression logistique
binaire pondérée
library(tidyverse)
library(labelled)
data(hdv2003, package = "questionr")
d <-
hdv2003 |>
mutate(
sexe = sexe |> fct_relevel("Femme"),
groupe_ages = age |>
cut(
c(18, 25, 45, 65, 99),
right = FALSE,
include.lowest = TRUE,
512
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",
heures.tv = "Heures de télévision / jour",
poids = "Pondération de l'enquête"
)
library(srvyr)
library(survey)
dp <- d |>
as_survey_design(weights = poids)
513
La plupart du temps, les poids de pondération ne sont pas des
nombres entiers, mais des nombres décimaux. Or, la famille de
modèles binomiaux repose sur des nombres entiers de succès
et d’échecs. Avec une version récente52 de R, cela n’est pas 52
Si vous utilisez une version an-
problématique. Nous aurons simplement un avertissement. cienne de R, cela n’était tout simple-
ment pas possible. Vous obteniez un
message d’erreur et le modèle n’était
mod_binomial <- svyglm( pas calculé. Si c’est votre cas, op-
sport ~ sexe + groupe_ages + etudes + relig + heures.tv,
tez pour un modèle quasi-binomial ou
family = binomial, bien mettez à jour R.
design = dp
)
Simple, non ?
514
mod_quasi2 <- step(mod_quasi)
Start: AIC=2309.89
sport ~ sexe + groupe_ages + etudes + relig + heures.tv
Df Deviance AIC
- relig 5 2266.3 2302.2
<none> 2263.9 2309.9
- heures.tv 1 2276.2 2320.2
- sexe 1 2276.4 2320.4
- groupe_ages 3 2313.9 2353.8
- etudes 4 2383.5 2421.2
Step: AIC=2296.28
sport ~ sexe + groupe_ages + etudes + heures.tv
Df Deviance AIC
<none> 2266.3 2296.3
- heures.tv 1 2278.4 2306.4
- sexe 1 2279.0 2307.0
- groupe_ages 3 2318.3 2342.1
- etudes 4 2387.2 2408.8
515
step_with_na_survey <- function(model, design, ...) {
# list of variables
variables <- broom.helpers::model_list_variables(
model,
only_variable = TRUE
)
# design with no na
design_no_na <- design |>
srvyr::drop_na(dplyr::any_of(variables))
# refit the model without NAs
model_no_na <- update(model, data = design_no_na)
# apply step()
model_simplified <- step(model_no_na, ...)
# recompute simplified model using full data
update(model, formula = terms(model_simplified))
}
library(gtsummary)
theme_gtsummary_language("fr", decimal.mark = ",", big.mark = " ")
mod_quasi2 |>
tbl_regression(exponentiate = TRUE) |>
add_global_p(keep = TRUE) |>
add_vif() |>
bold_labels()
516
Warning in printHypothesis(L, rhs, names(b)): one or more coefficients in the hypothesis includ
arithmetic operators in their names;
the printed representation of the hypothesis will be omitted
Warning in printHypothesis(L, rhs, names(b)): one or more coefficients in the hypothesis includ
arithmetic operators in their names;
the printed representation of the hypothesis will be omitted
95% p- Adjusted
Caractéristique OR IC valeur GVIF GVIF
Sexe 0,005 1,0 1,0
Femme — —
Homme 1,44 1,12 – 0,005
1,87
Groupe d’âges <0,001 2,1 1,1
18-24 ans — —
25-44 ans 0,85 0,48 – 0,6
1,51
45-64 ans 0,40 0,22 – 0,003
0,73
65 ans et plus 0,37 0,19 – 0,004
0,72
Niveau <0,001 2,2 1,1
d’études
Primaire — —
Secondaire 2,66 1,62 – <0,001
4,38
Technique / 3,09 1,90 – <0,001
Professionnel 5,00
Supérieur 6,54 3,99 – <0,001
10,7
517
95% p- Adjusted
Caractéristique OR IC valeur GVIF GVIF
Non documenté 10,3 4,60 – <0,001
23,0
Heures de 0,89 0,82 – 0,006 1,1 1,0
télévision / 0,97
jour
mod_quasi2 |>
ggstats::ggcoef_model(exponentiate = TRUE)
Sexe Femme
Homme (p=0.005**)
518
mod_quasi2 |>
broom.helpers::plot_marginal_predictions(type = "response") |>
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
519
partie V
Manipulation avancée
520
32 Fusion de tables
library(tidyverse)
library(nycflights13)
data(flights)
data(airports)
data(airlines)
521
que la table flights contient le code de la compagnie aérienne
du vol dans la variable carrier :
522
table seront associées à une autre en se basant non pas sur leur
position, mais sur les valeurs d’une ou plusieurs colonnes. Ces
colonnes sont appelées des clés.
Pour faire une jointure de ce type, on va utiliser la fonction
dplyr::left_join() :
fusion |>
select(month, day, carrier, name) |>
head(10)
# A tibble: 10 x 4
month day carrier name
<int> <int> <chr> <chr>
1 1 1 UA United Air Lines Inc.
2 1 1 UA United Air Lines Inc.
3 1 1 AA American Airlines Inc.
4 1 1 B6 JetBlue Airways
5 1 1 DL Delta Air Lines Inc.
6 1 1 UA United Air Lines Inc.
7 1 1 B6 JetBlue Airways
8 1 1 EV ExpressJet Airlines Inc.
9 1 1 B6 JetBlue Airways
10 1 1 AA American Airlines Inc.
On voit que la table obtenue est bien la fusion des deux tables
d’origine selon les valeurs des deux colonnes clés carrier. On est
parti de la table flights, et pour chaque ligne on a ajouté les
colonnes de airlines pour lesquelles la valeur de carrier est la
même. On a donc bien une nouvelle colonne name dans notre
table résultat, avec le nom complet de la compagnie aérienne.
523
Ĺ Note
Error in `left_join()`:
! `by` must be supplied when `x` and `y` have no common variables.
524
i Use `cross_join()` to perform a cross-join.
flights_ex |>
left_join(airports_ex, by = c("origin" = "faa")) |>
head(10)
# A tibble: 10 x 6
month day origin dest alt name
<int> <int> <chr> <chr> <dbl> <chr>
1 1 1 EWR IAH 18 Newark Liberty Intl
2 1 1 LGA IAH 22 La Guardia
3 1 1 JFK MIA 13 John F Kennedy Intl
4 1 1 JFK BQN 13 John F Kennedy Intl
5 1 1 LGA ATL 22 La Guardia
6 1 1 EWR ORD 18 Newark Liberty Intl
7 1 1 EWR FLL 18 Newark Liberty Intl
8 1 1 LGA IAD 22 La Guardia
9 1 1 JFK MCO 13 John F Kennedy Intl
10 1 1 LGA ORD 22 La Guardia
525
flights_ex |>
left_join(airports_ex, by=c("dest" = "faa")) |>
head(10)
# A tibble: 10 x 8
month day origin dest alt.x name.x alt.y name.y
<int> <int> <chr> <chr> <dbl> <chr> <dbl> <chr>
1 1 1 EWR IAH 18 Newark Liberty Intl 97 George Bush Interco~
2 1 1 LGA IAH 22 La Guardia 97 George Bush Interco~
3 1 1 JFK MIA 13 John F Kennedy Intl 8 Miami Intl
4 1 1 JFK BQN 13 John F Kennedy Intl NA <NA>
5 1 1 LGA ATL 22 La Guardia 1026 Hartsfield Jackson ~
6 1 1 EWR ORD 18 Newark Liberty Intl 668 Chicago Ohare Intl
7 1 1 EWR FLL 18 Newark Liberty Intl 9 Fort Lauderdale Hol~
8 1 1 LGA IAD 22 La Guardia 313 Washington Dulles I~
9 1 1 JFK MCO 13 John F Kennedy Intl 96 Orlando Intl
10 1 1 LGA ORD 22 La Guardia 668 Chicago Ohare Intl
flights_ex |>
left_join(
airports_ex,
by = c("dest" = "faa"),
suffix = c("_depart", "_arrivee")
) |>
head(10)
526
# A tibble: 10 x 8
month day origin dest alt_depart name_depart alt_arrivee name_arrivee
<int> <int> <chr> <chr> <dbl> <chr> <dbl> <chr>
1 1 1 EWR IAH 18 Newark Liberty ~ 97 George Bush~
2 1 1 LGA IAH 22 La Guardia 97 George Bush~
3 1 1 JFK MIA 13 John F Kennedy ~ 8 Miami Intl
4 1 1 JFK BQN 13 John F Kennedy ~ NA <NA>
5 1 1 LGA ATL 22 La Guardia 1026 Hartsfield ~
6 1 1 EWR ORD 18 Newark Liberty ~ 668 Chicago Oha~
7 1 1 EWR FLL 18 Newark Liberty ~ 9 Fort Lauder~
8 1 1 LGA IAD 22 La Guardia 313 Washington ~
9 1 1 JFK MCO 13 John F Kennedy ~ 96 Orlando Intl
10 1 1 LGA ORD 22 La Guardia 668 Chicago Oha~
# A tibble: 6 x 2
nom voiture
<chr> <chr>
1 Sylvie Twingo
2 Sylvie Ferrari
3 Monique Scenic
4 Gunter Lada
5 Rayan Twingo
6 Rayan Clio
527
voitures <- tibble(
voiture = c("Twingo", "Ferrari", "Clio", "Lada", "208"),
vitesse = c("140", "280", "160", "85", "160")
)
voitures
# A tibble: 5 x 2
voiture vitesse
<chr> <chr>
1 Twingo 140
2 Ferrari 280
3 Clio 160
4 Lada 85
5 208 160
32.1.3.1 left_join()
# A tibble: 6 x 3
nom voiture vitesse
<chr> <chr> <chr>
1 Sylvie Twingo 140
2 Sylvie Ferrari 280
3 Monique Scenic <NA>
4 Gunter Lada 85
5 Rayan Twingo 140
6 Rayan Clio 160
528
La clé de fusion étant unique dans la table de droite, le nombre
de lignes de la table de gauche est donc bien préservée.
[1] 6
[1] 6
# A tibble: 6 x 3
voiture vitesse nom
<chr> <chr> <chr>
1 Twingo 140 Sylvie
2 Twingo 140 Rayan
3 Ferrari 280 Sylvie
4 Clio 160 Rayan
5 Lada 85 Gunter
6 208 160 <NA>
La ligne 208 est bien là avec la variable nom remplie avec une
valeur manquante NA. Par contre Monique est absente.
ĺ Important
529
En résumé, quand on fait un left_join(x, y),
toutes les lignes de x sont présentes, et dupliquées
si nécessaire quand elles apparaissent plusieurs fois
dans y. Les lignes de y non présentes dans x dis-
paraissent. Les lignes de x non présentes dans y se
voient attribuer des valeurs manquantes NA pour
les nouvelles colonnes.
32.1.3.2 right_join()
# A tibble: 6 x 3
nom voiture vitesse
<chr> <chr> <chr>
1 Sylvie Twingo 140
2 Sylvie Ferrari 280
3 Gunter Lada 85
4 Rayan Twingo 140
5 Rayan Clio 160
6 <NA> 208 160
# A tibble: 6 x 3
voiture vitesse nom
<chr> <chr> <chr>
1 Twingo 140 Sylvie
530
2 Twingo 140 Rayan
3 Ferrari 280 Sylvie
4 Clio 160 Rayan
5 Lada 85 Gunter
6 208 160 <NA>
32.1.3.3 inner_join()
# A tibble: 5 x 3
nom voiture vitesse
<chr> <chr> <chr>
1 Sylvie Twingo 140
2 Sylvie Ferrari 280
3 Gunter Lada 85
4 Rayan Twingo 140
5 Rayan Clio 160
Ici la ligne 208 est absente, ainsi que la ligne Monique, qui dans
le cas d’un dplyr::left_join() avait été conservée et s’était
vue attribuer NA à vitesse.
32.1.3.4 full_join()
# A tibble: 7 x 3
nom voiture vitesse
531
<chr> <chr> <chr>
1 Sylvie Twingo 140
2 Sylvie Ferrari 280
3 Monique Scenic <NA>
4 Gunter Lada 85
5 Rayan Twingo 140
6 Rayan Clio 160
7 <NA> 208 160
# A tibble: 5 x 2
nom voiture
<chr> <chr>
1 Sylvie Twingo
2 Sylvie Ferrari
3 Gunter Lada
4 Rayan Twingo
5 Rayan Clio
# A tibble: 1 x 2
nom voiture
532
<chr> <chr>
1 Monique Scenic
533
personnes |> left_join(voitures)
# A tibble: 6 x 3
nom voiture vitesse
<chr> <chr> <chr>
1 Sylvie Twingo 140
2 Sylvie Ferrari 280
3 Monique Scenic <NA>
4 Gunter Lada 85
5 Rayan Twingo 140
6 Rayan Clio 160
# A tibble: 2 x 4
faa name lat lon
534
<chr> <chr> <dbl> <dbl>
1 04G Lansdowne Airport 41.1 -80.6
2 06A Moton Field Municipal Airport 32.5 -85.7
t2
# A tibble: 2 x 4
name faa lon lat
<chr> <chr> <dbl> <dbl>
1 Jekyll Island Airport 09J -81.4 31.1
2 Elizabethton Municipal Airport 0A9 -82.2 36.4
# A tibble: 2 x 2
faa name
<chr> <chr>
1 ADW Andrews Afb
2 AET Allakaket Airport
# A tibble: 6 x 4
faa name lat lon
<chr> <chr> <dbl> <dbl>
1 04G Lansdowne Airport 41.1 -80.6
2 06A Moton Field Municipal Airport 32.5 -85.7
3 09J Jekyll Island Airport 31.1 -81.4
535
4 0A9 Elizabethton Municipal Airport 36.4 -82.2
5 ADW Andrews Afb NA NA
6 AET Allakaket Airport NA NA
# A tibble: 6 x 5
source faa name lat lon
<chr> <chr> <chr> <dbl> <dbl>
1 1 04G Lansdowne Airport 41.1 -80.6
2 1 06A Moton Field Municipal Airport 32.5 -85.7
3 2 09J Jekyll Island Airport 31.1 -81.4
4 2 0A9 Elizabethton Municipal Airport 36.4 -82.2
5 3 ADW Andrews Afb NA NA
6 3 AET Allakaket Airport NA NA
# A tibble: 6 x 5
source faa name lat lon
536
<chr> <chr> <chr> <dbl> <dbl>
1 table1 04G Lansdowne Airport 41.1 -80.6
2 table1 06A Moton Field Municipal Airport 32.5 -85.7
3 table2 09J Jekyll Island Airport 31.1 -81.4
4 table2 0A9 Elizabethton Municipal Airport 36.4 -82.2
5 table3 ADW Andrews Afb NA NA
6 table3 AET Allakaket Airport NA NA
537
33 Dates avec lubridate
library(tidyverse)
library(nycflights13)
538
Les classes Date et POSIXct sont gérées nativement par R
tandis que la classe hms est fournies par le package homonyme
{hms}. Cette dernière classe est d’un usage plus spécifique.
Dans cette section, nous allons nous concentrer sur les dates
et les dates-heures.
Il est toujours préférable d’utiliser la classe la plus simple. Si
vous gérez uniquement des dates, privilégiez la classe Date. La
classe POSIXct, plus complexe, permet d’ajouter une heure as-
sociée à un fuseau horaire.
Pour obtenir la date ou la date-heure courante, vous pouvez
appeler today() ou now() :
today()
[1] "2023-08-15"
now()
# A tibble: 1 x 2
date datetime
<date> <dttm>
1 2022-01-02 2022-01-02 05:12:00
539
Ď Astuce
1
https://xkcd.com/1179/
540
Type Code Signification Exemple
%z décalage du fuseau par +0800
rapport au temps universel
UTC
Autre %. sauter un caractère (autre :
qu’un chiffre)
%* sauter un nombre
quelconque de caractères
(autres qu’un chiffre)
# A tibble: 1 x 1
date
<date>
1 2015-01-02
# A tibble: 1 x 1
date
<date>
1 2015-02-01
# A tibble: 1 x 1
date
541
<date>
1 2001-02-15
Quel que soit le format original, les dates importées seront tou-
jours affichées par R au format ISO.
Ď Astuce
date_names_langs()
[1] "af" "agq" "ak" "am" "ar" "as" "asa" "az" "bas" "be" "bem" "bez"
[13] "bg" "bm" "bn" "bo" "br" "brx" "bs" "ca" "cgg" "chr" "cs" "cy"
[25] "da" "dav" "de" "dje" "dsb" "dua" "dyo" "dz" "ebu" "ee" "el" "en"
[37] "eo" "es" "et" "eu" "ewo" "fa" "ff" "fi" "fil" "fo" "fr" "fur"
[49] "fy" "ga" "gd" "gl" "gsw" "gu" "guz" "gv" "ha" "haw" "he" "hi"
[61] "hr" "hsb" "hu" "hy" "id" "ig" "ii" "is" "it" "ja" "jgo" "jmc"
[73] "ka" "kab" "kam" "kde" "kea" "khq" "ki" "kk" "kkj" "kl" "kln" "km"
[85] "kn" "ko" "kok" "ks" "ksb" "ksf" "ksh" "kw" "ky" "lag" "lb" "lg"
[97] "lkt" "ln" "lo" "lt" "lu" "luo" "luy" "lv" "mas" "mer" "mfe" "mg"
[109] "mgh" "mgo" "mk" "ml" "mn" "mr" "ms" "mt" "mua" "my" "naq" "nb"
[121] "nd" "ne" "nl" "nmg" "nn" "nnh" "nus" "nyn" "om" "or" "os" "pa"
[133] "pl" "ps" "pt" "qu" "rm" "rn" "ro" "rof" "ru" "rw" "rwk" "sah"
[145] "saq" "sbp" "se" "seh" "ses" "sg" "shi" "si" "sk" "sl" "smn" "sn"
[157] "so" "sq" "sr" "sv" "sw" "ta" "te" "teo" "th" "ti" "to" "tr"
[169] "twq" "tzm" "ug" "uk" "ur" "uz" "vai" "vi" "vun" "wae" "xog" "yav"
[181] "yi" "yo" "zgh" "zh" "zu"
date_names_lang("fr")
<date_names>
Days: dimanche (dim.), lundi (lun.), mardi (mar.), mercredi (mer.), jeudi
542
(jeu.), vendredi (ven.), samedi (sam.)
Months: janvier (janv.), février (févr.), mars (mars), avril (avr.), mai (mai),
juin (juin), juillet (juil.), août (août), septembre (sept.),
octobre (oct.), novembre (nov.), décembre (déc.)
AM/PM: AM/PM
date_names_lang("en")
<date_names>
Days: Sunday (Sun), Monday (Mon), Tuesday (Tue), Wednesday (Wed), Thursday
(Thu), Friday (Fri), Saturday (Sat)
Months: January (Jan), February (Feb), March (Mar), April (Apr), May (May),
June (Jun), July (Jul), August (Aug), September (Sep), October
(Oct), November (Nov), December (Dec)
AM/PM: AM/PM
read_csv(
csv,
col_types = cols(date = col_date("%d de %B de %Y")),
locale = locale("es")
)
# A tibble: 1 x 1
date
<date>
1 2001-02-03
543
et le jour apparaissent dans vos dates, puis placez “y”, “m” et
“d” dans le même ordre. Cela vous donne le nom de la fonction
{lubridate} qui analysera votre date. Par exemple :
ymd("2017-01-31")
[1] "2017-01-31"
[1] "2017-01-31"
dmy("31-Jan-2017")
[1] "2017-01-31"
ymd_hms("2017-01-31 20:11:59")
mdy_hm("01/31/2017 08:01")
544
flights |>
select(year, month, day, hour, minute) |>
head()
# A tibble: 6 x 5
year month day hour minute
<int> <int> <int> <dbl> <dbl>
1 2013 1 1 5 15
2 2013 1 1 5 29
3 2013 1 1 5 40
4 2013 1 1 5 45
5 2013 1 1 6 0
6 2013 1 1 5 58
flights |>
select(year, month, day, hour, minute) |>
mutate(
departure = make_datetime(year, month, day, hour, minute),
departure_date = make_date(year, month, day)
) |>
head()
# A tibble: 6 x 7
year month day hour minute departure departure_date
<int> <int> <int> <dbl> <dbl> <dttm> <date>
1 2013 1 1 5 15 2013-01-01 05:15:00 2013-01-01
2 2013 1 1 5 29 2013-01-01 05:29:00 2013-01-01
3 2013 1 1 5 40 2013-01-01 05:40:00 2013-01-01
4 2013 1 1 5 45 2013-01-01 05:45:00 2013-01-01
5 2013 1 1 6 0 2013-01-01 06:00:00 2013-01-01
6 2013 1 1 5 58 2013-01-01 05:58:00 2013-01-01
545
33.1.4 conversion
as_datetime(today())
as_date(now())
[1] "2023-08-15"
year(datetime)
[1] 2026
month(datetime)
[1] 7
546
mday(datetime)
[1] 8
yday(datetime)
[1] 189
wday(datetime)
[1] 4
[1] juil
12 Levels: janv < févr < mars < avr < mai < juin < juil < août < ... < déc
[1] mercredi
7 Levels: dimanche < lundi < mardi < mercredi < jeudi < ... < samedi
547
month(datetime, label = TRUE, abbr = FALSE, locale = "en")
[1] July
12 Levels: January < February < March < April < May < June < ... < December
[1] julio
12 Levels: enero < febrero < marzo < abril < mayo < junio < ... < diciembre
[1] Juli
12 Levels: Januar < Februar < März < April < Mai < Juni < Juli < ... < Dezember
33.2.2 Arrondis
d <- ymd("2022-05-14")
floor_date(d, unit = "week")
[1] "2022-05-08"
[1] "2022-05-01"
548
floor_date(d, unit = "3 months")
[1] "2022-04-01"
[1] "2022-01-01"
month(datetime) <- 01
datetime
549
update(datetime, year = 2030, month = 2, mday = 2, hour = 2)
[1] "2023-03-02"
550
diff <- ymd("2021-06-30") - ymd("1979-10-14")
diff
as.duration(diff)
dseconds(15)
[1] "15s"
dminutes(10)
dhours(c(12, 24))
ddays(0:5)
551
dweeks(3)
dyears(1)
2 * dyears(1)
552
one_am <- ymd_hms("2026-03-08 01:00:00", tz = "America/New_York")
one_am
one_am + ddays(1)
one_am
one_am + days(1)
553
Comme pour les durées, on peut créer facilement des périodes
avec des fonctions dédiées (notez ici le pluriel des noms de fonc-
tion, alors que celles permettant d’extraire un composant d’une
date étaient au singulier) :
hours(c(12, 24))
days(7)
months(1:6)
10 * (months(6) + days(1))
554
ymd("2024-01-01") + years(1)
[1] "2025-01-01"
one_am + days(1)
ymd("2021-01-31") + months(1)
[1] NA
[1] "2021-02-28"
555
33.3.3 Intervalles (Interval)
interval(ymd("2022-05-13"), ymd("2022-08-15"))
y2023
y2024
556
[1] TRUE
[1] TRUE
[1] FALSE
int_overlaps(int3, int)
[1] TRUE
intersect(int3, int)
int
557
int_start(int)
int_end(int)
int_flip(int)
[1] 31536000
[1] 52.14286
[1] 365
558
33.4 Calcul d’un âge
[1] 42.62466
[1] 42
559
age_atteint <- year(evt) - year(naiss)
age_atteint
[1] 43
Ď Astuce
560
raison en est que la base de données de l’IANA doit enregistrer
des dizaines d’années de règles relatives aux fuseaux horaires.
Au fil des décennies, les pays changent de nom (ou se séparent)
assez fréquemment, mais les noms de villes ont tendance à res-
ter inchangés. Un autre problème réside dans le fait que le nom
doit refléter non seulement le comportement actuel, mais aussi
l’ensemble de l’histoire. Par exemple, il existe des fuseaux ho-
raires pour "America/New_York" et "America/Detroit". Cela
vaut la peine de lire la base de données brute des fuseaux ho-
raires (disponible à l’adresse https://www.iana.org/time-zones)
rien que pour lire certaines de ces histoires !
Vous pouvez découvrir ce que R pense être votre fuseau horaire
actuel avec Sys.timezone() :
Sys.timezone()
[1] "Europe/Paris"
length(OlsonNames())
[1] 596
head(OlsonNames())
561
x2 <- ymd_hms("2024-06-01 18:00:00", tz = "Europe/Copenhagen")
x2
562
[1] "2024-06-02 02:30:00 +1030" "2024-06-02 02:30:00 +1030"
[3] "2024-06-02 02:30:00 +1030"
x4a - x4
x4b - x4
563
34 Réorganisation avec tidyr
564
partie VI
Analyses avancées
565
35 Modèles de comptage
(Poisson & apparentés)
566
femmes à l’âge de 30 ans.
library(tidyverse)
library(labelled)
data("fecondite", package = "questionr")
[1] masculin
[2] féminin
[0] non
[1] oui
567
un fichier Stata ou SPSS avec {haven}. Première étape, nous
allons convertir à la volée ces variables catégorielles en facteurs
avec labelled::unlabelled().
femmes <-
femmes |>
unlabelled()
enfants <-
enfants |>
unlabelled()
enfants <-
enfants |>
left_join(
femmes |>
select(id_femme, date_naissance_mere = date_naissance),
by = "id_femme"
) |>
mutate(
age_mere = time_length(
date_naissance_mere %--% date_naissance,
unit = "years"
)
)
femmes <-
femmes |>
left_join(
enfants |>
568
filter(age_mere < 30) |>
group_by(id_femme) |>
count(name = "enfants_avt_30"),
by = "id_femme"
) |>
tidyr::replace_na(list(enfants_avt_30 = 0L))
femmes <-
femmes |>
mutate(
age = time_length(
date_naissance %--% date_entretien,
unit = "years"
),
educ2 = educ |>
fct_recode(
"secondaire/supérieur" = "secondaire",
"secondaire/supérieur" = "supérieur"
)
)
Enfin, pour l’analyse, nous n’allons garder que les femmes âgées
d’au moins 30 ans au moment de l’enquête. En effet, les femmes
plus jeunes n’ayant pas encore atteint 30 ans, nous ne connais-
sons pas leur descendance atteinte à cet âge.
femmes30p <-
femmes |>
filter(age >= 30)
569
la fonction stats::glm() en précisant family = poisson.
Start: AIC=1013.81
enfants_avt_30 ~ educ2 + milieu + region
Df Deviance AIC
- region 3 686.46 1010.6
<none> 683.62 1013.8
- milieu 1 686.84 1015.0
- educ2 2 691.10 1017.3
Step: AIC=1010.65
enfants_avt_30 ~ educ2 + milieu
Df Deviance AIC
<none> 686.46 1010.6
- milieu 1 691.30 1013.5
- educ2 2 693.94 1014.1
570
car la régression de Poisson peut également être utilisée pour
des modèles d’incidence, qui seront abordés dans le prochain
chapitre (cf. Chapitre 36).
Pour un tableau mis en forme des coefficients, on aura
tout simplement recours à {gtsummary} et sa fonction
gtsummary::tbl_regression().
library(gtsummary)
theme_gtsummary_language("fr", decimal.mark = ",", big.mark = " ")
mod1_poisson |>
tbl_regression(exponentiate = TRUE) |>
bold_labels()
library(ggstats)
mod1_poisson |>
571
ggcoef_table(exponentiate = TRUE)
IRR95% CI p
Niveau d'éducation
aucun 1.0
Milieu de résidence
urbain 1.0
broom.helpers::plot_marginal_predictions(mod1_poisson) |>
patchwork::wrap_plots() &
ggplot2::scale_y_continuous(limits = c(0, .4))
572
0.4 0.4
0.3 0.3
0.2 0.2
0.1 0.1
0.0 0.0
aucun primaire
secondaire/supérieur urbain rural
Niveau d'éducation Milieu de résidence
573
df <- dplyr::tibble(
status = c(
rep.int("observed", length(observed)),
rep.int("theoretical", length(theoretical))
),
values = c(observed, theoretical)
)
if (is.numeric(observed) && any(observed != as.integer(observed))) {
ggplot2::ggplot(df) +
ggplot2::aes(x = values, fill = status) +
ggplot2::geom_density(
alpha = .5,
position = "identity"
) +
ggplot2::theme_minimal() +
ggplot2::labs(fill = NULL)
} else {
ggplot2::ggplot(df) +
ggplot2::aes(x = values, fill = status) +
ggplot2::geom_bar(
alpha = .5,
position = "identity"
) +
ggplot2::theme_minimal() +
ggplot2::labs(fill = NULL)
}
}
mod1_poisson |>
observed_vs_theoretical()
574
600
400
count
observed
theoretical
200
0
0 2 4
values
Ĺ Note
mod1_poisson |>
performance::check_predictions(type = "discrete_both")
575
Posterior Predictive Check
Model−predicted points should be close to observed data points
600
400
Counts
Observed data
Model−predicted data
200
0
0 1 2 3 4 5
enfants_avt_30
mod1_poisson$deviance / mod1_poisson$df.residual
[1] 0.8580717
576
La package {AER} propose un test, AER::dispersiontest(),
pour tester s’il y a un problème de surdispersion. Ce test ne
peut s’appliquer qu’à un modèle de Poisson.
Overdispersion test
data: mod1_poisson
z = 3.3367, p-value = 0.0004238
alternative hypothesis: true dispersion is greater than 1
sample estimates:
dispersion
1.361364
mod1_poisson |>
performance::check_overdispersion()
# Overdispersion test
Overdispersion detected.
Dans les deux cas, nous obtenons une p-valeur inférieure à 0,001,
indiquant que le modèle de Poisson n’est peut-être pas appro-
prié ici.
577
variance qui est alors modélisée comme une relation linéaire de
la moyenne. Il se calcule également avec stats::glm(), mais
en indiquant family = quasipoisson. Comme avec le modèle
de Poisson, la fonction de lien par défaut est la fonction loga-
rithmique (log).
ĺ Important
mod1_quasi |>
tbl_regression(exponentiate = TRUE) |>
bold_labels()
578
Caractéristique IRR 95% IC p-valeur
urbain — —
rural 1,42 0,99 – 2,10 0,067
Niveau d'éducation
aucun
primaire
secondaire/supérieur
Milieu de résidence
urbain
rural
Poisson quasi−Poisson
579
mod1_quasi |>
performance::check_overdispersion()
# Overdispersion test
Overdispersion detected.
Start: AIC=979.1
enfants_avt_30 ~ educ2 + milieu + region
Df Deviance AIC
- region 3 462.89 975.01
580
<none> 460.98 979.10
- milieu 1 463.29 979.41
- educ2 2 466.11 980.22
Step: AIC=975
enfants_avt_30 ~ educ2 + milieu
Df Deviance AIC
<none> 460.14 975.00
- milieu 1 463.37 976.24
- educ2 2 465.54 976.40
mod1_nb |>
tbl_regression(exponentiate = TRUE) |>
bold_labels()
581
list(
Poisson = mod1_poisson,
"quasi-Poisson" = mod1_quasi,
"Binomial négatif" = mod1_nb
) |>
ggcoef_compare(exponentiate = TRUE)
Niveau d'éducation
aucun
primaire
secondaire/supérieur
Milieu de résidence
urbain
rural
mod1_nb |>
observed_vs_theoretical()
mod1_nb |>
performance::check_predictions(type = "discrete_both")
582
600
400
count
observed
theoretical
200
0
0 2 4
values
600
Counts
400
Observed data
Model−predicted data
200
0
0 1 2 3 4 5 6 7
enfants_avt_30
583
mod1_nb |>
performance::check_overdispersion()
# Overdispersion test
No overdispersion detected.
performance::compare_performance(
mod1_poisson,
mod1_nb,
metrics = "common"
)
584
Préparons les données en francisant les facteurs et en ajoutant
des étiquettes de variable.
mod2_poisson |>
observed_vs_theoretical()
585
15
count
10 observed
theoretical
0
0 20 40 60 80
values
mod2_poisson |>
performance::check_overdispersion()
# Overdispersion test
Overdispersion detected.
586
data = d
)
mod2_nb |>
observed_vs_theoretical()
15
count
10 observed
theoretical
0
0 20 40 60 80
values
mod2_nb |>
performance::check_overdispersion()
# Overdispersion test
No overdispersion detected.
587
Voilà !
Pour finir, visualisons les coefficients du modèle.
mod2_nb |>
ggcoef_table(exponentiate = TRUE)
IRR95% CI p
Sexe de l'enfant
fille 1.0
Vitesse d'apprentissage
dans la moyenne 1.0
0.8 0.91.0
IRR
588
data(hdv2003, package = "questionr")
d <-
hdv2003 |>
mutate(
groupe_ages = age |>
cut(
c(18, 25, 45, 65, 99),
right = FALSE,
include.lowest = TRUE,
labels = c("18-24 ans", "25-44 ans",
"45-64 ans", "65 ans et plus")
)
) |>
set_variable_labels(
sport = "Pratique un sport ?",
sexe = "Sexe",
groupe_ages = "Groupe d'âges",
heures.tv = "Heures de télévision / jour"
)
levels(d$sexe)
589
Nous allons maintenant calculer un modèle de Poisson. Nous
devons déjà ré-exprimer notre variable à expliquer sous la forme
d’une variable numérique égale à 0 si l’on ne pratique pas de
sport et à 1 si l’on pratique un sport.
levels(d$sport)
performance::check_overdispersion(mod3_poisson)
# Overdispersion test
No overdispersion detected.
mod3_binomial |>
ggstats::ggcoef_table(exponentiate = TRUE)
590
OR95% CI p
Sexe Homme 1.61.3, 1.9
<0.001
Femme 1.0
mod3_poisson |>
ggstats::ggcoef_table(exponentiate = TRUE)
IRR95% CI p
Sexe Homme 1.31.1, 1.5
<0.001
Femme 1.0
Nous pouvons voir ici que les deux modèles fournissent des résul-
591
tats assez proches. Par contre, les coefficients ne s’interprètent
pas de la même manière. Dans le cadre de la régression lo-
gistique, il s’agit d’odds ratios (ou rapports des côtes) définis
𝑝𝐴 𝑝𝐵
comme 𝑂𝑅𝐴/𝐵 = ( 1−𝑝 )/( 1−𝑝 ) où 𝑝𝐴 correspond à la proba-
𝐴 𝐵
bilité de faire du sport pour la modalité 𝐴. Pour la régression
de Poisson, il s’agit de prevalence ratios (rapports des préva-
lences) définis comme 𝑃 𝑅𝐴/𝐵 = 𝑝𝐴 /𝑝𝐵 . Avec un rapport des
prévalences de 1,3, nous pouvons donc dire que, selon le modèle,
les hommes ont 30% de chance en plus de pratiquer un sport.
Pour mieux comparer les deux modèles, nous pouvons présenter
les résultats sous la forme de contrastes marginaux moyens (cf.
Section 23.4) qui, pour rappel, sont exprimés dans l’échelle de
la variable d’intérêt, soit ici sous la forme d’une différence de
probabilité.
list(
"régression logistique" = mod3_binomial,
"régression de Poisson" = mod3_poisson
) |>
ggcoef_compare(tidy_fun = broom.helpers::tidy_marginal_contrasts) +
scale_x_continuous(labels = scales::percent)
Sexe
Femme − Homme
Groupe d'âges
25−44 ans − 18−24 ans
592
Les résultats sont ici très proches. Nous pouvons néanmoins
constater que les intervalles de confiance pour la régression
de Poisson sont un peu plus large. Nous pouvons comparer
les deux modèles avec performance::compare_performance()
pour constater que, dans notre exemple, la régression de Pois-
son est un peu moins bien ajustée aux données que la régression
logistique binaire. Cependant, en pratique, cela n’est pas ici pro-
blématique : le choix entre les deux modèles peut donc se faire
en fonction de la manière dont on souhaite présenter et narrer
les résultats.
performance::compare_performance(
mod3_binomial,
mod3_poisson,
metrics = "common"
)
Ď Astuce
593
Il faut noter que ce type de modèles a parfois du mal à
converger.
Error: impossible de trouver un jeu de coefficients correct : prière de fournir des valeurs i
mod3_log |>
ggstats::ggcoef_table(exponentiate = TRUE)
594
RR95% CI p
Sexe Homme 1.31.2, 1.4
<0.001
Femme 1.0
mod3_logbin |>
ggstats::ggcoef_table(exponentiate = TRUE)
Warning: The `tidy()` method for objects of class `logbin` is not maintained by the broom tea
595
RR95% CI p
Sexe Homme 1.31.2, 1.4
<0.001
Femme 1.0
library(srvyr)
library(survey)
dp <- d |>
as_survey_design(weights = poids)
mod4_poisson <- svyglm(
sport2 ~ sexe + groupe_ages + heures.tv,
family = poisson,
design = dp
)
mod4_quasi <- svyglm(
sport2 ~ sexe + groupe_ages + heures.tv,
1
Sur ce sujet, on pourra consulter l’article Log-binomial models: explo-
ring failed convergence par Tyler Williamson, Misha Eliasziw et Gor-
don Hilton Fick, DOI: 10.1186/1742-7622-10-14. On pourra également
consulter cet échange sur StackExchange.
596
family = quasipoisson,
design = dp
)
Ĺ Note
597
dp_rep <- dp |>
as_survey_rep(type = "bootstrap", replicates = 100)
mod4_nb_alt <- svrepmisc::svynb(
sport2 ~ sexe + groupe_ages + heures.tv,
design = dp_rep
)
598
36 Modèles d’incidence / de
taux
599
retombons sur un modèle de comptage classique, à condition
d’ajouter à chaque observation ce qu’on appelle un décalage
(offset en anglais) de 𝑙𝑜𝑔(𝑑𝑒𝑥𝑝 ). Ce décalage correspond donc
en quelque sorte à une variable ajoutée au modèle mais pour
laquelle on ne calcule pas de coefficient.
600
)
mod1_poisson_alt <- glm(
death ~ stage + trt + response,
offset = log(ttdeath),
family = poisson,
data = gtsummary::trial
)
mod1_poisson |>
performance::check_overdispersion()
# Overdispersion test
No overdispersion detected.
mod1_poisson |>
ggstats::ggcoef_table(exponentiate = TRUE)
601
IRR 95% CI p
T Stage T1 1.0
602
d <- MASS::Insurance
d$Age <- factor(d$Age, ordered = FALSE)
d$Group <- factor(d$Group, ordered = FALSE)
mod2_poisson <- glm(
Claims ~ Age + Group + offset(log(Holders)),
family = poisson,
data = d
)
mod2_poisson |>
performance::check_overdispersion()
# Overdispersion test
No overdispersion detected.
mod2_poisson |>
ggstats::ggcoef_table(exponentiate = TRUE)
603
IRR 95% CI p
Age <25 1.0
604
puisque, pour les femmes de plus de 25 ans à l’enquête, la
durée d’exposition entre 15 et 25 ans exacts est de 10 ans.
library(tidyverse)
library(labelled)
data("fecondite", package = "questionr")
femmes <-
femmes |>
unlabelled() |>
mutate(
age = time_length(
date_naissance %--% date_entretien,
unit = "years"
),
exposition = if_else(age <= 25, age - 15, 10),
educ2 = educ |>
fct_recode(
"secondaire/supérieur" = "secondaire",
"secondaire/supérieur" = "supérieur"
)
) |>
# exclure celles qui viennent juste d'avoir 15 ans
filter(exposition > 0)
enfants <-
enfants |>
unlabelled() |>
left_join(
femmes |>
select(id_femme, date_naissance_mere = date_naissance),
by = "id_femme"
) |>
mutate(
age_mere = time_length(
date_naissance_mere %--% date_naissance,
unit = "years"
)
605
)
femmes <-
femmes |>
left_join(
enfants |>
filter(age_mere >= 15 & age_mere < 25) |>
group_by(id_femme) |>
count(name = "enfants_15_24"),
by = "id_femme"
) |>
tidyr::replace_na(list(enfants_15_24 = 0L))
Vérifions la surdispersion.
mod3_poisson |>
performance::check_overdispersion()
# Overdispersion test
Overdispersion detected.
606
)
mod3_nb |>
performance::check_overdispersion()
# Overdispersion test
No overdispersion detected.
mod3_nb |>
ggstats::ggcoef_table(exponentiate = TRUE)
IRR95% CI p
Niveau d'éducation
aucun 1.0
Milieu de résidence
urbain 1.0
607
Poisson) avec R par Claire Della Vedova
• Zoom sur la Regression de Poisson et l’Incidence Risque
Ratio (IRR) : exemple du vaccin anti-SarsCov2 d’Oxford
par Ihsane Hmamouchi
608
37 Modèles de comptage
zero-inflated et hurdle
library(labelled)
library(tidyverse)
data("DebTrivedi", package = "MixAll")
d <- DebTrivedi |>
mutate(
609
gender = gender |>
fct_recode("femme" = "female", "homme" = "male"),
privins = privins |>
fct_recode("non" = "no", "oui" = "yes"),
health = health |>
fct_recode(
"pauvre" = "poor",
"moyenne" = "average",
"excellente" = "excellent"
)
) |>
set_variable_labels(
ofp = "Nombre de visites médicales",
gender = "Genre de l'assuré",
privins = "Dispose d'une assurance privée ?",
health = "Santé perçue",
numchron = "Nombre de conditions chroniques"
)
contrasts(d$health) <- contr.treatment(3, base = 2)
# Overdispersion test
610
p-value = < 0.001
Overdispersion detected.
# Overdispersion test
Overdispersion detected.
611
Posterior Predictive Check
Model−predicted intervals should include observed data points
600
Counts
400
Observed data
Model−predicted data
200
0
0 5 10 15 20
ofp
612
Calculons un premier modèle de Poisson zero-inflated.
mod_zip |>
ggstats::ggcoef_multicomponents(
type = "table",
exponentiate = TRUE,
intercept = TRUE
)
613
conditional
exp(Beta) 95% CI p
(Intercept) 4.8 4.7, 5.0 <0.001
zero_inflated
exp(Beta) 95% CI p
(Intercept) 0.6 0.5, 0.7 <0.001
614
modèles de Poisson zero-inflated simplifiés où seul un intercept
est utilisé pour la composante logistique binaire.
mod_zip_simple |>
ggstats::ggcoef_multicomponents(
type = "table",
tidy_fun = broom.helpers::tidy_zeroinfl,
exponentiate = TRUE,
intercept = TRUE,
component_label = c(
conditional = "Modèle de Poisson",
zero_inflated = "Modèle logistique binaire"
)
) +
patchwork::plot_layout(heights = c(6, 1))
615
Modèle de Poisson
exp(Beta) 95% CI p
(Intercept) 4.8 4.6, 5.0 <0.001
performance::compare_performance(
mod_poisson,
mod_zip_simple,
mod_zip,
616
mod_nb,
mod_zinb,
metrics = "AIC"
)
library(gtsummary)
tbl_nb <- mod_nb |>
tbl_regression(exponentiate = TRUE)
tbl_zinb <- mod_zinb |>
tbl_regression(
tidy_fun = broom.helpers::tidy_zeroinfl,
component = "conditional",
exponentiate = TRUE
)
list(tbl_nb, tbl_zinb) |>
tbl_merge(c("**NB**", "**ZI-NB**")) |>
bold_labels()
617
Table 37.1: Coefficients du modèle négatif binomial et de la
composante comptage du modèle négatif binomial
zero-inflated
95% p- 95% p-
Characteristic IRR CI value exp(Beta)
CI value
Genre de
l’assuré
femme — — — —
homme 0.90 0.84, <0.001 0.93 0.88, 0.031
0.95 0.99
Dispose
d’une
assurance
privée ?
non — — — —
oui 1.39 1.29, <0.001 1.23 1.14, <0.001
1.50 1.33
Santé perçue
pauvre 1.39 1.27, <0.001 1.38 1.26, <0.001
1.53 1.51
moyenne — — — —
excellente 0.71 0.63, <0.001 0.71 0.63, <0.001
0.80 0.81
Nombre de 1.21 1.19, <0.001 1.16 1.13, <0.001
conditions 1.25 1.19
chroniques
618
Si l’objectif de l’analyse est avant d’identifier les facteurs asso-
ciés avec le nombre moyen d’évènement, on pourra éventuelle-
ment se contenter d’un modèle zero-inflated simple, c’est-à-dire
avec seulement un intercept pour la composante zero-inflated
afin de corriger la sur-représentation des zéros dans nos don-
nées.
Alternativement, on pourra se tourner vers un modèle avec saut
qui distingue les valeurs nulles des valeurs positives : les modèles
hurdle en anglais.
619
parce qu’ils n’ont pas manifesté de tels comportements à risque
au cours de la période étudiée. La probabilité d’appartenir à
l’une ou l’autre population est estimée à l’aide d’une compo-
sante de probabilité à inflation nulle, tandis que les effectifs
de la seconde population du groupe d’utilisateurs sont modé-
lisés par une distribution de comptage ordinaire, telle qu’une
distribution de Poisson ou binomiale négative.
En revanche, un modèle hurdle suppose que toutes les données
nulles proviennent d’une source “structurelle”, une partie du
modèle étant un modèle binaire pour modéliser si la variable
de réponse est nulle ou positive, et une autre partie utilisant
un modèle tronqué, pour les données positives. Par exemple,
dans les études sur l’utilisation des soins de santé, la partie
zéro implique la décision de rechercher des soins, et la compo-
sante positive détermine la fréquence de l’utilisation au sein du
groupe de l’utilisateur.
Une autre différence importante entre les modèles hurdle et
zero-inflated est leur capacité à gérer la déflation zéro (moins
de zéros que prévu par le processus de génération des données).
Les modèles zero-inflated ne sont pas en mesure de gérer la dé-
flation zéro, quel que soit le niveau d’un facteur, et donneront
des estimations de paramètres de l’ordre de l’infini pour la com-
posante logistique, alors que les modèles hurdle peuvent gérer
la déflation zéro (Min et Agresti 2005).
Min, Yongyi, et Alan Agresti. 2005.
Les modèles hurdle peuvent être calculés avec la fonc- « Random Effect Models for Repea-
ted Measures of Zero-Inflated Count
tion pscl::hurdle() dont la syntaxe est similaire à Data ». Statistical Modelling 5 (1):
pscl::zeroinfl(). 1‑19. https://doi.org/10.1191/1471
082X05st084oa.
mod_hurdle_poisson <- pscl::hurdle(
ofp ~ gender + privins + health + numchron,
data = d
)
mod_hurdle_nb <- pscl::hurdle(
ofp ~ gender + privins + health + numchron,
dist = "negbin",
data = d
)
620
mod_hurdle_nb |>
ggstats::ggcoef_multicomponents(
type = "table",
tidy_fun = broom.helpers::tidy_zeroinfl,
exponentiate = TRUE,
component_label = c(
conditional = "Facteurs associés au nombre d'évènements",
zero_inflated = "Facteurs associés au fait d'avoir vécu l'évènement"
)
)
moyenne 1.0
moyenne 1.0
1 2 3
exp(Beta)
621
• Si l’on a vécu l’évènement au moins une fois, quels sont
les facteurs associés à la fréquence de l’évènement ?
622
• A comparison of zero-inflated and hurdle models for mo-
deling zero-inflated count data par Cindy Xin Feng. DOI :
10.1186/s40488-021-00121-4
• Zero-inflated models for adjusting varying exposures: a
cautionary note on the pitfalls of using offset by Cindy
Xin Feng. DOI : 10.1080/02664763.2020.1796943
623
38 Quel modèle choisir ?
624
partie VII
625
39 Ressources documentaires
626
• R for Non-Programmers: A Guide for Social Scientists
par Daniel Dauber
627