Chapter 3 Analyse simple en R base
Dans ce chapitre, nous allons analyser une base de données simple en utilisant les commandes de R base.
Les objectifs de ce chapitre sont de :
- préparer un dossier (et un environnement) de travail pour réaliser les analyses
- importer des données, créer des variables, modifier une variable déjà existante
- réaliser des analyses descriptives univariées des variables quantitatives et qualitatives
- réaliser des analyses descriptives bivariées
- faire des représentations graphiques des données
- savoir faire les tests statistiques de comparaison de moyennes et de pourcentages
- savoir faire une modèle de régression linéaire multivariée, et vérifier ses conditions d’application
- sauvegarder les données et les résultats
Note : Nous verrons dans les chapitre suivants comment réaliser la même analyse en utilisant :
- des packages spécifiques qui facilitent la réalisation et la présentation des analyses.
- la collection de packages du
tidyversequi apportent de nouvelles fonctions avec une nouvelle philosophie, une nouvelle grammaire et de nouvelles structures de données, qui est très utilisé dans la communauté des utilisateurs de R.
3.1 Préparer un dossier de travail
3.1.1 Projet R
1ère étape : Créer un dossier de travail. Commencez par créer un dossier vide, appelé analyse R base dans l’endroit de votre choix sur votre ordinateur. Au sein de ce dossier, vous pouvez ajouter un dossier data et un dossier figures qui pourra vous servir à stocker les données et les figures que vous souhaitez souvegarder.
2ème étape : Créer un projet R. Dans R Studio, cliquez sur le cube en 3 dimensions R “Project: (None)” en haut à droite, au dessus du cadran environnement.
- puis “New Project…”.
- choisissez “Existing Directory…”. Avec le bouton browse, vous pouvez alors rechercher le dossier analyse R base que vous venez de créer à la 1ère étape.
- enfin cliquez sur “Open” et “Create Project”.

R Studio va alors créer un fichier analyse R base.Rproj au sein de votre dossier analyse R base. De plus, ce dossier analyse R base sera défini comme votre dossier de travail au sein de R Studio.
Ainsi si vous demandez à R quel est votre dossier de travail actuel avec la commande getwd() (get working directory), la console va indiquer le chemin jusque ce dossier (R vous confirme qu’il s’agit bien de votre dossier de travail !).
Le contenu de ce dossier de travail apparaît également dans le menu “Files” dans le cadrant en bas à droite. Ce dossier sert de “dossier racine” (“répertoire racine”). Les fichiers au sein de ce dossier pourront être désignés avec un chemin raccourci. Tous les éléments que vous allez sauvegarder pendant votre session R Studio seront sauvegardés dans ce dossier de travail +++
Si vous ouvrez un nouveau script R et que vous le sauvegardez (par exemple, sous le nom 1_analyse en R base), il sera ajouté par défaut au sein de votre dossier de travail, et va apparaître parmi les fichiers du menu file (cadrant en bas à droite).
Maintenant, quittez le programme R Studio, puis double cliquez sur le fichier analyse R base.Rproj (icone d’un cube R en 3 dimensions) qui vient d’être créé dans votre dossier de travail “analyse R base”.
R Studio va alors s’ouvrir, en ayant directement défini le dossier analyse R base comme dossier de travail. C’est la méthode la plus simple pour reprendre une analyse R en cours et pour reproduire les résultats déjà obtenus.
3.1.2 Structurer un script d’analyse
Lorsque vous réaliserez une analyse, vous allez sauvegarder vos commandes dans un script (par exemple, dans le script 1_analyse en R base.R que l’on vient de sauvegarder précédemment).
En haut du script, si vous souhaitez recommencer à partir d’un environnement vide lorsque vous relancerez l’analyse depuis le départ, vous pouvez ajouter la commande rm(ls = list()).
Une bonne pratique est d’indiquer quelques informations utiles pour documenter votre script, comme votre nom et la date des analyses (en commentaires). Enfin, à la fin d’un script (à la fin de vos analyses), vous pouvez lancer la commande sessionInfo() qui va lister dans la console la version de R et les versions des packages qui ont été utilisés pour votre analyse (vous pourrez copier ces informations à la fin de votre script, en commentaires).
Les commentaires peuvent également servir à indiquer des titres et sous-titres dans votre script d’analyse. Un menu d’accès rapide est alors accessible à partir du bouton Outline en haut à droite de la fenêtre du script R.
Note : sur windows, le raccourci “ctrl-shift-c” transforme les lignes sélectionnées en commentaires (en ajoutant automatiquement des # en début de ligne)
Voici un exemple de script qui suit cette structure :
### 1_analyse en R base
### Date : 15 septembre 2025
### Auteur : Timoté Chalamais
rm(list = ls()) # cette commande vide le contenu de l'environnement
# ---------------------------------------------------------------------------- #
# 1) Titre n°1 (il faut ajouter 4 tirets à la fin des titres) ----
## 1.2) Sous-titre (en ajoutant un # au début de ligne) ----
### 1.2.1) Sous-sous-titre (en ajoutant des # supplémentaires) ----
# ---------------------------------------------------------------------------- #
# Import des données
df_1 <- read.csv2("data/df_1.csv")
meta_df_1 <- read.csv2("data/meta_df_1.csv")
# ---------------------------------------------------------------------------- #
# 2) Titre n°2 ----
# ---------------------------------------------------------------------------- #
head(df_1)
(...) # continuez vous analyses
(...)
# ---------------------------------------------------------------------------- #
# Fin de script ----
# à la fin, vous pouvez récupérer les informations sur la session
# pour les copier-coller en bas du script (en format de commentaires)
sessionInfo()
# R version 4.5.1 (2025-06-13 ucrt)
# Platform: x86_64-w64-mingw32/x64
# Running under: Windows 11 x64 (build 26100)
#
# Matrix products: default
# LAPACK version 3.12.1
#
# locale:
# [1] LC_COLLATE=French_France.utf8
# [2] LC_CTYPE=French_France.utf8
# [3] LC_MONETARY=French_France.utf8
# [4] LC_NUMERIC=C
# [5] LC_TIME=French_France.utf8
#
# time zone: Europe/Paris
# tzcode source: internal
#
# attached base packages:
# [1] stats graphics grDevices utils datasets
# [6] methods base
#
# loaded via a namespace (and not attached):
# [1] compiler_4.5.1 bookdown_0.43
# [3] fastmap_1.2.0 cli_3.6.5
# [5] htmltools_0.5.8.1 tools_4.5.1
# [7] rstudioapi_0.17.1 yaml_2.3.10
# [9] rmarkdown_2.29 knitr_1.50
# [11] xfun_0.52 digest_0.6.37
# [13] rlang_1.1.6 evaluate_1.0.4 3.2 Importer une base de données
Pour cet exemple,
- téléchargez la base
df_1.csvainsi que la base de méta-donnéesmeta_df_1.csv(cliquez sur le bouton download raw file, à droite avec une flèche vers le bas). - Vous pouvez ensuite coller ces deux bases de données au sein du dossier data de votre dossier de travail.
Il s’agit de fichiers avec l’extension “.csv” (comma separated variables) où les valeurs sont séparées par un point virgule. Vous pouvez les importer dans R avec la fonction read.csv2() (il existe également une fonction read.csv() qui importe les données .csv où le séparateur est une virgule). Vous pouvez regarder les différents arguments de ces fonctions dans l’aide ?read.csv2.
Nous allons stocker la base dans des objets nommés df_1 et meta_df_1. Ces bases importées sont des objets de type data.frame.
df_1 <- read.csv2("data/df_1.csv")
meta_df_1 <- read.csv2("data/meta_df_1.csv")
class(df_1) # [1] "data.frame"- La base
df_1contient les données que nous allons analyser. - La base
meta_df_1contient des méta-données (noms de variables, noms des labels, etc) qui sera utile pour préciser les noms des variables, les labels des variables qualitatives, etc.
3.3 Examiner les données et les méta-données
Comme vu précédemment, vous pouvez examiner les données avec les fonctions head(), tail(), str() et View().
## subjid sex imc trait pas
## 1 1 0 24.8 2 140
## 2 2 0 24.1 3 109
## 3 3 0 26.4 1 156
## 4 4 0 23.3 2 124
## 5 5 0 25.4 2 131
## 6 6 1 25.0 3 148
## subjid sex imc trait pas
## 295 295 1 24.1 1 148
## 296 296 0 18.7 3 121
## 297 297 0 23.3 3 111
## 298 298 1 27.5 3 134
## 299 299 1 24.7 2 158
## 300 300 1 22.8 1 147
## 'data.frame': 300 obs. of 5 variables:
## $ subjid: int 1 2 3 4 5 6 7 8 9 10 ...
## $ sex : int 0 0 0 0 0 1 0 0 0 0 ...
## $ imc : num 24.8 24.1 26.4 23.3 25.4 25 25.2 21.5 21.8 25.9 ...
## $ trait : int 2 3 1 2 2 3 3 3 1 1 ...
## $ pas : int 140 109 156 124 131 148 125 117 132 133 ...
On voit que la base de données contient 300 observations et 5 variables :
subjid, l’identifiant patientsex, le sexe du patientimc, l’indice de masse corporelle du patient (en kg/m2)trait, le traitement 0 = placebo, 1 = traitement A, 2 = traitement Bpas, la pression artérielle systolique (en mmHg)
Toutes les variables dans df_1 sont de type entier (int) ou réel (num).
## var label id_labs code_labs labs
## 1 subjid Identifiant patient 1 NA
## 2 sex Sexe 1 0 Féminin
## 3 sex Sexe 2 1 Masculin
## 4 imc IMC (kg/m²) 1 NA
## 5 trait Traitement 1 1 Placebo
## 6 trait Traitement 2 2 Traitement A
## 7 trait Traitement 3 3 Traitement B
## 8 pas PAS (mmHg) 1 NA
La base de méta-données contient 5 variables :
var, nom des variables que l’on retrouve dans la base de donnéesdf_1label, nom en détail de la variable
id_labs, un identifiant pour les labels éventuels, par variablecode_labs, codage numérique du label (code utilisé dans la basedf_1)labs, modalités de réponses des variables qualitatives, en détail
Vous pouvez également trier les données selon les valeurs d’une ou plusieurs variables avec la fonction sort_by() :
### Nous allons trier les données pour nous aider à les inspecter :
### en triant sur le sexe (1ère variable de tri), et l'imc (2ème variable de tri)
### pour voir les première lignes de la base de données triée :
head(sort_by(x = df_1, # x = la base à trier
y = c(df_1$sex, df_1$imc))) # y = variables de tri, dans l'ordre ## subjid sex imc trait pas
## 1 1 0 24.8 2 140
## 2 2 0 24.1 3 109
## 3 3 0 26.4 1 156
## 4 4 0 23.3 2 124
## 5 5 0 25.4 2 131
## 7 7 0 25.2 3 125
### pour voir les dernières lignes de la base de donnée triée :
tail(sort_by(x = df_1,
y = ~ sex + imc), # demandé suivant le format de "formula"
n = 10) # nombre de lignes à présenter dans les fonctions head() et tail()## subjid sex imc trait pas
## 258 258 1 29.2 1 142
## 173 173 1 29.5 1 129
## 145 145 1 29.9 2 121
## 92 92 1 30.1 3 167
## 224 224 1 30.2 1 147
## 132 132 1 30.3 2 141
## 144 144 1 30.5 3 158
## 274 274 1 30.5 1 143
## 64 64 1 31.2 1 152
## 223 223 1 31.2 1 172
3.4 Créer ou modifier une variable
3.4.1 Créer des variables
Nous allons créer une variable obesite dont la valeur est égale à 1 si l’indice de masse corporelle est \(\geq 30\text{ kg/m}^2\) et égale à 0 sinon.
Pour cela, on peut utilise la fonction ifelse() dont le premier argument est une condition, le 2ème argument est la valeur à retourner si la condition est vraie, et le 3ème argument est la valeur à retourner si la condition est fausse.
Chaque fois que vous créez ou modifiez une variable, il est utile de vérifier que vous n’avez pas fait d’erreurs. Dans cet exemple, on peut vérifier quelles sont les valeurs minimales et maximales de l’indice de masse corporelle au sein des deux catégories de la nouvelle variable :
### vérifier que la variable est correctement crée :
min(df_1$imc[df_1$obesite == 0]) # [1] 15.4
max(df_1$imc[df_1$obesite == 0]) # [1] 29.9
### les valeurs d'IMC varient de 15.4 à 29.9 lorsque obesite == 0
min(df_1$imc[df_1$obesite == 1]) # [1] 30.1
max(df_1$imc[df_1$obesite == 1]) # [1] 32.5
### les valeurs d'IMC varient de 30.1 à 32.5 lorsque obesite == 1 => c'est bon !Nous allons ensuite créer une variable d’IMC en classes (imc_cl) définie telle que :
imc_cl = 1(maigreur) si l’IMC < 18.5 kg/m\(^2\),imc_cl = 2(normal) si l’IMC \(\geq 18.5 \text{ kg/m}^2\) et IMC \(< 25 \text{ kg/m}^2\),imc_cl = 3(normal) si l’IMC \(\geq 25 \text{ kg/m}^2\) et IMC \(< 30 \text{ kg/m}^2\),imc_cl = 4(normal) si l’IMC \(\geq 30 \text{ kg/m}^2\).
Il y a plusieurs possibilités pour créer cette variable, voici 3 méthodes différentes :
### 1) créer une variable où toutes les données sont manquantes :
df_1$imc_cl <- rep(NA, nrow(df_1))
### puis remplacer les données manquantes par les valeurs souhaitées
df_1$imc_cl[df_1$imc < 18.5] <- 1
df_1$imc_cl[df_1$imc >= 18.5 & df_1$imc < 25] <- 2
df_1$imc_cl[df_1$imc >= 25 & df_1$imc < 30] <- 3
df_1$imc_cl[df_1$imc >= 30] <- 4
### 2) avec la fonction ifelse de manière itérative :
df_1$imc_cl <- ifelse(df_1$imc < 18.5, 1,
ifelse(df_1$imc >= 18.5 & df_1$imc < 25,
2, ifelse(df_1$imc >= 25 & df_1$imc < 30, 3, 4)))
### 3) avec une formule intégrant des conditions.
### Comme on multiplie les résultats de conditions à des entiers, les
### réponses TRUE et FALSE sont transformées en 1 et 0 par coercition
df_1$imc_cl <- (1 * (df_1$imc < 18.5) +
2 * (df_1$imc >= 18.5 & df_1$imc < 25) +
3 * (df_1$imc >= 25 & df_1$imc < 30) +
4 * (df_1$imc >= 30))Pensez à vérifier que vous n’avez pas fait d’erreur en créant cette variable. Par exemple, on peut vérifier la distribution de l’IMC au sein des 4 classes à l’aide d’un box plot.

Après avoir créé ces deux nouvelles variables, nous pouvons compléter la base de méta-données pour définir les noms de variables et de labels en clair. Pour cela, nous allons ajouter des lignes supplémentaires avec la fonction rbind() (row bind pour “fusion par rang”). A noter qu’il existe aussi une fonction cbind() qui permet de fusionner des bases ou des matrices par colonnes
meta_df_1 <- rbind(meta_df_1,
data.frame(var = c(rep("obesite", 2),
rep("imc_cl", 4)),
label = c(rep("Obésité", 2),
rep("IMC en classes", 4)),
id_labs = c(1:2, 1:4),
code_labs = c(0:1, 1:4),
labs = c("Non", "Oui",
"Maigreur", "Normal", "Surpoids", "Obèse"))
)
meta_df_1## var label id_labs code_labs labs
## 1 subjid Identifiant patient 1 NA
## 2 sex Sexe 1 0 Féminin
## 3 sex Sexe 2 1 Masculin
## 4 imc IMC (kg/m²) 1 NA
## 5 trait Traitement 1 1 Placebo
## 6 trait Traitement 2 2 Traitement A
## 7 trait Traitement 3 3 Traitement B
## 8 pas PAS (mmHg) 1 NA
## 9 obesite Obésité 1 0 Non
## 10 obesite Obésité 2 1 Oui
## 11 imc_cl IMC en classes 1 1 Maigreur
## 12 imc_cl IMC en classes 2 2 Normal
## 13 imc_cl IMC en classes 3 3 Surpoids
## 14 imc_cl IMC en classes 4 4 Obèse
3.4.2 Modifier des variables
Enfin, nous souhaitons modifier la valeur de la pression artérielle du patient n°137 : suite à une erreur de saisie, nous nous sommes rendu compte que sa valeur n’est pas 133 mmHg, mais 123 mmHg.
## [1] 133
## nous pouvons directement assigner la nouvelle valeur avec l'indexation par
## condition :
df_1$pas[df_1$subjid == 137] <- 123
df_1[df_1$subjid %in% 135:140,] ## subjid sex imc trait pas obesite imc_cl
## 135 135 0 28.0 2 154 0 3
## 136 136 1 26.2 1 138 0 3
## 137 137 0 20.1 1 123 0 2
## 138 138 0 24.7 3 155 0 2
## 139 139 0 17.0 2 115 0 1
## 140 140 1 25.7 1 160 0 3
3.4.3 Sauvegarder la base de données
Pour sauvegarder la base de données avec les variables que vous venez de créer et de modifier, vous pouvez utiliser les fonctions :
write.csv2()sauvegarde une base au format “.csv” avec un point-virgule comme séparateur (comme pourread, il existe une fonctionwrite.csv2()qui utilise une virgule comme séparateur). Le format .csv peut être lu par tous les logiciels statistiques, les gestionnaires de base de données (commme Access, LibreOffice Base), les tableurs (comme Excel, LibreOffice Calc, …) ou tout éditeur de texte (bloc-note, TextEdit, etc).saveRDS()permet de sauvegarder tout objet R. Il sera sauvegardé avec l’extension “.R”. Vous pourrez seulement l’importer dans le logiciel R avec la commandereadRDS(). L’intérêt de ce format est que l’objet R est sauvegardé avec tous ses attributs R.
### Sauvegarder la base df_1 dans le dossier data, au format .csv,
### pensez à lui donnant un nouveau nom "df_1_new.csv" pour ne pas écraser
### l'ancienne base "df_1.csv"
write.csv2(df_1, "data/df_1_new.csv")
### Sauvegarder la base df_1 dans le dossier data, au format .R,
### pensez à lui donnant un nouveau nom "df_1_new.R" pour ne pas écraser
### l'ancienne base "df_1.csv"
saveRDS(df_1, "data/df_1_new.R")
### pour importer à nouveau ces objets dans R :
df_1_new <- read.csv2("data/df_1_new.csv")
df_1_new_bis <- readRDS("data/df_1_new.R")
# vous retrouvez alors la base dans l'environnement avec leur nouveau nom :
# df_1_new et df_1_new_bis3.5 Analyses univariées
3.5.1 fonction summary()
Nous pouvons utiliser la fonction summary() pour obtenir de manière synthétique une description univariée de l’ensemble des variables, indiquant : les valeurs minimales et maximales, quartiles et médiane, et la valeur moyenne.
## subjid sex imc trait
## Min. : 1.00 Min. :0.00 Min. :15.40 Min. :1.000
## 1st Qu.: 75.75 1st Qu.:0.00 1st Qu.:22.30 1st Qu.:1.000
## Median :150.50 Median :0.00 Median :24.60 Median :2.000
## Mean :150.50 Mean :0.49 Mean :24.48 Mean :1.897
## 3rd Qu.:225.25 3rd Qu.:1.00 3rd Qu.:26.40 3rd Qu.:3.000
## Max. :300.00 Max. :1.00 Max. :32.50 Max. :3.000
## pas obesite imc_cl
## Min. : 92.0 Min. :0.00000 Min. :1.000
## 1st Qu.:125.0 1st Qu.:0.00000 1st Qu.:2.000
## Median :138.0 Median :0.00000 Median :2.000
## Mean :137.1 Mean :0.04333 Mean :2.453
## 3rd Qu.:149.0 3rd Qu.:0.00000 3rd Qu.:3.000
## Max. :177.0 Max. :1.00000 Max. :4.000
Les variables de la base df_1 sont toutes codées en valeurs numériques, la fonction summary a donc décrit les variables qualitatives comme s’il s’agissait de variables quantitatives : ce n’est pas adapté.
Nous allons créer 2 nouvelles variables en factor à partir des variables qualitatives. Pour cela, nous allons également nous servir des informations indiquées dans la base de méta-données. Puis nous allons relancer la fonction summary() :
### création de 2 variables de type "factor" à partir des variables sex et trait
df_1$sexL <- factor(df_1$sex,
labels = meta_df_1$labs[meta_df_1$var == "sex"])
df_1$traitL <- factor(df_1$trait,
labels = meta_df_1$labs[meta_df_1$var == "trait"])
summary(df_1)## subjid sex imc trait
## Min. : 1.00 Min. :0.00 Min. :15.40 Min. :1.000
## 1st Qu.: 75.75 1st Qu.:0.00 1st Qu.:22.30 1st Qu.:1.000
## Median :150.50 Median :0.00 Median :24.60 Median :2.000
## Mean :150.50 Mean :0.49 Mean :24.48 Mean :1.897
## 3rd Qu.:225.25 3rd Qu.:1.00 3rd Qu.:26.40 3rd Qu.:3.000
## Max. :300.00 Max. :1.00 Max. :32.50 Max. :3.000
## pas obesite imc_cl sexL
## Min. : 92.0 Min. :0.00000 Min. :1.000 Féminin :153
## 1st Qu.:125.0 1st Qu.:0.00000 1st Qu.:2.000 Masculin:147
## Median :138.0 Median :0.00000 Median :2.000
## Mean :137.1 Mean :0.04333 Mean :2.453
## 3rd Qu.:149.0 3rd Qu.:0.00000 3rd Qu.:3.000
## Max. :177.0 Max. :1.00000 Max. :4.000
## traitL
## Placebo :120
## Traitement A: 91
## Traitement B: 89
##
##
##
Les nouvelles variables sexL et traitL sont à présent décrites par dénombrement du nombre d’individus par modalité de réponse, de manière adaptée aux variables qualitatives.
3.5.2 Variables quantitatives
Pour décrire des variables quantitatives, on peut s’intéresser aux paramètres suivants :
- effectifs observés (non-manquants), avec les fonctions
length()qui indique la longueur d’un vecteur, ounrow()qui indique le nombre de lignes d’une base de données (ou d’une matrice) - moyenne, avec la fonction
mean() - écart type et variance, avec les fonctions
sd()etvar() - minimum, 1er quartiles, médiane, 3ème quartile, maximum, avec les fonctions
min(),max(),median(),quantiles()
### Effectifs observés :
### Comme il n'y a pas de manquant dans cette base, on peut directement utiliser
### la fonction length() ou nrow() pour connaître les effectifs
nrow(df_1) # 300
length(df_1$imc) # 300
### Pour compter les effectifs non-manquants, de manière explicite :
length(which(!is.na(df_1miss$imc))) # 300
### moyennes
mean(df_1$imc, na.rm = TRUE) # 24.481
mean(df_1$pas, na.rm = TRUE) # 137.1133
### déviation standard (écart-type)
sd(df_1$imc, na.rm = TRUE) # 3.069072
sd(df_1$pas, na.rm = TRUE) # 16.82053
### variances
var(df_1$imc, na.rm = TRUE) # 9.419203
var(df_1$pas, na.rm = TRUE) # 282.9303
### quantiles
min(df_1$imc, na.rm = TRUE) # 15.4
min(df_1$pas, na.rm = TRUE) # 92
max(df_1$imc, na.rm = TRUE) # 32.5
max(df_1$pas, na.rm = TRUE) # 177
median(df_1$imc, na.rm = TRUE) # 24.6
median(df_1$pas, na.rm = TRUE) # 138
quantile(df_1$imc, probs = c(0, 0.25, 0.5, 0.75, 1), na.rm = TRUE)
# 0% 25% 50% 75% 100%
# 15.4 22.3 24.6 26.4 32.5
quantile(df_1$pas, probs = c(0, 0.25, 0.5, 0.75, 1), na.rm = TRUE)
# 0% 25% 50% 75% 100%
# 92 125 138 149 1773.5.3 Programmation élémentaire
3.5.3.1 Créer une nouvelle fonction
Il est fastidieux de devoir récupérer les différents paramètres de distribution de chaque variable, un par un !
Mais R permet facilement de programmer de nouvelles fonctions “maison” à l’aide de function(arguments) { expression }, où on indique une liste d’arguments entre parenthèses, puis une liste de commandes à réaliser entre les accolades.
Par exemple, on peut créer une nouvelle fonction exemple_fonction() qui ajoute +2 à l’objet x :
## [1] 3 4 5 6 7
Pour accélérer notre analyse des variables quantitatives, nous allons créer une nouvelle fonction qui va calculer d’un coup l’ensemble des paramètres qui nous intéressent. Cette fonction sera nommée univ_quanti, elle va dépendre de 3 arguments :
x, la variable à décriredig, le nombre de chiffres après la virgule pour présenter des valeurs arrondiesremove_miss, une valeur logique qui sera utilisée pour exclure (ou non) les données manquantes du calcul (pour renseigner l’argumentna.rmdes fonctions descriptives de base)
Entre accolades, on va demander à la fonction de :
- calculer les effectifs, la moyenne, l’écart type et les quantiles, de la variable
x - regrouper ces valeurs au sein d’un vecteur de réels, arrondis à
digchiffres après la virgule, - retourner le vecteur obtenu
### Définir une nouvelle fonction dans R
univ_quanti <- function(x, # la variable à décrire
dig = 2, # par défaut, 2 chiffres après la virgule
remove_miss = TRUE # par défaut, la valeur est TRUE
) { # fermez la parenthèse et ouvrez l'accolade
# on commence par calculer les différents paramètres et on les stocke dans
# les objets : "n", "moy", "sd" et "q"
n <- length(x[!is.na(x)])
moy <- mean(x, na.rm = remove_miss)
sd <- sd(x, na.rm = remove_miss)
q <- quantile(x, probs = c(0, 0.25, 0.5, 0.75, 1), na.rm = remove_miss)
# on stocke les résultats dans un vecteur de réels, nommé "param",
# en gardant uniquement la valeur arrondie pour la moyenne et l'écart-type
param <- c(n,
round(moy, digits = dig),
round(sd, digits = dig),
q)
# on peut ajouter un nom à chaque élément du vecteur "param"
names(param) <- c("N", "mean", "sd", "min", "Q1", "median", "Q3", "max")
# indiquer ce que doit retourner la fonction
return(param)
} # fermez l'accoladeSi on applique cette fonction à nos deux variables quantitatives, on obtient l’ensemble des paramètres présentés dans un vecteur :
## N mean sd min Q1 median Q3 max
## 300.0 24.5 3.1 15.4 22.3 24.6 26.4 32.5
## N mean sd min Q1 median Q3 max
## 300.0 137.1 16.8 92.0 125.0 138.0 149.0 177.0
Note : Vous pouvez remarquer qu’en utilisant cette fonction, les objets n, moy, sd, q et param n’apparaissent pas dans l’environnement de travail, ils ont uniquement été créés de manière temporaire au sein de la fonction.
3.5.3.2 Utiliser des boucles
Les boucles for dans R permettent également d’automatiser des opérations en boucle. La syntaxe d’une boucle est :
for (variable in sequence) { expession }où l’expressionentre accolade est répétée chaque fois que lavariableest égale à une valeur de lasequence
### exemple de boucle :
### - pour chaque valeur i variant de 1 à 5,
### => calcule 20 + i
### => ajoute cette valeur à la fin de la phrase "calcul de 20 + i = "
### => imprime le résultat à l'écran
for (i in 1:5) {
print(paste0("calcul de 20 + i = ", 20 + i))
}## [1] "calcul de 20 + i = 21"
## [1] "calcul de 20 + i = 22"
## [1] "calcul de 20 + i = 23"
## [1] "calcul de 20 + i = 24"
## [1] "calcul de 20 + i = 25"
On peut appliquer cette démarche pour répéter l’analyse univariée avec notre nouvelle fonction univ_quanti() à nos deux variables quantitatives. Les deux variables quantitatives sont dans les colonnes 3 et 5 de la base de données.
### Avec la fonction names(), on voit que les variables quantitatives imc et pas
### sont la 3ème et la 5ème variable de la base df_1
names(df_1)## [1] "subjid" "sex" "imc" "trait" "pas" "obesite" "imc_cl"
## [8] "sexL" "traitL"
for (i in c(3, 5)) {
print(names(df_1)[i]) # imprime le nom de la i-ème variable
# puis imprime les résultats de la fonction univ_quanti appliquée à la i-ème
# variable de la base df_1
print(univ_quanti(df_1[[i]], dig = 1, remove_miss = TRUE))
}## [1] "imc"
## N mean sd min Q1 median Q3 max
## 300.0 24.5 3.1 15.4 22.3 24.6 26.4 32.5
## [1] "pas"
## N mean sd min Q1 median Q3 max
## 300.0 137.1 16.8 92.0 125.0 138.0 149.0 177.0
Question bonus : que se passe t’il si vous n’indiquez pas la fonction print() ?
- réponse : il a fait tourner la fonction, mais ne l’a pas imprimé les résultats à l’écran
3.5.3.3 Fonctions apply(), lapply(), sapply()
On peut également lancer une fonction de manière répétée appliquée :
- à des colonnes ou des lignes de matrices (ou de data frame) avec la fonction
apply() - à des vecteurs ou des listes avec
lapply()(qui retourne les résultats sous forme de liste) ousapply()(qui retournent les résultats sous forme “simplifiée” de matrice ou de vecteur)
Exemple 1 : La fonction apply() permet d’appliquer notre fonction de description des paramètres aux colonnes "imc" et "pas" de la la base df_1 (Le résultat sera une matrice de réels) :
apply(df_1[,c("imc", "pas")], # matrice ou data.frame sélectionnée
MARGIN = 2, # 2 = par colonne ; 1 = par ligne
FUN = univ_quanti, # fonction à utiliser
dig = 1, # on peut ajouter les arguments de la fonction à la suite
remove_miss = TRUE) ## imc pas
## N 300.0 300.0
## mean 24.5 137.1
## sd 3.1 16.8
## min 15.4 92.0
## Q1 22.3 125.0
## median 24.6 138.0
## Q3 26.4 149.0
## max 32.5 177.0
Exemple 2 : La fonction lapply() applique la fonction à une liste de vecteurs et retourne une liste de la même longueur. La fonction sapply() fait la même chose, mais retourne les résultats sous forme de vecteur ou de matrice.
lapply(X = df_1[,c("imc", "pas")],
FUN = univ_quanti, # fonction à utiliser
dig = 1,
remove_miss = TRUE)## $imc
## N mean sd min Q1 median Q3 max
## 300.0 24.5 3.1 15.4 22.3 24.6 26.4 32.5
##
## $pas
## N mean sd min Q1 median Q3 max
## 300.0 137.1 16.8 92.0 125.0 138.0 149.0 177.0
sapply(X = df_1[,c("imc", "pas")],
FUN = univ_quanti, # fonction à utiliser
dig = 1,
remove_miss = TRUE) ## imc pas
## N 300.0 300.0
## mean 24.5 137.1
## sd 3.1 16.8
## min 15.4 92.0
## Q1 22.3 125.0
## median 24.6 138.0
## Q3 26.4 149.0
## max 32.5 177.0
Si on veut remplacer les noms de colonnes par les noms de variables en clair, on peut utiliser la base de méta-données :
res_quanti <- sapply(df_1[,c("imc", "pas")],
FUN = univ_quanti,
dig = 1)
# on modifie le nom des colonne de la matrice de résultats
# avec les informations disponibles dans les méta-données
colnames(res_quanti) <- c(meta_df_1$label[meta_df_1$var == "imc"],
meta_df_1$label[meta_df_1$var == "pas"])
res_quanti## IMC (kg/m²) PAS (mmHg)
## N 300.0 300.0
## mean 24.5 137.1
## sd 3.1 16.8
## min 15.4 92.0
## Q1 22.3 125.0
## median 24.6 138.0
## Q3 26.4 149.0
## max 32.5 177.0
3.5.4 Variables qualitatives
Pour décrire les variables qualitatives,
- la fonction
table()permet de décrire les effectifs dans chaque modalité de réponse, - la fonction
prop.table()permet de décrire les pourcentages des données obtenues avec la fonctiontable(),
On va utiliser les variables en “facteur” sexL et traitL dont les modalités de réponse sont labellisées.
##
## Féminin Masculin
## 153 147
##
## Féminin Masculin
## 0.51 0.49
### On peut combiner ces deux vecteurs avec cbind() (combinaison par colonne)
### pour les afficher dans une matrice
tab_sex <- cbind(table(df_1$sexL),
round(prop.table(table(df_1$sexL)) * 100, digits = 1))
colnames(tab_sex) <- c("n", "pct")
tab_sex## n pct
## Féminin 153 51
## Masculin 147 49
### De même pour la variable traitement
tab_trait <- cbind(table(df_1$traitL),
round(prop.table(table(df_1$traitL)) * 100, digits = 1))
colnames(tab_trait) <- c("n", "pct")
tab_trait## n pct
## Placebo 120 40.0
## Traitement A 91 30.3
## Traitement B 89 29.7
3.6 Représentations graphiques
Il est toujours utile de faire des représentations graphiques de la distribution des variables : cela permet d’avoir une bonne vision de l’ensemble des données et de détecter des anomalies éventuelles.
3.6.1 Distributions univariées
La distribution des variables quantitatives peut être représentée à l’aide d’un histogramme hist(), d’une densité de kernel density() ou d’un box plot boxplot().
Note : On peut modifier les paramètres des graphiques avec la fonction par. Par exemple mfrow() permet de combiner des figures par lignes et par colonnes.
### Variables quantitatives : imc et pas
### Histogrammes
hist(df_1$imc, xlab = "IMC (kg/m²)", main = "Histogramme de l'IMC")

### On va afficher deux figures sur une 1 ligne et deux colonnes
par(mfrow = c(1, 2)) # indiquer c(X, Y) ou X = nb de lignes et Y = nb de colonnes
### Densités de kernel
plot(density(df_1$imc), xlab = "IMC (kg/m²)", main = "Fonction de densité - IMC")
plot(density(df_1$pas), xlab = "PAS (mmHg)", main = "Fonction de densité - PAS")
### Box plots
boxplot(df_1$imc, main = "Boxplot de l'IMC", ylab = "IMC (kg/m²)")
boxplot(df_1$pas, main = "Boxplot de la PAS", ylab = "PAS (mmHg)")
Les distributions des variables qualitatives peuvent être représentées par des diagrammes en barres barplot().
### Diagrammes en barres, appliqués aux "facteurs"
par(mfrow = c(1, 2)) # 2 figures sur une seule ligne
barplot(table(df_1$sexL), # appliquer la fonction à une table()
ylab = "N",
main = "Diagramme en barres du traitement") # avec les effectifs
barplot(prop.table(table(df_1$sexL)),
ylab = "Frequency",
main = "Diagramme en barres du traitement") # avec les pourcentages
3.6.2 Distributions bivariées
On peut croiser une variable quantitative en fonction des modalités d’une variable qualitative à l’aide de box-plots.
Une bonne pratique est d’indiquer clairement le noms des axes, les titres et légendes. Pour cela, nous pouvons utiliser la base de méta-données.
### Le signe "tilde" ~ est souvent utilisé pour définir une variable comme une
### fonction d'une ou plusieurs autres variables :
### y = f(x1, x2, x3) s'écrit y ~ x1 + x2 + x3
### PAS en fonction du sex.
boxplot(df_1$pas ~ df_1$sexL,
ylab = "PAS (mmHg)",
xlab = meta_df_1$label[meta_df_1$var == "sex" & meta_df_1$id_labs == 1],
main = "Boxplot de la PAS")
### PAS en fonction du traitement
boxplot(df_1$pas ~ df_1$traitL,
ylab = "PAS (mmHg)",
xlab = meta_df_1$label[meta_df_1$var == "trait" & meta_df_1$id_labs == 1],
main = "Boxplot de la PAS")
Un nuage de points peut être utilisé pour représenter le croisement de deux variables quantitatives.
R offre beaucoup de possibilités pour modifier les paramètres graphiques. L’aide ?par indique l’ensemble des options possibles. Il difficile d’y voir clair dans cette multitude d’options, les paramètres les plus utiles sont notamment :
col: pour spécifier la couleur des points et des lignes. Ce paramètre se décline à la couleur des axescol.axis, des labelscol.lab, des titrescol.main, des sous-titrescol.sub, …pch: pour définir le symbole des points dans les nuages de points (0 pour un carré, 1 pour un rond, 2 pour un triange, etc). Cf. le détail de l’aide de?points()lty: pour spécifier le type de ligne : 0 = blank, 1 = solid, 2 = dashed, etc. On peut également l’indiquer en caractère “blank”, “solid”, “dashed”, …. cflwd: pour spécifier la largeur d’une lignecex: valeur numérique indiquant la taille relative de la police de caractères. Elle se décline pour la police utilisée sur les axescex.axis, les labelscex.lab, les titrescex.main, les sous-titrescex.sub, etc. Ce paramètre va également influencer la taille des points dans un nuage de pointsmfrometmfcolpour combiner plusieurs graphiques sur une ou plusieurs lignes et une ou plusieurs colonnesmar: indique le nombre de ligne par marge, indiqué dans cet ordrec(bottom, left, top, right). La valeur par défaut estc(5, 4, 4, 2) + 0.1.mai: permet également de préciser la taille des marges (en pouces), dans le même ordrec(bottom, left, top, right)legend: permet d’ajouter une légende- etc.
La fonction exemple permet d’avoir des exemples d’utilisation paramètres graphiques : example(mar), example(line), example(axis), example(legend)
### On peut représenter un nuage de points de la PAS en fonction de l'IMC :
### où les hommes et les femmes ont deux symboles différents et les traitements
### deux couleurs différentes
par(mfrow = c(1, 1)) # pour revenir à un seul graphique par sortie
plot.new()
par(mar = c(5,4,4,2) + 0.1) # paramètre des marges par défaut
plot.window(xlim = c(15, 35), # range(df_1$imc)
ylim = c(80, 180)) # range(df_1$pas)
grid() # ajoute une grille
### ajoute le nuage de points des femmes, groupe placebo
points(data = subset(df_1, subset = c(sex == "Féminin" & trait == "Placebo")),
pas ~ imc,
col = "indianred3", # placebo en rouge
pch = 4) # femme avec une croix
# ajoute le nuage de points des femmes, groupe traitement A
points(data = subset(df_1, subset = c(sex == 0 & trait == 2)),
pas ~ imc,
col = "dodgerblue3", # traitement A en bleu
pch = 4) # femme avec une croix
# ajoute le nuage de points des femmes, groupe traitement B
points(data = subset(df_1, subset = c(sex == 0 & trait == 3)),
pas ~ imc,
col = "palegreen3", # traitement B en vert
pch = 4) # femme avec une croix
# ajoute le nuage de points des hommes, groupe placebo
points(data = subset(df_1, subset = c(sex == 1 & trait == 1)),
pas ~ imc,
col = "indianred3", # placebo en rouge
pch = 1) # hommes avec un rond
# ajoute le nuage de points des hommes, groupe traitement A
points(data = subset(df_1, subset = c(sex == 1 & trait == 2)),
pas ~ imc,
col = "dodgerblue3", # traitement A en bleu
pch = 1) # hommes avec un rond
# ajoute le nuage de points des hommes, groupe traitement B
points(data = subset(df_1, subset = c(sex == 1 & trait == 3)),
pas ~ imc,
col = "palegreen3", # traitement B en vert
pch = 1) # hommes avec un rond
axis(1, # axe du bas
lwd = 1, # largeur de la ligne
font.axis=1) # taille de la police de caractère
axis(2, # axe à gauche
lwd = 1, # largeur de la ligne
font.axis=1) # taille de la police de caractère
title(xlab = "IMC (kg/m²)")
title(ylab = "PAS (mmHg)")
title(main = "Nuage de points de la PAS en fonction de l'IMC")
legend("bottomright",
c("Femme, P", "Femme, A", "Femme, B",
"Homme, P", "Homme, A", "Homme, B"),
pch = c(4,4,4,1,1,1),
col = c("indianred3", "dodgerblue3", "palegreen3",
"indianred3", "dodgerblue3", "palegreen3"),
ncol = 1,
cex = 0.7)
Pour trouver des informations détaillées sur l’utilisation des fonction graphiques de R base, vous pouvez lire le tutoriel détaillé de Karolis Koncevičius.
3.7 Analyses bivariées
Normalement, vous devriez savoir choisir quel test de comparaison utiliser en fonction des variables à comparer.

3.7.1 Variable quantitative \(\times\) qualitative
Pour décrire une variable quantitative en fonction d’une variable qualitative, on peut utiliser les fonctions aggregate() et tapply().
La fonction aggregate() permet d’appliquer des fonctions (mean(), sd(), etc) à des sous-groupes de variables définies en facteurs.
### Description de la moyenne de l'IMC et de la PAS en fonction du facteur sex
aggregate(x = df_1[,c("imc", "pas")], # variable(s) quantitative(s)
by = list(df_1$sexL), # en fonction du "factor" sexL
FUN = mean) # fonction à appliquer à la variable x## Group.1 imc pas
## 1 Féminin 24.09281 132.4248
## 2 Masculin 24.88503 141.9932
Pour décrire de manière synthétique, la distribution, on veut récupérer l’effectif, la moyenne et l’écart-type par groupe. Nous allons modifier la fonction “maison” univ_quanti() que nous avions défini précédemment en ajoutant un argument details :
- si
details = TRUE, alors la fonction retourne l’ensemble des résultats détaillés (effectifs, moyenne, écart-type, minimum, 1er quartile, médiane, 3ème quartile et maximum) - si
details = FALSE, alors la fonction retourne uniquement les effectifs, la moyenne et l’écart-type.
Pour cela nous allons compléter la fonction avec la construction if(condition) {expression1} else {expression2} : l’expression1 est appliquée si la condition est vraie, sinon l’expression2 est appliquée.
univ_quanti <- function(x, dig = 2, remove_miss = TRUE, details = TRUE) {
n <- length(x[!is.na(x)])
moy <- mean(x, na.rm = remove_miss)
sd <- sd(x, na.rm = remove_miss)
q <- quantile(x, probs = c(0, 0.25, 0.5, 0.75, 1), na.rm = remove_miss)
# construction if (condition) {expression1} else {expression2}
if (details == TRUE) { # si la condition entre parenthèse est vraie ...
param <- c(n, # ... appliquer les fonctions entre accolades
round(moy, digits = dig),
round(sd, digits = dig),
q)
} else { # sinon appliquer les fonctions entre la 2ème accolade
param <- c(n,
round(moy, digits = dig),
round(sd, digits = dig))
}
# ajouter un nom à chaque élément du vecteur
if (details == TRUE) {
names(param) <- c("N", "mean", "sd", "min", "Q1", "median", "Q3", "max")
} else {
names(param) <- c("N", "mean", "sd")
}
# retourne les résultats
return(param)
}
### Description de la PAS en fonction du sexe (facteur sexL)
aggregate(x = df_1$pas,
by = list(df_1$sexL),
FUN = univ_quanti, # fonction à utiliser
dig = 1, remove_miss = TRUE, details = FALSE)## Group.1 x.N x.mean x.sd
## 1 Féminin 153.0 132.4 16.8
## 2 Masculin 147.0 142.0 15.5
### Description de la PAS en fonction du traitement (facteur traitL)
aggregate(x = df_1$pas,
by = list(df_1$traitL),
FUN = univ_quanti, # fonction à utiliser
dig = 1, remove_miss = TRUE, details = FALSE)## Group.1 x.N x.mean x.sd
## 1 Placebo 120.0 141.4 15.6
## 2 Traitement A 91.0 130.7 16.4
## 3 Traitement B 89.0 137.8 17.0
La fonction tapply() permet également d’appliquer une fonction selon les sous-groupes d’un facteur (indiqué en argument INDEX). Les résultats obtenus sont au format de liste.
### Description de l'IMC en fonction du sexe (facteur sexL)
tapply(X = df_1$imc,
INDEX = list(df_1$sexL), # facteur à indiquer sous forme de liste.
FUN = univ_quanti, # fonction à utiliser sur la variable X
dig = 1, remove_miss = TRUE, details = FALSE)## $Féminin
## N mean sd
## 153.0 24.1 3.1
##
## $Masculin
## N mean sd
## 147.0 24.9 3.0
### Description de l'IMC en fonction du sexe (facteur sexL)
tapply(X = df_1$imc,
INDEX = list(df_1$traitL), # facteur à indiquer sous forme de liste.
FUN = univ_quanti, # fonction à utiliser sur la variable X
dig = 1, remove_miss = TRUE, details = FALSE)## $Placebo
## N mean sd
## 120.0 24.3 2.9
##
## $`Traitement A`
## N mean sd
## 91.0 24.8 3.0
##
## $`Traitement B`
## N mean sd
## 89.0 24.3 3.3
3.7.2 Variable qualitative \(\times\) qualitative
On peut décrire une variable qualitative en fonction d’une autre variable qualitative avec les fonctions table() et prop.table(table()).
### Décrire les effectifs de la variable sexL (en lignes),
### en fonction de la variable traitL (en colonnes) avec la fonction table()
### on va stocker les résultats dans un objet "sex_by_trait_N"
sex_by_trait_N <- table(df_1$sexL, df_1$traitL)
sex_by_trait_N##
## Placebo Traitement A Traitement B
## Féminin 57 46 50
## Masculin 63 45 39
### Les pourcentages peuvent être décrits avec la fonction prop.table()
### il faut préciser l'argument margin = 1 pour des pourcentages en ligne,
### ou margin = 2 pour des pourcentages en colonnes
### ou margin = NULL pour des pourcentages par cellule.
### on va stocker les pourcentage dans un objet "sex_by_trait_pct"
sex_by_trait_pct <- prop.table(sex_by_trait_N,
margin = 2) # % par colonne
sex_by_trait_pct##
## Placebo Traitement A Traitement B
## Féminin 0.4750000 0.5054945 0.5617978
## Masculin 0.5250000 0.4945055 0.4382022
### On peut combiner ces deux résultats avec la fonction paste0() qui combine
### chaque élément de 2 vecteurs
tab_biv_quali <- paste0(sex_by_trait_N,
"(", round(sex_by_trait_pct * 100, digits = 1), "%)")
tab_biv_quali## [1] "57(47.5%)" "63(52.5%)" "46(50.5%)" "45(49.5%)" "50(56.2%)" "39(43.8%)"
### c'est devenu un vecteur atomique de caractères
### on va lui redonner les dimensions et noms des matrices initiales
### en lui redonnant les attribut dim() et dimnames() des tables initiales
dim(tab_biv_quali) <- dim(sex_by_trait_N)
dimnames(tab_biv_quali) <- dimnames(sex_by_trait_N)
tab_biv_quali##
## Placebo Traitement A Traitement B
## Féminin "57(47.5%)" "46(50.5%)" "50(56.2%)"
## Masculin "63(52.5%)" "45(49.5%)" "39(43.8%)"
3.7.3 Comparer 2 moyennes
On peut comparer deux moyennes avec le test de Student t.test().
### Comparaion de la moyenne de PAS en fonction du sexe (2 moyennes)
### par un test de Student
ttest_pas_sex <- t.test(data = df_1, # préciser quelle est la base de donnée
pas ~ sexL, # moyenne de la PAS en fonction du sexe
var.equal = TRUE)
## Attention, par défaut, l'argument var.equal = FALSE
# il considère que l'hypothèse d'égalité des variances est fausse et applique
# un test de Welch qui est robuste, même en cas de variances inégales)
## Pour réaliser le test de Student, il faut indiquer : var.equal = TRUE
ttest_pas_sex##
## Two Sample t-test
##
## data: pas by sexL
## t = -5.1297, df = 298, p-value = 5.239e-07
## alternative hypothesis: true difference in means between group Féminin and group Masculin is not equal to 0
## 95 percent confidence interval:
## -13.239193 -5.897528
## sample estimates:
## mean in group Féminin mean in group Masculin
## 132.4248 141.9932
## Les résultats indiquent que l'on peut rejeter l'hypothèse nulle d'égalité des
## moyennes entre les hommes et les femmes,
## de manière statistiquement significative, avec une p-value = 5.2e-07
## On peut récupérer des éléments spécifiques de la liste de résultats avec
## l'opérateur dollar appliqué à l'objet où on stocke les résultats :
ttest_pas_sex$p.value # pour récupérer uniquement la p-value## [1] 5.238798e-07
## [1] -13.239193 -5.897528
## attr(,"conf.level")
## [1] 0.95
Les conditions d’application du test de Student sont :
- l’égalité des variances
- et la normalité de la distribution dans chaque groupe (surtout si les effectifs sont faibles). Dans notre exemple avec un effectif de 300, les résultats seront robuste même en cas d’écart à la normalité.
Pour vérifier l’égalité des variance, on peut utiliser le test de Levene leveneTest() qui est disponible dans le package car qui n’est pas chargé en mémoire. Pour pouvoir utiliser le test de Levene, il faut :
- soit commencer par charger le package avec
library(car), puis lancer la fonctionleveneTest() - soit utiliser la syntaxe
car::leveneTest()qui permet de lancer uniquement la fonctionleveneTest()sans charger la totalité du packagecar.
# note : la variable en classe (sexe) doit être de type "factor"
car::leveneTest(data = df_1, pas ~ sexL) ## Levene's Test for Homogeneity of Variance (center = median)
## Df F value Pr(>F)
## group 1 1.0178 0.3139
## 298
La p-value est égale à 0.31 : on ne rejette pas l’hypothèse nulle d’égalité des variances (par abus d’interprétation, on va considérer que la condition d’égalité des variances est vraie).
Pour vérifier la normalité de la distribution de la PAS par sexe, on peut tracer la densité de kernel dans chaque sous-groupe. Voici un exemple de programmation (qui ajoute une courbe normale en pointillés rouge pour servir de référence) :
par(mfrow = c(1, 2)) # les 2 figures seront présentées sur 1 ligne
for (i in 1:2) {
vect <- df_1$pas[as.integer(df_1$sexL) == i]
plot(density(vect), # densité de kernel
main = attributes(df_1$sexL)$levels[i])
lines(x = seq(min(vect), max(vect), length(50)), # loi normale de référence
y = dnorm(seq(min(vect), max(vect), length(50)),
mean = mean(vect),
sd = sd(vect)),
col = "red", lty = "dashed")
}
Ici, les distributions semblent assez proches de lois normales.
Si les conditions d’application du test de Student ne sont pas vérifiées, on peut utiliser le test des rangs de Wilcoxon-Mann-Whitney wilcox.test() ).
##
## Wilcoxon rank sum test with continuity correction
##
## data: pas by sex
## W = 7609.5, p-value = 1.29e-06
## alternative hypothesis: true location shift is not equal to 0
Le test de Wilcoxon rejette l’hypothèse nulle d’égalité des moyennes de PAS entre les hommes et les femmes (de manière significative, avec une p-value = 1.5e-06).
3.7.4 Comparer 3 moyennes ou plus
On peut comparer 3 moyennes ou plus avec une Anova anova(). Par exemple, si on compare la moyenne de PAS en fonction du traitement, l’hypothèse nulle est : “il n’existe pas de différence de moyenne de PAS entre les 3 groupes de traitement”.
Dans R, la fonction anova() s’applique au résultat d’un modèle linéaire. Nous allons donc d’abord estimer le modèle linéaire de la pression artérielle systolique en fonction du traitement avec la fonction lm() (pour linear model). Nous pourrons également utiliser les résidus de ce modèle pour vérifier la condition de normalité. La condition d’égalité des variances pourra être vérifiée comme précédemment avec le test de Levenne.
mod_pas_trait <- lm(pas ~ traitL, # modèle de PAS en fonction du traitement
data = df_1)
# le fait d'utiliser un "factor" (traitL) comme variable explicative
# va permettre à R de créer automatiquement deux indicatrices pour modéliser
# le traitement à 3 catégories, en prenant la première catégorie ("placebo")
# comme référence
summary(mod_pas_trait)##
## Call:
## lm(formula = pas ~ traitL, data = df_1)
##
## Residuals:
## Min 1Q Median 3Q Max
## -38.747 -11.747 0.157 11.340 39.157
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 141.400 1.486 95.182 < 2e-16 ***
## traitLTraitement A -10.653 2.262 -4.709 3.82e-06 ***
## traitLTraitement B -3.557 2.277 -1.563 0.119
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 16.27 on 297 degrees of freedom
## Multiple R-squared: 0.07022, Adjusted R-squared: 0.06396
## F-statistic: 11.22 on 2 and 297 DF, p-value: 2.016e-05
Le modèle linéaire indique que :
- la PAS est en moyenne de 141.4 mmHg dans le groupe de référence (placebo) avec l’intercept.
- la PAS est plus faible de -10.7 mmHg en moyenne dans le groupe “traitement A” par rapport au placebo (de manière significative, p = 3.8e-06)
- la PAS est plus faible de -3.6 mmHg en moyenne dans le groupe “traitement B” par rapport au placebo (de manière non significative, p = 0.12)
## On applique ensuite ce modèle linéaire à la fonction anova
## qui va retourner une table d'Anova avec les différentes sources de
## variabilité, degrés de liberté, sommes des carrés, carrés moyens,
## statistique F du test de Fisher et p-value.
anova_pas_trait <- anova(mod_pas_trait)
anova_pas_trait## Analysis of Variance Table
##
## Response: pas
## Df Sum Sq Mean Sq F value Pr(>F)
## traitL 2 5940 2970.18 11.215 2.016e-05 ***
## Residuals 297 78656 264.83
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## on peut récupérer des résultats spécifiques avec l'opérateur dollar
anova_pas_trait$`Pr(>F)` # récupère uniquement la p-value## [1] 2.015778e-05 NA
La p-value est très petite (\(p = 2.10^{-5}\)), on rejette l’hypothèse nulle d’égalité des moyennes de PAS entre les 3 groupes de traitements.
On peut vérifier la condition d’application d’égalité des variances avec un test de Levenne :
## Levene's Test for Homogeneity of Variance (center = median)
## Df F value Pr(>F)
## group 2 0.6669 0.514
## 297
On ne rejette pas l’hypothèse nulle d’égalité des variances, car la p-value n’est pas significative (p = 0.51). Par abus d’interprétation, on considère que les variances sont égales.
Pour vérifier la condition de normalité, on va évaluer la normalité des résidus du modèle linéaire.
par(mfrow = c(1, 2)) # 2 graphiques sur 1 ligne
### On peut récupérer les résidus du modèle linéaire avec l'opérateur dollar
### Représenter les résidus dans un QQ-plot
qqnorm(mod_pas_trait$residuals)
qqline(mod_pas_trait$residuals)
### Représenter la distribution des résidus par une densité de kernel
vect <- mod_pas_trait$residuals
plot(density(vect), # densité de kernel
main = "Residuals")
lines(x = seq(min(vect), max(vect), length(50)), # loi normale de référence
y = dnorm(seq(min(vect), max(vect), length(50)),
mean = mean(vect),
sd = sd(vect)),
col = "red", lty = "dashed")
La condition normalité semble acceptable.
Si les variances avaient été inégales, on aurait appliqué un test de Kruskal-Wallis, avec la fonction kruskal.test().
##
## Kruskal-Wallis rank sum test
##
## data: pas by traitL
## Kruskal-Wallis chi-squared = 19.04, df = 2, p-value = 7.336e-05
Le test de Kruskal-Wallis rejette l’hypothèse nulle d’égalité des moyenne entre les 3 groupes de traitement, de manière significative (p = 7.3e-05).
3.7.5 Comparer des pourcentages
On peut comparer plusieurs pourcentages avec un test du chi-2 chisq.test().
Par exemple, nous allons tester l’hypothèse nulle suivante : _“H0 : La répartition par sexe est la même dans les 3 groupes de traitement_”
Pour répondre à la question, nous pouvons faire un test comparant le pourcentage d’hommes et de femmes au sein des 3 groupes de traitement. Nous avons déjà vu que les fonction table() et prop.table(table()) permettaient de décrire la répartition du traitement en fonction du sexe.
La fonction chisq.test() va s’appliquer à un tableau croisé des effectifs par traiement et par sexe. On rappelle que les conditions d’application à vérifier sont que tous les effectifs attendus doivent être \(\geq 5\).
### Appliquer un test du chi2 au tableau croisé du traitement en fonction du sex
# Attention, par défaut la fonction chisq.test applique la correction de Yates
# en indiquant correct = FALSE
# cette correction n'est utile que si les conditions d'application du test
# du chi-2 ne sont pas vérifiées.
chi2 <- chisq.test(table(df_1$sexL, df_1$traitL),
correct = FALSE)
chi2##
## Pearson's Chi-squared test
##
## data: table(df_1$sexL, df_1$traitL)
## X-squared = 1.5512, df = 2, p-value = 0.4604
D’après ces résultats, on ne rejette pas l’hypothèse nulle d’égalité de la répartition par sexe dans les 3 groupes de traitement (p = 0.46)
Pour récupérer la table des effectifs attendus et vérifier les conditions d’application, on peut utiliser l’opérateur dollar $expected :
##
## Placebo Traitement A Traitement B
## Féminin 61.2 46.41 45.39
## Masculin 58.8 44.59 43.61
On voit que la condition d’application est vérifiée car tous les effectifs attendus sont \(\geq 5\).
Si les conditions n’avaient pas été vérifiées, on aurait pu appliquer un test exact de Fisher fisher.test()
##
## Fisher's Exact Test for Count Data
##
## data: table(df_1$sexL, df_1$traitL)
## p-value = 0.4536
## alternative hypothesis: two.sided
D’après le test exact de Fisher, on ne peut pas rejetter l’hypothèse nulle d’égalité de la répartition par sexe dans les 3 groupes de traitement.
3.7.6 Corrélations
On peut calculer les corrélations de Pearson et de Spearman entre la PAS et l’IMC avec la fonction cor(). La fonction cor.test() teste l’hypothèse nulle \(H0: \rho = 0\) (elle recalcule également le coefficient de corrélation ainsi qu’un intervalle de confiance). Au sein de ces deux fonctions, vous pouvez préciser si vous souhaiter calculer et tester une corrélation de Pearson ou de Spearman avec l’argument method.
### Estimer puis tester une corrélation de Pearson entre PAS et IMC
rho_pearson <- cor.test(df_1$imc, df_1$pas, method = "pearson")
rho_pearson##
## Pearson's product-moment correlation
##
## data: df_1$imc and df_1$pas
## t = 3.8534, df = 298, p-value = 0.0001427
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
## 0.1072647 0.3231284
## sample estimates:
## cor
## 0.2178593
Le coefficient de corrélation de Pearson est \(\rho_1 = 0.218\), il est signficativement différent de 0 (p=0.00014).
### Estimer puis tester une corrélation de Pearson entre PAS et IMC
rho_spearm <- cor.test(df_1$imc, df_1$pas, method = "spearman") ## Warning in cor.test.default(df_1$imc, df_1$pas, method = "spearman"):
## Impossible de calculer la p-value exacte avec des ex-aequos
##
## Spearman's rank correlation rho
##
## data: df_1$imc and df_1$pas
## S = 3522826, p-value = 0.0001503
## alternative hypothesis: true rho is not equal to 0
## sample estimates:
## rho
## 0.217141
Le coefficient de corrélation de Spearmon est \(\rho_2 = 0.217\), il est signficativement différent de 0 (p=0.00015).
3.8 Analyse multivariée
Nous allons estimer un modèle multivarié de la moyenne de pression artérielle systolique, en fonction du traitement, ajusté sur le sexe et l’IMC.
Pour cela nous allons utiliser la fonction lm() (linear model) :
##
## Call:
## lm(formula = pas ~ traitL + sexL + imc, data = df_1)
##
## Residuals:
## Min 1Q Median 3Q Max
## -38.871 -10.083 -0.342 10.941 41.171
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 109.5668 7.1403 15.345 < 2e-16 ***
## traitLTraitement A -10.9623 2.1246 -5.160 4.55e-07 ***
## traitLTraitement B -2.8373 2.1374 -1.327 0.185388
## sexLMasculin 8.5634 1.7796 4.812 2.39e-06 ***
## imc 1.1240 0.2904 3.870 0.000134 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 15.24 on 295 degrees of freedom
## Multiple R-squared: 0.1902, Adjusted R-squared: 0.1792
## F-statistic: 17.32 on 4 and 295 DF, p-value: 8.911e-13
Après ajustement sur le sexe et l’IMC, on observe que :
- la PAS était en moyenne inférieure de -11.0 mmHg dans le groupe traitement A par rapport au groupe placebo, de manière significative (p=4.6e-07),
- la PAS était en moyenne inférieure de -2.8 mmHg dans le groupe traitement B par rapport au groupe placebo, de manière non-significative (p=0.19).
Pour vérifier les conditions d’application, la méthode la plus classique est de représenter le nuage de points des résidus en fonction des valeurs prédites par le modèle. On peut se servir de l’opérateur dollar pour récupérer :
- les résidus du modèle
model$residuals - les valeurs prédites par le modèle
model$fitted.values
par(mfrow = c(1, 1)) # 1 figure sur 1 ligne et 1 colonne
# la fonction suivant trace une courbe lissée (loess)
# au milieu d'un nuage de points
scatter.smooth(model$fitted.values, model$residuals,
lpars = list(col = "red", lwd = 0.5, lty = 1),
xlab = "Valeurs prédites", ylab = "Résidus")
abline(h = 0, # ajoute une ligne horizontale en 0
lwd = 0.5, # largeur de ligne (line width)
lty = 2) # type de ligne = pointillé
De manière encore plus automatisée, la commande plot(model) permet d’obtenir une série de 4 graphiques de diagnostic post-estimation d’un modèle de régression linéaire (tapez la touche “entrée” 4 fois pour obtenir les graphiques) :
- le 1er graphique est le nuage de points des résidus en fonction des valeurs prédites
- le 2ème graphique permet de vérifier la normalité des résidus standardisés
- le 3ème graphique est le nuage de points de la racine carrée des résidus standardisés en fonction des valeurs prédites
- le 4ème graphique le nuage de points des résidus standardisés en fonction des distances de Cook (pour évaluer l’effet levier)
Pour rappel, vous pouvez sauvegarder le résultat de ce modèle linéaire sous forme d’objet R avec tous ses attributs en utilisant la fonction saveRDS(). Cela vous permet de stocker, puis recharger ces résultats directement dans R.
### Créez un dossier "results" dans votre dossier de travail
### soit directement dans l'environnement windows ou macOS ou linux,
### soit avec la commande dir.create()
dir.create("results")
### Sauvegardez l'objet contenant le modèle linéaire
saveRDS(model, "results/model.R")
### si vous videz la totalité des objets présents dans l'environnement
rm(list = ls())
### Vous pourrez re-importez le modèle original avec readRDS(),
### avec tous les attributs contenus dans le modèle
model <- readRDS("results/model.R")
summary(model)
# Call:
# lm(formula = pas ~ traitL + sexL + imc, data = df_1)
#
# Residuals:
# Min 1Q Median 3Q Max
# -38.871 -10.083 -0.342 10.941 41.171
#
# Coefficients:
# Estimate Std. Error t value Pr(>|t|)
# (Intercept) 109.5668 7.1403 15.345 < 2e-16 ***
# traitLTraitement A -10.9623 2.1246 -5.160 4.55e-07 ***
# traitLTraitement B -2.8373 2.1374 -1.327 0.185388
# sexLMasculin 8.5634 1.7796 4.812 2.39e-06 ***
# imc 1.1240 0.2904 3.870 0.000134 ***
# ---
# Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
#
# Residual standard error: 15.24 on 295 degrees of freedom
# Multiple R-squared: 0.1902, Adjusted R-squared: 0.1792
# F-statistic: 17.32 on 4 and 295 DF, p-value: 8.911e-13
qqnorm(model$residuals)
scatter.smooth(model$fitted.values, model$residuals,
lpars = list(col = "red", lwd = 0.5, lty = 1),
xlab = "Valeurs prédites", ylab = "Résidus")
abline(h = 0, # ajoute une ligne horizontale en 0
lwd = 0.5, # largeur de ligne (line width)
lty = 2) # type de ligne = pointilléIl n’est pas nécessaire de relancer toute l’analyse pour retrouver ces résultats !