solutions

Transcription

solutions
Introduction à R pour la recherche biomédicale
Exercices corrigés
> a <- data.frame(x1=rnorm(n, 10), x2=rnorm(n, 12),
+
x3=gl(2, n/2))
> write.csv(a, file="a.csv")
5. On peut utiliser l'indexation, par exemple
> d$class[which(d$height==max(d$height))]
Le langage R
1. Comme il n'est pas précisé pas si le facteur doit être équilibré et/ou préalablement trié, on peut utiliser sample. Autres
possibilités :
> x <- gl(2, 10, labels=c("toto","titi"))
> relevel(x, ref="titi")
[1] B
Levels: A B
[1] toto toto toto toto toto toto toto toto toto
[10] toto titi titi titi titi titi titi titi titi
[19] titi titi
Levels: titi toto
Ou alors trier les données :
> d[do.call(order, d),"class"][40]
[1] B
Levels: A B
2. Le plus simple est d'utiliser matrix et apply :
> m <- matrix(c(6,7,2,1,5,9,8,3,4), nr=3, byrow=TRUE)
> apply(m, 1, sum) # lignes
[1] 15 15 15
> apply(m, 2, sum)
# colonnes
[1] 15 15 15
3. On peut utiliser rbinom ou sample. Dans le premier cas, une
réalisation de l'événement ”tirer 20 fois une pièce” s'écrit
> rbinom(20, 1, p=0.5)
[1] 0 0 1 1 0 0 1 0 1 1 1 1 1 1 0 1 1 0 0 0
Dans le second cas,
> sample(0:1, 20, replace=TRUE)
[1] 1 1 0 1 1 1 0 0 0 1 0 0 0 0 1 1 0 1 0 1
On peut insérer ces commandes dans une boucle, par exemple
> k <- seq(10, 10000, by=100)
> res <- numeric(length(k))
> for (i in seq_along(k))
+
res[i] <- sum(rbinom(k[i], 1, p=0.5))/k[i]
ou alors, en plus élégant :
> tails <- function(n=5, p=0.5) sum(rbinom(n, 1, p))/n
> res <- sapply(k, tails)
Ci-dessous figure l'évolution du ”nombre de piles” lorsque k
augmente, avec un intervalle de confiance à 95% asymptotique basé sur la loi normale, 𝑝 ± τΊ½.τ»…τ»‚βˆšπ‘(τΊ½ βˆ’ 𝑝)/𝑛.
Proportion de piles
0.60
0.55
●
●
●
●
●
●
●
●●
0.50
●
●
●●
●
●
●
●
●
●
● ●
●
●
●
● ● ● ●●●
● ● ●● ● ●●
● ●
● ●●
●
●
●● ●●
●●
● ● ●● ●● ● ●●
● ●●
● ●●
● ●
●
● ●● ● ● ●●●
●
●
● ● ● ● ●●
●
●
●
●
●●
●
●
0
2000
4000
6000
8000
[1] old old new old new old
Levels: new old
7. Quelques suggestions : vérifier le format du fichier à l'aide
d'un éditeur de texte (en particulier : ligne d'en-tête, séparateur de champ, séparateur décimal) afin de définir la commande à utiliser pour charger le fichier ; vérifier le type des
variables que R a lu ; afficher un résumé numérique (summary) et examiner si les distributions univariées semblent
cohérentes.
Ici, on ne se souciera pas d'imputation : les valeurs suspectes seront converties en valeurs manquantes.
> WD <- "../pub"
> lung <- read.table(paste(WD, "lungcancer.txt", sep="/"),
+
header=TRUE, na.strings=".")
> summary(lung) # str(lung)
time
Min.
: -2.0
1st Qu.: 73.0
Median :167.0
Mean
:186.1
3rd Qu.:275.0
Max.
:558.0
NA's
: 2.0
●
●
●
●
● ●●
●
On peut ajouter as.character pour supprimer l'affichage des
attributs du facteur.
6. La solution simple consiste à créer le schéma d'allocation de
base des niveaux du facteur et à le dupliquer pour obtenir
la taille voulue. Par exemple,
> tx <- factor(rep(rep(c("std","new"), each=3), 10))
Comme le motif est régulier (3 std suivi de 3 new), on peut
utiliser gl directement :
> tx <- gl(2, 3, 60, labels=c("std","new"))
Pour la suite, il s'agit de manipulation des niveaux du facteur.
> levels(tx)[1] <- "old"
> tx <- relevel(tx, ref="new")
> head(tx <- sample(tx))
10000
Nombre de lancers
4. Une solution de base :
> n <- 10
1
age
Min.
: 5.00
1st Qu.:53.00
Median :59.00
Mean
:59.02
3rd Qu.:66.00
Max.
:78.00
NA's
: 2.00
cens
Min.
:0.0000
1st Qu.:0.0000
Median :1.0000
Mean
:0.5191
3rd Qu.:1.0000
Max.
:2.0000
+
> names(anorex)
vital.capac
high:95
low :35
low : 1
[1]
[6]
[11]
[16]
[21]
> head(sort(lung$time))
[1] -2
0
0
1
1
2
> head(sort(lung$age))
"weight"
"purge"
"school"
"body"
"diag2"
to.data.frame=TRUE)
"mens"
"hyper"
"satt"
"time"
"time2"
"fast"
"fami"
"sbeh"
"diag"
"binge"
"eman"
"mood"
"tidi"
> table(lung$cens)
> anorex <- anorex[,-c(21,22)]
> table(anorex$time)
0 1
64 66
1 2 3 4
55 53 54 55
[1]
5 35 36 38 39 40
2
1
> table(lung$vital.capac)
> class(anorex$time)
high
95
[1] "numeric"
low low
35
1
> anorex$time <- factor(anorex$time, ordered=TRUE)
Les données sont stockées au format ”long”, c'est-à-dire
qu'on dispose d'une variable codant la période d'acquisition
des données pour chaque cas (ligne). Sous réserve qu'il
n'y ait pas de valeurs manquantes isolées, un tableau de
fréquence de l'identifiant patient (number) doit nous permettre de savoir de combien de cas complets nous disposons.
> any(apply(anorex, 1, function(x) sum(is.na(x))) != 0)
> lung <- within(lung, {
+
time[time<0] <- NA
+
age[age==5] <- NA
+
cens[cens==2] <- NA
+
cens <- factor(cens)
+
levels(vital.capac)[2:3] <- "low"
+ })
Après ces modifications, on peut vérifier que tout semble
normal :
> summary(lung)
time
Min.
: 0.0
1st Qu.: 74.5
Median :167.5
Mean
:187.6
3rd Qu.:277.0
Max.
:558.0
NA's
: 3.0
vital.capac
high:95
low :36
age
Min.
:35.00
1st Qu.:53.00
Median :59.50
Mean
:59.44
3rd Qu.:66.00
Max.
:78.00
NA's
: 3.00
[1] FALSE
> unique(table(anorex$number))
cens
0
:64
1
:66
NA's: 1
[1] 4 3
> table(table(anorex$number)==4)
FALSE
3
"id"
"los"
"priorstr"
"cooking"
TRUE
52
> names(which(table(anorex$number) < 4))
[1] "47" "71" "76"
Pour connaître la répartition par groupe clinique, il faut bien
sûr filtrer sur la période (time).
> with(subset(anorex, time=="1"), table(diag))
diag
Anorexia Nervosa
25
Anorexia with Bulimia Nervosa
9
Bullimia Nervosa after Anorexia
14
Atypical Eating Disorder
7
8. Pour charger des fichiers de données SPSS (*.sav), on a besoin du package foreign. Voir aussi R Data Import/Export,
§ 3.1 (http://bit.ly/yRnVit).
> library(foreign)
> adl <- read.spss(paste(WD, "adl.sav", sep="/"),
+
to.data.frame=TRUE)
> names(adl)
[1]
[5]
[9]
[13]
"vomit"
"frie"
"preo"
"number"
Enfin, pour le score mood, comme rien n'est précisé on va
calculer le score moyen aux quatre dates :
> with(subset(anorex, diag=="Anorexia Nervosa"),
+
tapply(mood, time, mean))
"group"
"gender"
"age"
"diabetic" "hypertns" "afib"
"smoker"
"psd"
"travel"
"housekpg"
> apply(adl, 2, function(x) sum(is.na(x)))
id
group
0
0
diabetic hypertns
0
0
psd
travel
0
0
gender
age
0
0
afib priorstr
0
0
cooking housekpg
0
0
1
2
3
4
2.360000 2.652174 2.791667 2.840000
los
0
smoker
0
10. Ici, il s'agit d'un jeu de fichier propre à R, que l'on peut
charger avec la commande data, éventuellement en précisant le package dans lequel se trouve le jeu de données.
> data(birthwt, package="MASS")
> summary(birthwt)
> table(adl$group)
Control Treatment
46
54
9. Même principe que ci-dessus. On charge d'abord les données :
> anorex <- read.spss(paste(WD, "anorectic.sav", sep="/"),
2
low
Min.
:0.0000
1st Qu.:0.0000
Median :0.0000
Mean
:0.3122
age
Min.
:14.00
1st Qu.:19.00
Median :23.00
Mean
:23.24
lwt
Min.
: 80.0
1st Qu.:110.0
Median :121.0
Mean
:129.8
3rd Qu.:1.0000
Max.
:1.0000
race
Min.
:1.000
1st Qu.:1.000
Median :1.000
Mean
:1.847
3rd Qu.:3.000
Max.
:3.000
ptl
Min.
:0.0000
1st Qu.:0.0000
Median :0.0000
Mean
:0.1958
3rd Qu.:0.0000
Max.
:3.0000
ui
Min.
:0.0000
1st Qu.:0.0000
Median :0.0000
Mean
:0.1481
3rd Qu.:0.0000
Max.
:1.0000
3rd Qu.:26.00
3rd Qu.:140.0
Max.
:45.00
Max.
:250.0
smoke
Min.
:0.0000
1st Qu.:0.0000
Median :0.0000
Mean
:0.3915
3rd Qu.:1.0000
Max.
:1.0000
ht
Min.
:0.00000
1st Qu.:0.00000
Median :0.00000
Mean
:0.06349
3rd Qu.:0.00000
Max.
:1.00000
ftv
bwt
Min.
:0.0000
Min.
: 709
1st Qu.:0.0000
1st Qu.:2414
Median :0.0000
Median :2977
Mean
:0.7937
Mean
:2945
3rd Qu.:1.0000
3rd Qu.:3487
Max.
:6.0000
Max.
:4990
Descriptive Statistics by low
+------------+--------------+--------------+
|
|No
|Yes
|
|
|(N=130)
|(N=59)
|
+------------+--------------+--------------+
|race : White|
56% (73)
|
39% (23)
|
+------------+--------------+--------------+
|
Black
|
12% (15)
|
19% (11)
|
+------------+--------------+--------------+
|
Other
|
32% (42)
|
42% (25)
|
+------------+--------------+--------------+
|smoke : Yes |
34% (44)
|
51% (30)
|
+------------+--------------+--------------+
|ui : Yes
|
11% ( 14) |
24% ( 14) |
+------------+--------------+--------------+
|ht : Yes
|
4% ( 5) |
12% ( 7) |
+------------+--------------+--------------+
|age
|19.0/23.0/28.0|19.5/22.0/25.0|
+------------+--------------+--------------+
Analyse exploratoire
11. Le fichier étant au format csv, on le charge avec read.csv :
> WD <- "../pub" # à adapter
> cereal <- read.csv(paste(WD, "cereal.csv", sep="/"))
Plusieurs solutions pour afficher les distributions : histogrammes, matrice de dispersion. La dernière à l'avantage
de combiner les deux approches. Considérons par exemple
les 6 premières variables numériques :
> p <- splom(~ cereal[,3:8], type=c("p","smooth"),
+
diag.panel=panel.hist.splom, xlab="",
+
pscale=0, varname.cex=0.7, cex=.8,
+
col="grey60")
On peut décider de recoder les variables low, race, smoke,
ui et ht en facteurs.
> yesno <- c("No","Yes")
> ethn <- c("White","Black","Other")
> birthwt <- within(birthwt, {
+
low <- factor(low, labels=yesno)
+
race <- factor(race, labels=ethn)
+
smoke <- factor(smoke, labels=yesno)
+
ui <- factor(ui, labels=yesno)
+
ht <- factor(ht, labels=yesno)
+ })
Pour les tableaux croisés, on peut utiliser table ou xtabs
(parfois plus commode car on peut utiliser des notations
par formule à partir du data.frame).
> xtabs(~ low + race, data=birthwt)
●
●
●
●
●
● ● ●●
●● ●
●
● ●●
●
●
●
●● ●
●
● ●●● ●
●
●●
●
●
●
●
● ●
●●
●
●
●● ●
●●
● ●
●
race
low
White Black Other
No
73
15
42
Yes
23
11
25
●● ●
●●
●
●
● ●
●
●
●
●●
●●● ●●
●
●
●
●
●
> with(birthwt, table(low, smoke))
smoke
low
No Yes
No 86 44
Yes 29 30
●
●
●
On peut également profiter des commandes très pratiques
dans le package Hmisc,1 par exemple la commande summary.formula qui permet d'effectuer des résumés de variables quantitative ou qualitative conditionnellement aux
niveaux d'un facteur.
Surtout, ne pas oublier de lire l'aide en ligne :
help(summary.formula).
> library(Hmisc)
> summary(low ~ race + smoke + ui + ht + age,
+
data=birthwt, method="reverse")
●
●
●
●●
● ●
●
●
●● ●● ●
● ●●
●●
●● ●
●
●
● ●●●
●
●● ●
●
● ●
●
●
●
●
●
● ●●
●
● ● ●
●
●
●
●
●
●
● ●
●
● ●
● ●
● ●
●●
● ● ●
● ●
● ●
●
●●● ●●
●
●
●●
●
●
●●
●
●
●
●
●
● ● ●●
●●● ●
●●
●
●
●●
●
●
● ●● ●
●● ●
●●
● ●●
●●● ● ●
●
●
●
●
●
●
● ●
● ●
●
●
●
●
●
●●
●
●
●●
●●
●●● ● ●
●●
●
● ●● ●
● ●
●
●
●
●
●
●●●
●●
●●
●
● ●●● ● ●●●
●●
●
●
●
●
●
●
● ●
●
●
●
calories
●
●
●
●
●
●
● ●
●
● ● ●●
●● ●
●●
●●●
●
●
●
●● ●●
● ●●
● ● ● ●●
●●
●●
●
●
●
●
● ●
●
●
●
● ●
●
● ●
● ●
●● ●
● ●●
●
●
●
●●●●
●
●
●
● ●
●●
●
● ●
●
●
●
●●●● ● ●
● ● ●
● ●● ●
●●
●
●
●
●
●
●
●
● ●
●
●
●
●
●
●
●
●
●
●● ●●
● ●●
●●●
●
●
● ●
●
●
● ●
●
●
●
●
●
●
●
●●
●
●
●
●●
● ●● ●
● ●● ●
●
●●
●
●
● ●●●
●●
●
● ●
●
●● ●●● ●
●
●●
●
●
●
●
●
●
● ●
●
●
●●●
●
●●
● ●●
●
●
●●●
●
●
●●●●●●●● ●
●
●●
●●
●
●
●●●
●
●
●
●
●
●
●
●
●
●
● ●
●
●
●
● ●●
●
●●●●
●●
● ●
●●●
● ●
●●
●
●●
●
●●
●●
●
●
●
●
●
●
●
●
●
●●
●●
● ●
● ●●
●
●●
●
●●
● ●●●●
●●
●
●
●
●●●
●●
●
● ●
● ● ● ●●● ●
●
● ● ●
●
●
●
●
●
●
●
●
●
●●●
●
●●
● ●
●●
●
●●
●
● ●
●
●
●
K.
●
●
●●
●●
●
●
●
●
●●
● ●●●
● ●
●
●
● ●● ●● ●●●
●
fat
X.fat
●
Na.
●
●
●
●●
●
● ●●
●●
●●
●
●
●
●●
●●
●
●
● ●●
● ●
●
●
●
●
●● ●● ●
●
● ●
● ●● ● ● ●
●●
●
●
●
● ●
●
●
●● ● ●
●
●
●
●
●●
●● ●
●
●●
●
●
●●
● ●
●●● ●
●
size
●
●
●
●
●
●●
●● ●●
●
●
● ●
●
●●●
●
●
●
●
Notons que l'on a supprimé l'affichage des unités (pscale=0),
par souci de lisibilité et parce que dans une optique ”exploratoire visuelle” on n'en a pas vraiment besoin. Lorsque
le nombre d'observations est beaucoup plus important, on
peut avantageusement remplacer le nuage de points par un
nuage en densité, en ajoutant panel=panel.hexbinplot (après
avoir chargé le package hexbin). Enfin, on peut avoir une approche plus interactive et étudier les distributions bivariées
1
C Alzola and F Harrell. An Introduction to S and The Hmisc and Design Libraries.
2006. URL: http://biostat.mc.vanderbilt.edu/wiki/Main/RS.
3
une à une. Le code suivant permet de sélectionner interactivement les points et afficher le nom de la compagnie.2
> xyplot(cost ~ calories, cereal)
> while (!is.null(fp <- trellis.focus())) {
+
if (fp$col > 0 & fp$row > 0)
+
panel.identify(labels=cereal$company)
+ }
12. On s'intéresse aux données à l'inclusion (time=1), donc dans
un premier temps il est intéressant de réduire le tableau à
ces seules données. Voir l'exercice 9 pour le chargement
du fichier et le recodage de time en facteur. Notons que la
commande summary ne renvoit pas l'écart-type. Elle reste
toutefois utile pour résumer les distributions univariées,
vérifier leur symétrie et la présence d'observations atypiques !
> anorex.scores.t1 <- subset(anorex, time=="1",
+
select=weight:body)
> sapply(anorex.scores.t1,
+
function(x) c(mean=mean(x), sd=sd(x)))
3
4
6
20
22
36
47
54
56
70
72
88
121
138
142
172
189
240
X1
fast
binge
purge
binge
purge
binge
preo
purge
fami
purge
fami
fami
eman
frie
mood
satt
sbeh
body
X2
weight
weight
weight
mens
mens
fast
fast
binge
binge
vomit
vomit
purge
fami
eman
eman
school
satt
preo
value
0.3340665
-0.5726599
-0.4260873
-0.3201155
-0.4378354
-0.3103808
0.3106862
0.7043708
-0.3792735
0.4230144
-0.3332859
-0.4151892
0.3269283
0.4245090
-0.3887455
0.7405494
0.4015018
0.4195842
14. Il faut dans un premier temps calculer les scores moyens par
période. Il existe plusieurs façons d'aborder le problème :
weight
mens
fast
binge
procéder colonne par colonne, et utiliser tapply pour calmean 1.6545455 1.218182 1.4363636 3.254545
culer les scores moyens conditionnellement à time ; passer
sd
0.9854155 0.533712 0.8336363 1.294120
du
format ”wide” au format ”long” (en utilisant le package
vomit
purge
hyper
fami
reshape).
mean 3.6727273 2.763636 1.9636364 1.8727273
sd
0.9438798 1.477782 0.6074731 0.8400738
Pour la première approache, voici une solution possible :
eman
frie
school
satt
> items <- 1:16
mean 1.7818182 2.4727273 2.0181818 2.1454545
> res <- matrix(nr=16, nc=4,
sd
0.6580248 0.8131909 0.7325983 0.7556744
+
dimnames=list(colnames(anorex[,items]),
sbeh
mood
preo
body
+
levels(anorex$time)))
mean 1.818182 2.2727273 1.4363636 1.890909
> for (i in items)
sd
0.434226 0.7806584 0.5362295 0.785817
+
res[i,] <- tapply(anorex[,i], anorex[,"time"], mean)
13. Ici, l'idée est de construire la matrice de corrélation des 16
Dans ce cas, on effectue les opérations item par item, soit
items, en utilisant le coefficient de Spearman. Cette matrice,
16 opérations au total. On pourrait également calculer les
𝑋 , est symétrique, c'est-dire que 𝑋𝑖𝑗 = 𝑋𝑗𝑖 , et sa diagonale ne
moyennes pour chaque niveau du facteur time, soit 4 opéracontient que des 1 (puisqu'il s'agit de la corrélation de l'item
tions seulement :
avec lui-même). Si l'on veut filtrer les coefficients, il faut
> for (i in levels(anorex$time))
considérer soit la partie inférieure, soit la partie supérieure
+
res[,i] <- apply(subset(anorex, time==i, items),
(hors diagonale).
+
2, mean)
> correl.t1 <- cor(anorex.scores.t1, method="spearman")
Quant à la second approche, on utilise une combinaison de
> correl.t1[upper.tri(correl.t1, diag=TRUE)] <- NA
melt et cast pour passer du format ”wide” au format ”long”
À partir de là, une instruction comme apply(correl.t1, 1, funcet revenir avec les données moyennées au format ”wide”.
tion(x) x > 0.3) indiquerait précisément quelles paires de vari> anorex.scores <- melt(anorex[,1:17], id.vars=17)
ables ont un coefficient de corrélation > τΊΌ.τΊΏ. Pour extraire
> res <- with(anorex.scores,
directement les paires de variables qui nous intéressent, on
+
aggregate(value,
peut faire :
+
list(item=variable, time=time),
> names(unlist(apply(correl.t1, 1,
+
mean))
+
function(x) which(abs(x) > .3))))
> cast(res, item ~ time)
[1]
[4]
[7]
[10]
[13]
[16]
"fast.weight"
"binge.fast"
"purge.binge"
"fami.vomit"
"frie.eman"
"mood.eman"
"binge.weight"
"purge.weight"
"purge.vomit"
"fami.purge"
"satt.school"
"preo.fast"
"binge.mens"
"purge.mens"
"fami.binge"
"eman.fami"
"sbeh.satt"
"body.preo"
item
1
2
3
4
1 weight 1.654545 2.641509 2.759259 3.036364
2
mens 1.218182 1.773585 2.222222 2.381818
3
fast 1.436364 2.660377 2.833333 2.963636
4
binge 3.254545 3.584906 3.500000 3.600000
5
vomit 3.672727 3.735849 3.777778 3.709091
6
purge 2.763636 3.452830 3.444444 3.581818
7
hyper 1.963636 2.339623 2.537037 2.709091
8
fami 1.872727 2.301887 2.500000 2.709091
9
eman 1.781818 2.283019 2.462963 2.672727
10
frie 2.472727 2.509434 2.537037 2.600000
mais ce n'est pas très élégant. Une autre solution est de
convertir la matrice de corrélation en data.frame :
> library(reshape)
> subset(melt(correl.t1), abs(value) > .3)
2
D Sarkar. Lattice, Multivariate Data Visualization with R. Springer, 2008, p. 219.
4
L'avantage de cette approche est que l'on peut utiliser plot
sur le résultat de la commande ci-dessus pour obtenir un
graphique de type dotplot, comme illustré ci-dessous :
> adl.num <- subset(adl, select=c(group, travel:housekpg))
> plot(summary(group ~ ., data=adl.num, method="reverse"))
11 school 2.018182 2.188679 2.462963 2.454545
12
satt 2.145455 2.245283 2.314815 2.400000
13
sbeh 1.818182 2.433962 2.351852 2.490909
14
mood 2.272727 2.528302 2.814815 2.727273
15
preo 1.436364 2.188679 2.333333 2.400000
16
body 1.890909 2.188679 2.407407 2.363636
Proportions Stratified by group
En fait cette dernière approche est intéressante lorsque l'on
utilise lattice car elle permet de travailler directement à partir de formule. Quoi qu'il en soit, il est toujours plus intéressant d'avoir des données au format ”long” pour faire des
graphiques avec lattice. Avec les deux premières solutions,
on peut convertir la matrice res en data.frame en tapant
as.data.frame(as.table(res)).
> p <- xyplot(value ~ time, data=anorex.scores,
+
groups=variable, type="a",
+
ylab="Score moyen",
+
auto.key=list(space="right",
+
lines=TRUE, points=FALSE))
travel
Same as before illness
Gets out if someone else drives
Gets out in wheelchair
Home or hospital bound
Bedβˆ’ridden
● ●
●
●
●●
●
●
●●
cooking
Plans and prepares meals
Some cooking but less than normal
Gets food out if prepared by other
Does nothing for meals
Never did any
●
●
● ●
●●
●
●
● ●
housekpg
As before
Does at least 1/2 usual
Occasional dusting of small jobs
No longer keeps house
Never did any
●
●
● ●
●
●
●
●
●
●
4.0
0.0
0.2
0.6
0.8
1.0
Proportion
3.5
weight
mens
fast
binge
vomit
purge
hyper
fami
eman
frie
school
satt
sbeh
mood
preo
body
3.0
Score moyen
0.4
2.5
2.0
16. Voir l'exercice 11 pour le chargement des données. On
procède dans un premier au recodage des compagnies.
> company2 <- factor(cereal$company)
> levels(company2)[c(2,4:8)] <- "other"
On pourrait remplacer la dernière instruction par
> levels(company2)[!(levels(company2) %in%
+
c("g mills","kellogs"))] <- "other"
qui paraît plus compliquée mais qui présente l'avantage de
ne pas avoir à se soucier du nom des variables à regrouper.
Ensuite, le graphique est assez simple à réaliser :
> p <- parallel(~ cereal[,3:8], groups=company2,
+
horizontal.axis=FALSE, auto.key=TRUE)
1.5
1.0
1
2
3
4
time
15. Voir l'exercice 8 pour le chargement des données. On a toujours deux options : procéder variable par variable en utilisant table, ou utiliser des fonctions spécifiques (reshape ou
Hmisc).
> adl.bin <- subset(adl, select=c(group, diabetic:psd))
> library(Hmisc)
> summary(group ~ ., data=adl.bin, method="reverse")
Descriptive Statistics by group
+--------------+---------+----------+
|
|Control |Treatment |
|
|(N=46)
|(N=54)
|
+--------------+---------+----------+
|diabetic : Yes| 17% ( 8)| 13% ( 7) |
+--------------+---------+----------+
|hypertns : Yes| 28% (13)| 37% (20) |
+--------------+---------+----------+
|afib : Yes
| 7% ( 3)| 2% ( 1) |
+--------------+---------+----------+
|priorstr : Yes| 0% ( 0)| 2% ( 1) |
+--------------+---------+----------+
|smoker : Yes | 2% ( 1)| 0% ( 0) |
+--------------+---------+----------+
|psd : Yes
| 33% (15)| 35% (19) |
+--------------+---------+----------+
g mills
other
kellogs
Max
Min
size
calories
X.fat
fat
Na.
K.
À la lecture de ce graphique, il apparaît clairement que les
deux marques g mills et kellogs diffèrent systématiquement
dans la teneur en graisses (staurées ou non), et contrairement aux autres marques, celles-ci ont des teneurs en Na.
supérieures aux autres marques.
5
> p <- qqmath(~ age | group, data=adl, aspect=1,
+
prepanel=prepanel.qqmathline,
+
panel=function(x, ...) {
+
panel.qqmathline(x, ...)
+
panel.qqmath(x, ...)
+
})
17. On part avec les données pré-traitées à l'exercice 14 ; les
données aggrégées par temps se trouve dans la variable res.
> p <- dotplot(reorder(item, x, min) ~ x, data=res,
+
group=time, auto.key=list(column=2),
+
xlab="Score moyen", aspect=.6)
1
2
3
4
●
βˆ’2
βˆ’1
Control
●
85
●
●
●
●
80
●
75
●
●
70
●
●
●
●●
●
●
●●●
●●●●●●●●●●
●
●
●
●
●
●
●
●
●
●
●
●●●
●
●
●●●●●●
●●●●
βˆ’2
●
●●
●
●
65
●
βˆ’1
0
1
●●
●
●
●
●●●
●●●●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●●●●●●●●
●
●
●●●
2
qnorm
●
2.0
2.5
3.0
3.5
On notera que, par défault, lattice propose un arrangement
des sous-figures (”panels”) utilisant des axes verticaux communs. Cela fait souvent sens, mais on peut s'en affranchir en
contrôlant l'argument scale=, comme dans l'exemple suivant :
> update(p, scale=list(relation="free"))
19. Ici, on peut envisager deux solutions. la première consiste à
afficher en parallèle les distributions avant et après traitement, pour chaque groupe de traitement. La seconde consiste à montrer plus explicitement la covariation des scores
au niveau individuel, et elle est plus avantageurse pour détecter des patterns d'association spécifiques ou des individus atypiques.
> data(anorexia, package="MASS")
> p <- bwplot(value/2.2 ~ variable | Treat,
+
data=melt(anorexia), aspect=1.2,
+
layout=c(3,1), ylab="Weight (kg)")
On peut choisir de trier les scores par n'importe quelle fonction. Ici on a choisi le score minimum, qui est apparemment
toujours observé à l'inclusion.
18. Considérant que les données sont déjà chargées sous R (exercice 8), pour afficher la distribution des effectifs, on utilise
type="count", autrement on a par défaut des fréquences relatives.
> p <- histogram(~ age | group, data=adl,
+
type="count", aspect=1)
65
70
Control
75
80
85
90
Treatment
25
20
Count
●
●
●
Score moyen
15
10
5
CBT
0
Cont
FT
70
75
80
85
Weight (kg)
45
65
90
age
Notons qu'il est souvent avantageux de remplacer les histogrammes par des densités (voir densityplot).
Pour la fonction de répartition,
> p <- qqmath(~ age, data=adl, group=group,
+
distribution=qunif, aspect=.5,
+
auto.key=list(x=.05, y=.95))
Control
Treatment
●
●
●●●
●●●●●●●●●●
●●●●●●●●
●●●
●●●●
●
●●●●●●
●●●●
●●●
65
0.0
0.2
0.4
0.6
0.8
●
●
●
●
●
●
●
Postwt
Prewt
Postwt
Prewt
Postwt
Voici la seconde solution, qui utilise simplement un diagramme de dispersion, auquel on ajoute la ”droite identité” (𝑦 = π‘₯) pour faciliter la lecture : les points au dessus
de celle-ci dénotent des patients dont le poids a augmenté
après traitement.
> anorexia <- transform(anorexia, Prewt=Prewt/2.2,
+
Postwt=Postwt/2.2)
> p <- xyplot(Postwt ~ Prewt | Treat, data=anorexia,
+
layout=c(3,1), aspect="iso")
> p <- update(p,
+
panel=function(...) {
+
panel.xyplot(...)
+
panel.abline(0, 1, col="lightgrey")
+
})
●
70
●
Prewt
●
75
●
●
●
80
●
40
35
85
age
2
●
1.5
90
1
90
●
age
vomit
binge
purge
frie
mood
satt
school
hyper
body
fami
sbeh
eman
weight
preo
fast
mens
0
Treatment
1.0
qunif
Pour les QQ plots,
6
15 16 17 18 19
●
CBT
Cont
●
FT
●
●
20
●
●
●
Postwt
●
●
●
●
●
●●
●
●
●
●
●●
●
●●
16
●
●
●●
●
●
●
18
●
●
●
●
●
●
●
●
16
●
● ●
●
●●
17
18
19
Mean 3rd Qu.
0.62
0.81
Max.
4.30
Median
-0.072
Mean 3rd Qu.
-0.093
0.740
Max.
3.300
Median
1.90
Mean 3rd Qu.
1.50
2.40
Max.
4.40
21. Puisque les mesures sont collectées sur les 10 mêmes sujets,
le test approprié est un test pour données appariées :
> with(sleep, tapply(extra, group, mean))
1
2
0.75 2.33
> t.test(extra ~ group, data=sleep, paired=TRUE)
Paired t-test
data: extra by group
t = -4.0621, df = 9, p-value = 0.002833
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
-2.4598858 -0.7001142
sample estimates:
mean of the differences
-1.58
20. Ici, il s'agit globalement de la seconde option de visualisation évoquée à l'exercice précédent, mais en superposant les
graphiques dans la même figure, soit
> p <- xyplot(Postwt ~ Prewt, data=anorexia,
+
group=Treat, span=.8,
+
type=c("p","g","smooth"),
+
auto.key=list(space="right", points=TRUE,
+
lines=TRUE))
●
●
●
●
●
●
●
●
18
CBT
Cont
FT
●
●
●
●
●
●
●
●
● ●
●
●
● ●
Le résultat du test indique qu'il y a bien une différence significative (𝑝 = τΊΌ.τΊΌτΊΌτΊΏ) en faveur du deuxième traitement
(augmentation de une heure et demie de sommeil environ).
Si les groupes étaient considérés comme indépendants (soit
20 sujets au total), on obtient une p-valeur > 0.05, d'où
l'importance de prendre en considération l'appariement
lorsqu'il est présent !
> t.test(extra ~ group, data=sleep)$p.value
[1] 0.07939414
22. L'idée générale est d'estimer la précision d'un estimateur, ici
une moyenne de différences, en se servant de l'échantillon
disponible par une méthode que l'on appelle le bootstrap.
> di <- with(sleep, abs(diff(tapply(extra,
+
group, mean))))
> sleep2 <- with(sleep, cbind(extra[group==1],
+
extra[group==2]))
> B <- 499
> res <- numeric(B)
> for (i in 1:B)
+
res[i] <- mean(apply(sleep2[sample(10, rep=TRUE),],
+
1, diff))
> quantile(res, c(.025, .975))
●
●
16
●
●
●
2.5% 97.5%
1.0090 2.3765
●
●
16
●
16
Mesures et tests d'association
Median
0.29
20
●
15 16 17 18 19
$FT
Min. 1st Qu.
-1.10
0.81
●
●
Prewt
On calcule les scores de différence assez simplement,
comme suit :
> anorexia$an.diff <- with(anorexia, Postwt-Prewt)
et on les résume aussi simplement :
> with(anorexia, tapply(an.diff, Treat, summary,
+
digits=2))
$CBT
Min. 1st Qu.
-1.90
-0.14
$Cont
Min. 1st Qu.
-2.500 -1.400
CBT
Cont
FT
●
●
●
●
15
Prewt
Postwt
● ●●
●
●
●● ● ●
● ●
●●
●
●
15 16 17 18 19
15
18
●
●●
●●
●
●
●
●
●●
●
●
●
●
●● ●
●
●
●
●
●●
●
●● ●
Postwt
●
●
20
17
18
19
Prewt
100
Le paramètre span permet de contrôler le degré de lissage
des courbes ”loess”.
En utilisant des droites de régression, soit en supposant
linéarité de la relation fonctionnelle entre les deux séries
de mesure (typiquement, dans le cadre d'une analyse de covariance), on a les résultats suivants :
> p <- update(p, type=c("p","g","r"), aspect=.5)
Count
80
60
40
20
0
1.0
7
1.5
2.0
2.5
On peut vérifier que l'on obtient sensiblement les mêmes
résultats en utilisant les fonctions R dédiées.
> library(boot)
> d <- function(d, x) mean(apply(d[x,], 1, diff))
> boot.ci(boot(sleep2, d, R=499), type="perc")
purge
π‘˜
y a βˆπ‘› =
BOOTSTRAP CONFIDENCE INTERVAL CALCULATIONS
Based on 499 bootstrap replicates
CALL :
boot.ci(boot.out = boot(sleep2, d, R = 499), type = "perc")
Intervals :
Level
Percentile
95%
( 0.985, 2.450 )
Calculations and Intervals on Original Scale
23. Dans un premier temps, on filtre les données qui nous intéressent, à savoir mood à 𝑑τΊ½ et 𝑑τ»€ . On a également besoin
de l'identifiant patient pour s'assurer du bon appariement
des scores.
> anorex0 <- subset(anorex, time %in% c(1,4) &
+
diag=="Anorexia Nervosa",
+
select=c(number,time,mood))
> all(table(anorex0$number)==2)
[1] TRUE
> xtabs(~ time + mood, data=anorex0)
time
1
2
3
4
mood
1
5
0
0
1
2 3
6 14
0 0
0 0
2 22
> wilcox.test(mood ~ time, data=anorex0, paired=TRUE,
+
exact=FALSE)
Wilcoxon signed rank test with continuity
correction
data: mood by time
V = 9, p-value = 0.01628
alternative hypothesis: true location shift is not equal to 0
Le résultat suggère que les scores ont tendance à évoluer
dans le sens de valeurs plus élevées entre l'inclusion et
la fin de l'étude.
Ceci étant, en regardant les données brutes, il apparaît clairement que les scores se
concentrent autour de 3 à 𝑑τ»€ (88 % des observations).
●
●
●
1
● ●●
●●
1
●●
●
● ●
●
2
0.704
0.423
1.000
􏻀!
τΊΎ!(τ»€βˆ’τΊΎ)!
= τ»‚ manières de combiner deux éléments
pris parmi n). En voici une, selon laquelle on sélectionne les
colonnes de anorex1 à tester à partir d'un tableau résumant
les combinaisons possibles :
> cb <- combn(4, 2)
> res.p <- numeric(ncol(cb))
> for (i in 1:ncol(cb))
+
res.p[i] <- cor.test(anorex1[,cb[1,i]],
+
anorex1[,cb[2,i]],
+
method="spearman",
+
exact=FALSE)$p.value
Enfin, voici une façon possible pour regrouper les résultats
dans un data.frame, qui repose globalement sur ce qui a été
vu à l'exercice 13 :
> res.c[upper.tri(res.c, diag=TRUE)] <- NA
> res.c <- subset(melt(res.c), !is.na(value))
> res.c$p <- res.p
> res.c$p.adj <- ifelse(res.c$p*6 < 1, res.c$p*6, 1)
On pourra vérifier que la dernière colonne peut également
être obtenue par la commande suivante :
> p.adjust(res.c$p, "bonf")
Le résultat ci-dessous indique que, à l'exception de
weight/vomit et binge/vomit, toutes les associations testées
sont significatives, même après une correction (très conservative) pour les comparaisons multiples.
> format(res.c, digits=3, scientific=FALSE)
2
3
4
7
8
12
X1
X2 value
p
p.adj
binge weight -0.573 0.00000491182 0.0000294709
vomit weight -0.120 0.38353694716 1.0000000000
purge weight -0.426 0.00118078100 0.0070846860
vomit binge 0.170 0.21490582189 1.0000000000
purge binge 0.704 0.00000000197 0.0000000118
purge vomit 0.423 0.00129263518 0.0077558111
La même procédure peut être exploitée pour refaire les calculs à 𝑑τ»€ : il suffit de changer le filtre initial, time==4. On
obtiendrait les résultats suivants :
> format(res.c, digits=3, scientific=FALSE)
2
3
4
7
8
12
●● ●●● ● ●
●
● ●●●
● ● ●●
● ●●
time
4
-0.426
Ici, il existe plusieurs façons de procéder pour tester les corrélations des variables prises deux à deux (on rappelle qu'il
● ●●●
● ●●●
●●
●
●●●
3
X1
X2 value
p
p.adj
binge weight 0.2910 0.03113317414 0.1867990449
vomit weight 0.0597 0.66515436534 1.0000000000
purge weight 0.2406 0.07684707553 0.4610824532
vomit binge 0.3536 0.00808064175 0.0484838505
purge binge 0.6880 0.00000000647 0.0000000388
purge vomit 0.4811 0.00020071774 0.0012043065
Cette fois-ci, toutes les associations sont positives ; en particulier, la corrélation entre binge et weight est passée de
-0.573 à 0.291 alors que celle entre purge et weight est passée
de -0.426 à 0.241, et seraient juger non-significatives à 𝑑τ»€
selon le critère de Bonferroni. Toutefois, on a un peu triché
car on a appliqué les corrections sur 6 comparaisons, alors
qu'en réalité on en a réalisé 12 !
25. Les données ont déjà été résumées visuellement sous forme
de boîtes à moustaches à l'exercice 19. Le modèle d'ANOVA
est assez simple à réaliser :
mood
24. Encore une fois, on sélectionne dans un premier temps les
données qui nous intéressent.
> anorex1 <- subset(anorex, time==1,
+
select=c(weight,binge:purge))
> round(res.c <- cor(anorex1, method="spearman"), 3)
weight binge vomit purge
weight 1.000 -0.573 -0.120 -0.426
binge -0.573 1.000 0.170 0.704
vomit -0.120 0.170 1.000 0.423
8
> with(anorexia, tapply(Prewt, Treat, var))
70
CBT
Cont
FT
1.002272 1.390382 1.074346
Df Sum Sq Mean Sq F value Pr(>F)
2
1.39 0.6952
0.599 0.552
69 80.01 1.1596
●
50
PCV
Min.
:38.00
1st Qu.:44.00
Median :45.00
Mean
:45.25
3rd Qu.:47.00
Max.
:52.00
NEUTRO
Min.
: 9.00
1st Qu.:20.00
Median :24.00
Mean
:24.89
3rd Qu.:29.00
Max.
:42.00
WBC
Min.
:3100
1st Qu.:4500
Median :5100
Mean
:5384
3rd Qu.:6000
Max.
:9899
LEAD
Min.
:13.00
1st Qu.:17.00
Median :19.00
Mean
:20.27
3rd Qu.:23.00
Max.
:62.00
LYMPHO
●
À l'évidence, rien ne permet de rejeter l'hypothèse nulle
d'absence de différence de poids lors de l'entrée dans l'étude
chez ces 72 patientes.
26. Avant toute chose, il est utile de caractériser numériquement et visuellement les distributions uni- et bivariées.
> paint <- read.table(paste(WD, "PAINT.DAT", sep="/"),
+
header=TRUE)
> summary(paint)
HAEMO
Min.
:13.20
1st Qu.:14.60
Median :15.00
Mean
:15.17
3rd Qu.:15.85
Max.
:17.40
LYMPHO
Min.
: 8.00
1st Qu.:17.00
Median :22.00
Mean
:23.83
3rd Qu.:27.50
Max.
:69.00
●
60
> summary(aov(Prewt ~ Treat, anorexia))
Treat
Residuals
●
●
●
40
●
●
●
30
●
●
●
●
●●
● ● ●
●
● ●
●
●
●●
●
●
●
● ●
● ● ●
● ● ● ●
●
● ● ●
●
●● ●
●● ● ●
● ●
●●
● ●
●
●●
●
●
●
●
●
10
●
●
●
●
●
●
4000
●
●
●
●
●
●
20
●
●●
●
●
●●
●
●
● ●
●
●
● ●
●
●
●
●
6000
8000
10000
WBC
Finalement, la corrélation entre LYMPHO et WBC, ajustée sur
le volume moyen d'hémoglobine ne varie pas sensiblement
des calculs ci-dessus.
> library(ppcor)
> with(paint, pcor.test(LYMPHO, WBC, HAEMO))
estimate
p.value statistic
n gp Method
1 0.8261682 1.10664e-48 14.66336 103 1 pearson
27. Le chargement des données ne pose pas de problème particulier puisque les données sont déjà stockées sous forme de
data.frame, format manipulable avec la commande xtabs.
> blood <- read.table(paste(WD, "blood.txt", sep="/"),
+
header=TRUE)
> bg <- c("A","B","AB","O")
> blood$mother <- factor(blood$mother, labels=bg)
> blood$father <- factor(blood$father, labels=bg)
> blood$sex <- factor(blood$sex,
+
labels=c("female","male"))
> (chsq <- chisq.test(xtabs(count ~ mother + father,
+
data=blood)))
> p <- splom(~ paint)
Le test de corrélation ne pose pas de difficultés ; sans aucune hypothèse sur la direction de l'association (positive ou
négative), un test bilatéral s'impose :
> with(paint, cor.test(LYMPHO, WBC))
Pearson's product-moment correlation
data: LYMPHO and WBC
t = 14.7823, df = 101, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
0.7541505 0.8797144
sample estimates:
cor
0.8269801
Pearson's Chi-squared test
data: xtabs(count ~ mother + father, data = blood)
X-squared = 20.8776, df = 9, p-value =
0.0132
Le résultat du test est significatif et suggère une association
entre les groupes sanguins. L'inspection des résidus, residuals(chsq) indique des écarts particuliers à l'indépendance,
avec une plus grande contribution au πœ’τΊΎ des groupes A/A et
B/A (mère/père).
28. Reconstruisons dans un premier temps le tableau de contingence et réalisons un test du πœ’τΊΎ pour tester l'indépendance
entre les deux variables.
> snp <- matrix(c(30,30,246,130,380,184), ncol=3)
> dimnames(snp) <- list(BMI=c("<= 25","> 25"),
+
Geno=c("AA","GA","GG"))
> chisq.test(snp)
Un simple diagramme de dispersion met en évidence une
observation assez éloignée des autres (aux coordonnées,
(τ»…τ»„τ»…τ»…, τ»‚τ»…)). On peut recalculer la corrélation linéaire sans
les points appartenant à l'enveloppe du nuage de dispersion
comme suit :
> chi <- with(paint, chull(LYMPHO, WBC))
> with(paint[-chi,], cor.test(LYMPHO, WBC))
Pearson's product-moment correlation
data: LYMPHO and WBC
t = 12.9676, df = 92, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
0.7183762 0.8655857
sample estimates:
cor
0.803971
Pearson's Chi-squared test
data: snp
X-squared = 7.2638, df = 2, p-value =
0.02647
Le résultat du test est significatif (𝑝 = τΊΌ.τΊΌτΊΎτ»‚) et suggère
que le génotype est associé au BMI des sujets. Ce test utlise
9
(τΊΎ βˆ’ τΊ½) × (τΊΏ βˆ’ τΊ½) = τΊΎ degrés de libertés. On peut faire mieux
Control
Treatment
0.15
Density
et surtout rendre compte de la nature même de la variable
Geno sous un modèle de dosage allélique en recodant celleci comme une variable ordinale : AA=0, GA=1, GG=2. Il est
nécessaire de passer au format ”long” (en utilisant le package reshape) :
> snp.df <- as.data.frame(as.table(snp))
> snp.df <- untable(snp.df, snp.df$Freq)
> snp.df$Geno <- ordered(snp.df$Geno)
> test.stats <- sum(snp) * with(snp.df,
+
cor(as.numeric(BMI), as.numeric(Geno))^2)
> pchisq(test.stats, df=1, lower.tail=FALSE)
0.10
0.05
0.00
●
●
10
●
●
●
●
●
●
●
●
●
●
●
●
●
15
20
●
●
25
los
Il est tout à fait possible d'utiliser un test 𝑑 pour comparer
les durées moyennes de séjour. Toutefois, considérons la
stratégie suivante : on permute aléatoirement les labels du
facteur group et on recalcule la différence de moyennes ;
cette procédure est répétée 999 fois. Il s'agit donc d'une
approximation car le nombre total de permutations est de
[1] 0.03396257
Le test est ici encore significatif. À la différence du
πœ’τΊΎ d'indépendance, ici on a tenu compte de la nature ordinale de Geno et gagner un degré de liberté.
(􏻀􏻂+􏻁􏻀)!
􏻀􏻂!􏻁􏻀!
.
> perm.diff <- replicate(999,
+
with(adl,
+
diff(tapply(los,
+
sample(group),
+
mean))))
> sum(perm.diff <= dm)/1000
BMI above 25
BMI less or equal to 25
[1] 0.027
AA
GA
GG
200
Genotype
On peut comparer le résultat précédent avec un test de
Cochran-Armitage (version conditionnelle) :
> library(coin)
> independence_test(BMI ~ Geno, data=snp.df,
+
teststat="quad",
+
scores=list(Geno=c(0,1,2)))
Count
150
100
50
0
βˆ’2
Asymptotic General Independence Test
data: BMI by Geno (AA < GA < GG)
chi-squared = 4.4921, df = 1, p-value =
0.03405
βˆ’1
0
1
Ce résultat peut être comparé à ce que donneraient des
fonctions plus sophistiquées de R pour les tests de permutation. Par exemple,
> library(coin)
> oneway_test(los ~ group, data=adl,
+
distribution=approximate(B=999))
29. La différence de moyenne, même minime, mérite d'être
testée puisque la durée de séjour est un facteur pronostic.
On peut résumer les données de manière numérique avec
tapply ou bien en utilisant le package Hmisc :
> summary(los ~ group, data=adl, fun=mean)
Approximative 2-Sample Permutation Test
los by
group (Control, Treatment)
Z = 2.0488, p-value = 0.04605
alternative hypothesis: true mu is not equal to 0
data:
los
N=100
+-------+---------+---+--------+
|
|
|N |los
|
+-------+---------+---+--------+
|group |Control | 46|17.82609|
|
|Treatment| 54|16.75926|
+-------+---------+---+--------+
|Overall|
|100|17.25000|
+-------+---------+---+--------+
> (dm <- as.numeric(diff(with(adl, tapply(los, group,
+
mean)))))
[1] -1.066828
10
Un test exact donnerait une 𝑝-valeur de 0.04381. Notre test
”manuel”, avec 99999 rééchantillonnages, donne 0.04546
(en bilatéral). Un test 𝑑 nous donnerait 𝑝 = τΊΌ.τΊΌτΊΏτ»…τ»„τ», en
supposant l'homogénéité des variances.
30. L'idée est de vérifier empiriquement le taux de faux positifs
(risque de première espèce) que l'on observerait hypothétiquement en répétant le processus de décision associé au
test d'hypothèse.
La commande suivante permet d'appeler la fonction
sim.data en fixant dm=0 et en variant n de 10 à 20 (par pas
de 1). Elle renvoit les 𝑝-valeurs associées au test de Student.
Cela ne suffit toutefois pas pour évaluer la fréquence de rejet de 𝐻τΊΌ , car il faudrait répliquer l'expérience plusieurs fois,
pour chaque paramètre manipulé.
> sapply(10:20, sim.data, dm=0.5)
2369.623518
4.429108
[[2]]
(Intercept) I(lwt/2.2)
2.369623518 0.009744037
[1] 0.22592951 0.06750175 0.04390103 0.86025844
[5] 0.19966900 0.36742708 0.20093824 0.23607682
[9] 0.24345003 0.26565522 0.06743805
Dans l'exemple ci-dessous, on simule 100 tests sur la différence de moyenne observée, alors qu'en réalité les deux
échantillons proviennent de la même population (dm=0).
> sum(replicate(100, sim.data(n=15, dm=0,
+
var.equal=TRUE))<.05)/100
[[3]]
(Intercept) scale(lwt, scale = F)
2944.587302
4.429108
> summary(mod[[3]])
Call:
lm(formula = bwt ~ scale(lwt, scale = F), data = birthwt)
Residuals:
Min
1Q
Median
3Q
Max
-2192.12 -497.97
-3.84
508.32 2075.60
[1] 0.04
On rejetterait donc 𝐻τΊΌ (à tort) dans 4 cas sur les 100, mais
cela reste en deçà du risque de première espèce fixé à 5 %.
Voici un autre exemple de simulation où l'on fait varier simultanément n et dm. Les fréquences de rejet de 𝐻τΊΌ sur
100 simulations sont résumées dans la figure suivante.
> k <- 100
> des.params <- expand.grid(n=seq(10, 100, by=10),
+
dm=seq(0, 1, by=.2))
> res <- replicate(k, apply(des.params, 1,
+
function(x) sim.data(n=x[1], dm=x[2],
+
var.equal=TRUE)))
> res <- data.frame(des.params,
+
perc=apply(res, 1,
+
function(x) sum(x<.05))/k)
●
●
●
●
Residual standard error: 718.4 on 187 degrees of freedom
Multiple R-squared: 0.0345,
Adjusted R-squared: 0.02933
F-statistic: 6.681 on 1 and 187 DF, p-value: 0.0105
L'examen des coefficients de régression et tests 𝑑 associés
indiquent que dans les trois cas on arrive aux mêmes conclusions : le poids de la mère (lwt) est significatif à 5 %. En
revanche, l'interprétation des coefficients sera différente :
dans le premier modèle, l'ordonnée à l'origine est le poids
moyen des enfants, en g, lorsque le poids de la mère (en
livres) est nul (peu interprétable) ; dans le deuxième modèle, le terme d'intercept reflète la même chose en changeant
les unités de mesure (kg) ; dans le troisième modèle, il s'agit
du poids des enfants ayant des mères de poids moyen. La
pente indique dans tous les cas l'augmentation du poids des
bébés lorsque le poids de la mère, mesuré en livres ou en
kg, varie d'une unité.
32. Considérons le premier modèle. Les résidus s'obtiennent
ainsi :
> mod1.resid <- resid(mod[[1]])
Une façon d'examiner leur distribution est d'afficher un histogramme, ou un Q-Q plot de leurs valeurs, ainsi qu'un
graphique montrant l'évolution des résidus en fonction des
valeurs prédites.
> p <- qqmath(~ resid(mod[[1]]))
> p <- xyplot(resid(mod[[1]]) ~ fitted(mod[[1]]))
●
●
●
% rejet H0
0.8
0.6
dm
●
0
0.2
0.4
0.6
0.8
1
0.4
0.2
●
●
●
●
●
●
●
●
●
●
0.0
20
40
60
80
100
n
Modèle linéaire et applications
31. La formule R de base reste la même dans les trois cas, à
l'unité de mesure près. On notera que l'opérateur / sert
à désigner les relations d'emboîtement dans les formules,
donc il faut utiliser I() pour empêcher une telle interprétation.3
> mod <- list()
> mod[[1]] <- lm(bwt ~ lwt, data=birthwt)
> mod[[2]] <- lm(I(bwt/1000) ~ I(lwt/2.2), data=birthwt)
> mod[[3]] <- lm(bwt ~ scale(lwt, scale=F), data=birthwt)
> lapply(mod, coef)
[[1]]
(Intercept)
2000
1000
0
βˆ’1000
βˆ’2000
●
●
●●●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●●
●
●●
βˆ’3 βˆ’2 βˆ’1
1000
0
βˆ’1000
βˆ’2000
0
1
2
3
Standard normal quantiles
lwt
2000
Residuals
●
●
Estimate Std. Error t value
(Intercept)
2944.587
52.259 56.346
scale(lwt, scale = F)
4.429
1.713
2.585
Pr(>|t|)
(Intercept)
<2e-16
scale(lwt, scale = F)
0.0105
Residuals
1.0
Coefficients:
●
●
●●
● ●●
●
●
●●
●● ●
●●
●●
● ●●
● ●
● ●● ●
●●
●●
●
●
●
●
●
●
●●
●●
●●●●●●
● ●● ●● ●
●●
●●
●●
●
●●
● ●●
● ●
●●
●
●●●
●
●
● ●●●
●
●
●
●
●●●
●
●
●
● ●●
●
●
●
●
●●
●
●
● ●
●●
●●●●
●●
●
● ●●
●●
●●●
●
●●
●
● ●
●
●●
●●
●
●●
● ● ●●● ●●
●●
●●
●
● ●
●
●
●
●
2800 3000 3200 3400
Fitted values
Pour aller plus loin, on peut examiner les mesures
d'influence.
> mod1.inf <- influence.measures(mod[[1]])
3
JM Chambers and TJ Hastie, eds. Statistical Models in S. Wadsworth & Brooks,
1992, pp. 28 et 31.
11
> head(round(mod1.inf$infmat, 3))
85
86
87
88
89
91
> anova(mod1a)
dfb.1_ dfb.lwt dffit cov.r cook.d
hat
0.097 -0.115 -0.134 1.023 0.009 0.021
0.030 -0.043 -0.067 1.014 0.002 0.009
-0.029
0.023 -0.036 1.018 0.001 0.009
-0.024
0.018 -0.032 1.018 0.001 0.008
-0.024
0.019 -0.031 1.018 0.000 0.008
-0.012
0.006 -0.031 1.014 0.000 0.005
Analysis of Variance Table
Response: bwt
Df
Sum Sq Mean Sq F value
Pr(>F)
race
2 5015725 2507863 4.9125 0.008336
Residuals 186 94953931 510505
Les contrastes utilisés ci-dessus sont appelés contrastes de
traitement.
> op <- options(contrasts=c("contr.sum", "contr.poly"))
> mod1b <- lm(bwt ~ race, data=birthwt)
> coef(mod1b)
Si l'on se réfère aux indices DFFITs, les observations jugées
influentes (supérieures en valeurs absolues à ±τΊΎβˆšτΊΎ/𝑛) sont
données par
> idx <- which(mod1.inf$is.inf[,"dffit"])
> c(-1,1)*2*sqrt(2/nrow(birthwt))
[1] -0.2057378
(Intercept)
2875.8982
0.2057378
dfb.1_ dfb.lwt dffit cov.r cook.d
hat
0.349 -0.409 -0.464 0.944 0.103 0.024
0.282 -0.322 -0.351 1.006 0.061 0.033
Black
Other
-383.0264 -297.4352
Pour ré-estimer le modèle de régression sans ces individus, on pourrait utiliser update(mod[[1]], subset=!(rownames(birthwt) %in% idx). Ces deux points
sont surlignés dans le diagramme de dispersion suivant.
5000
> grp.means[1:2] - mean(grp.means)
Dans le premier modèle, l'intercept vaut grp.means[1], tandis que dans le second modèle il s'agit de la moyenne des
moyennes de groupe (mean(grp.means)). Les deux coefficients associés aux pentes représentent dans le premier cas
les déviations entre Black et Other par rapport à White, et
dans le second cas entre White et Black et la moyenne des
trois groupes. Pour plus d'informations sur le codage des
contrastes sous R, http://bit.ly/LFkFBg.
34. Pour l'ANOVA à deux facteurs, on considèrera donc les termes race, smoke et race:smoke (interaction).
> aov0 <- aov(bwt ~ race * smoke, data=birthwt)
> summary(aov0)
●
bwt
3000
2000
●
●
●
●
●
●
●●
●
●●
●
●●
● ●
●
●
● ●
●
●
●
●
●
●
●
●
●
●
● ●
●●● ●●
●
● ● ●● ●
●●
● ●
●
●
●
●
●
● ●
●
● ●
● ●●
●
●●
● ● ●
●
● ● ● ● ●● ●●
●
●
●
● ●
●
●
●●
●
●
●
●
●
●
●●● ● ● ● ●
●●
● ●
●●
●
●
●
● ●
●● ●
●
●● ●
●
●
● ● ●
● ●●● ●
●
●
●●
●
●
●
● ●● ● ● ●
●
●
●
●
● ●
●
●
●
●
●
●●
●
●
●
●
●
●
●
●
●
●●
●
●
●
●
●
●
●
●
●
1000
●
●
28
11
●
100
150
200
# mod1b
White
Black
226.8205 -156.2059
●
4000
race2
-156.2059
> options(op)
On peut le vérifier là partir des moyennes de groupe :
> grp.means <- with(birthwt, tapply(bwt, race, mean))
> grp.means[2:3] - grp.means[1]
# mod1a
> round(mod1.inf$infmat[idx,], 3)
11
28
race1
226.8205
250
lwt
33. Un
modèle
d'ANOVA
à
un
facteur
s'écrirait
aov(bwt ~ race, data=birthwt). Avec la commande
lm, on obtient les mêmes informations, ainsi que les
déviations des moyennes de groupes par rapport à la
moyenne des poids des bébés chez les mères dont le niveau
est white. Après recodage du facteur (cf. exercice 10), on
obtient
> mod1a <- lm(bwt ~ race, data=birthwt)
> summary(mod1a)
race
smoke
race:smoke
Residuals
Df
Sum Sq
2 5015725
1 7322575
2 2101808
183 85529548
Mean Sq F value
Pr(>F)
2507863
5.366 0.005438
7322575 15.667 0.000108
1050904
2.249 0.108463
467375
L'interaction n'est pas significative. On peut réestimer le
modèle sans elle comme suit :
> aov1 <- update(aov0, . ~ . - race:smoke)
> summary(aov1)
Call:
lm(formula = bwt ~ race, data = birthwt)
Residuals:
Min
1Q
Median
3Q
Max
-2096.28 -502.72
-12.72
526.28 1887.28
race
smoke
Residuals
Df
Sum Sq Mean Sq F value
Pr(>F)
2 5015725 2507863
5.294 0.005809
1 7322575 7322575 15.459 0.000119
185 87631356 473683
Un résumé des effets principaux peut être obtenu ainsi :
> model.tables(aov1)
Tables of effects
race
White Black Other
158.1 -224.9 -139.3
rep 96.0
26.0
67.0
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 3102.72
72.92 42.548 < 2e-16
raceBlack
-383.03
157.96 -2.425 0.01627
raceOther
-297.44
113.74 -2.615 0.00965
Residual standard error: 714.5 on 186 degrees of freedom
Multiple R-squared: 0.05017,
Adjusted R-squared: 0.03996
F-statistic: 4.913 on 2 and 186 DF, p-value: 0.008336
12
smoke
No
Yes
148.5 -230.8
rep 115.0
74.0
> summary(mod2)
ce qui est essentiellement équivalent à calculer les différences entre les moyennes de groupe et la grande
moyenne manuellement :
> with(birthwt, tapply(bwt, race, mean) - mean(bwt))
Call:
lm(formula = glucose ~ exercise, data = hers, subset = diabetes ==
0)
Residuals:
Min
1Q Median
3Q
Max
-48.668 -6.668 -0.668
5.639 29.332
White
Black
Other
158.1314 -224.8950 -139.3037
35. Pour obtenir les résidus du modèle aov1 précédent, on utilise
comme dans le cas de la régression l'extracteur resid() :
> aov1.resid <- resid(aov1)
> round(summary(aov1.resid), 1)
Min. 1st Qu.
-2314.0 -440.2
Median
15.8
Mean 3rd Qu.
0.0
492.1
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 97.3610
0.2815 345.848 < 2e-16
exercise
-1.6928
0.4376 -3.868 0.000113
Max.
1655.0
> p <- qqmath(~ aov1.resid)
Residual standard error: 9.715 on 2030 degrees of freedom
Multiple R-squared: 0.007318,
Adjusted R-squared: 0.006829
F-statistic: 14.97 on 1 and 2030 DF, p-value: 0.000113
●
Residuals
1000
0
βˆ’1000
●
●
●●●●
●●●●●
●●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●●
●●
●●
●●
●●●
●
On constate que l'effet du facteur exercise est plus important sur le taux de glucose (1.7 mg/dL plus bas chez les patients qui font de l'exercice au moins 3 fois par semaine).
Les deux modèles sont emboîtés (mod2 ne comprend que
des termes présents dans mod1), ce qui justifierait l'emploi
d'un test 𝐹 pour comparer les deux résiduelles. Toutefois, il
faudrait s'assurer que les deux modèles ont bien été estimés
sur le même nombre d'individus, ce qui n'est pas garantit
en raison de la présence de valeurs manquantes (supposées
MAR).
L'examen des valeurs résiduelles en fonction des valeurs
prédites fait apparaître quelques patients atypiques. On
peut confirmer cette approche graphique par un résumé
numérique des résidus par rapport aux critères habituels.
> p <- xyplot(resid(mod1) ~ fitted(mod1),
+
type=c("p", "smooth"))
> mod1.inf <- influence.measures(mod1)
> which(mod1.inf$is.inf[,"dffit"])
●
●
●
βˆ’2000
●
βˆ’3
βˆ’2
βˆ’1
0
1
2
3
Standard normal quantiles
Rien n'indique apparemment de déviations flagrante par
rapport à l'hypothèse de normalité des résidus (on pourrait utiliser un test plus formel, comme shapiro.test, mais
cela n'est pas vraiment utile dans ce cas).
36. Pour le chargement des données, il faut vérifier le codage
des données manquantes (souvent présentes dans les études
épidémiologiques).
> hers <- read.table("../pub/hersdata.txt",
+
header=TRUE, na.strings=".")
> fm <- glucose ~ exercise + age + drinkany + BMI
> mod1 <- lm(fm, data=hers, subset=diabetes==0)
> summary(mod1)
20 256 369 391 401 804 1187 1295 1352 1597
16 176 255 272 281 587 868 954 996 1186
1730 1747 1833 2470 2655 2709 2717
1282 1292 1353 1814 1947 1987 1993
Concernant les résidus studentisés les plus extrêmes, on
retrouve une partie des individus listés ci-dessus :
> which(abs(rstudent(mod1))>3)
Call:
lm(formula = fm, data = hers, subset = diabetes == 0)
Residuals:
Min
1Q Median
3Q
Max
-47.560 -6.400 -0.886
5.496 32.060
256 1187 1747 1833 2002 2221 2470
176 868 1292 1353 1479 1646 1814
●
●●
●●●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●
●●●
●●
●
20
Residuals
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 78.96239
2.59284 30.454
<2e-16
exercise
-0.95044
0.42873 -2.217
0.0267
age
0.06355
0.03139
2.024
0.0431
drinkany
0.68026
0.42196
1.612
0.1071
BMI
0.48924
0.04155 11.774
<2e-16
0
βˆ’20
●
●
●
1833
βˆ’40
Residual standard error: 9.389 on 2023 degrees of freedom
(4 observations deleted due to missingness)
Multiple R-squared: 0.07197,
Adjusted R-squared: 0.07013
F-statistic: 39.22 on 4 and 2023 DF, p-value: < 2.2e-16
●
2470
βˆ’2
0
2
Standard normal quantiles
37. Connaissant les coefficients de régression du modèle,
> coef(mod1)
Le modèle réduit s'écrit glucose ~ exercise :
> mod2 <- update(mod1, . ~ . - age - drinkany - BMI)
13
(Intercept)
exercise
78.96239394 -0.95044096
BMI
0.48924198
age
0.06354948
>
>
>
>
drinkany
0.68026413
il est tout à fait possible de calculer manuellement la prédiction recherchée, par exemple :
> agem <- median(hers$age[hers$diabetes==0],
+
na.rm=TRUE)
> t(coef(mod1)) %*% c(1, 1, agem, 0, 22)
Call:
lm(formula = fm, data = cystic)
Residuals:
Min
1Q Median
3Q
-39.535 -11.904
4.259 15.614
[,1]
[1,] 93.03309
1
93.03309
> predict(mod1, data.frame(exercise=0, age=agem,
+
drinkany=0, BMI=22))
Residual standard error: 22.73 on 20 degrees of freedom
Multiple R-squared: 0.6148,
Adjusted R-squared: 0.5378
F-statistic: 7.981 on 4 and 20 DF, p-value: 0.0005139
1
93.98353
Par rapport à un patient ne faisant pas d'exercice, on voit
que la différence dans les taux de glucose prédit est de
0.950441, ce qui correspond bien à l'interprétation du coefficient de régression partiel pour le facteur exercise.
Pour produire une surface de réponse, il est nécessaire de
construire une grille de valeurs pour les variables d'intérêt,
ici age et BMI. Pour les deux autres prédicteurs, on assumera
des valeurs fixées.
> pd <- expand.grid(age=seq(44, 79, length=100),
+
BMI=seq(15.21, 54.13, length=100))
> pd$exercise <- rep(1, nrow(pd))
> pd$drinkany <- rep(0, nrow(pd))
> pd$pred <- predict(mod1, pd)
> head(pd)
39.
age
44.00000
44.35354
44.70707
45.06061
45.41414
45.76768
Max
35.334
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 62.4448
53.2431
1.173 0.254647
Weight
1.7480
0.3797
4.603 0.000172
BMP
-1.3644
0.5633 -2.422 0.025062
FEV
1.5480
0.5771
2.682 0.014317
RV
0.1275
0.0832
1.532 0.141135
Ou alors, et c'est plus simple lorsqu'on s'intéresse à
plusieurs prédictions, on utilise la fonction predict() :
> predict(mod1, data.frame(exercise=1, age=agem,
+
drinkany=0, BMI=22))
1
2
3
4
5
6
cystic <- read.table("../pub/CYSTIC.DAT", header=TRUE)
fm <- PEmax ~ Weight + BMP + FEV + RV
mod1 <- lm(fm, data=cystic)
summary(mod1)
BMI exercise drinkany
pred
15.21
1
0 88.24950
15.21
1
0 88.27197
15.21
1
0 88.29443
15.21
1
0 88.31690
15.21
1
0 88.33937
15.21
1
0 88.36184
Les résultats indiquent que le poids (Weight), ainsi que deux
autres prédicteurs (BMP et FEV) semblent significativement
associés aux variations de PEmax.
Concernant l'identification des observations avec effet
levier, on peut utiliser le critère β„Žπ‘– > τΊΎ(π‘˜ + τΊ½)/𝑛, avec π‘˜ = τ»
et 𝑛 = τΊΎτ», soit
> hcrit <- 2*(5+1)/25
> any(hatvalues(mod1) > hcrit)
[1] FALSE
A priori, aucune observation n'exerce d'effet levier.
Concernant la qualité d'ajustement du modèle, le coefficient
de détermination ajusté vaut 0.538 (soit environ 54 % de
la variabilité de PEmax expliquée par les facteurs pris en
compte dans le modèle).
On augmente le modèle précédent avec les variables Age et
Sex :
> mod2 <- update(mod1, . ~ . + Age + Sex)
> summary(mod2)
Call:
lm(formula = PEmax ~ Weight + BMP + FEV + RV + Age + Sex, data = cystic)
Residuals:
Min
1Q Median
3Q
Max
-37.976 -10.632
2.201 15.952 38.151
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 100.01652
89.98226
1.112
0.2810
Weight
2.37709
1.20397
1.974
0.0639
BMP
-1.71963
0.86957 -1.978
0.0635
FEV
1.49478
0.71844
2.081
0.0520
RV
0.11140
0.09244
1.205
0.2438
Age
-1.95096
3.54021 -0.551
0.5884
Sex
0.57570
11.63143
0.049
0.9611
pred
age
Residual standard error: 23.76 on 18 degrees of freedom
Multiple R-squared: 0.6213,
Adjusted R-squared: 0.4951
F-statistic: 4.922 on 6 and 18 DF, p-value: 0.003837
BMI
38. On charge les données de la manière habituelle pour les
données texte avec séprateur de champ de type tabulation.
Plus rien ne semble significatif ! Le problème vient essentiellement du nombre de paramètres dans le modèle : 6
14
prédicteurs (+ l'intercept) pour 25 observations.
40. Les données sont stockées au format texte, avec séparation
de type tabulation. Les données manquantes sont codées
avec un ”.”.
> babies <- read.table("../pub/babies.txt",
+
header=TRUE, na.string=".")
> babies$baby <- factor(babies$baby)
> babies$loginsp <- log(babies$inspirat)
> range(table(babies$baby))
[1]
8 47
Le modèle d'ANOVA incorporant entre 8 et 47 mesures
répétées par sujet s'écrit :
> aov1 <- aov(loginsp ~ maxp + Error(baby),
+
data=babies[complete.cases(babies),])
> summary(aov1)
Error: baby
Df Sum Sq Mean Sq F value Pr(>F)
maxp
1 1.829
1.829
1.559 0.223
Residuals 25 29.328
1.173
Error: Within
Df Sum Sq Mean Sq F value Pr(>F)
maxp
1 32.93
32.93
197 <2e-16
Residuals 523 87.44
0.17
L'effet de la pression maximale est largement significatif.
Notons que l'analyse a été effectuée sur les cas complets.
Comparons avec une approche par modèle à effet aléatoire :
> library(nlme)
> mod1 <- lme(loginsp ~ maxp, data=babies,
+
random= ~ 1 | baby, na.action=na.omit)
> anova(mod1)
(Intercept)
maxp
numDF denDF F-value p-value
1
523 726.9259 <.0001
1
523 190.2486 <.0001
On obtient donc sensiblement les mêmes résultats. Il est
intéressant de regarder ce que ce dernier modèle fournit
comme prédictions. Comme on peut le voir dans la figure
suivante, les données individuelles sont assez ”bruitées” et
il est difficile d'estimer la pente commune à vue d'Ε“il. Cela
devient par contre beaucoup plus évident lorsque l'on affiche les prédictions individuelles. On peut utiliser
> qqmath(~ ranef(mod1))
pour visualiser la distribution des effets aléatoires (termes
d'intercept propres à chaque sujet) sous forme de Q-Q plot.
3
Fitted
loginsp
2.5
2
1
2.0
1.5
1.0
0
10
20
30
maxp
40
10
20
30
40
maxp
15

Documents pareils