C. Huber

Transcription

C. Huber
Université Paris V, René Descartes
UFR Biomédicale
45, rue des Saints-Pères
75 006 Paris
Cours de Modélisation Biostatistique en Splus.
C. Huber
1
Modélisation Statistique en Splus.
Chapitre 1 : Introduction
1 Les objets en Splus
2 Les quatre modes de données en Splus
3 Comment obtenir des données dans l'environnement Splus
a Ordre d'affectation
b Fonction scan()
Exemple 1
c Importation d'un fichier existant
Exemple 2 (mantel.rat)
d Génération systématique de nombres
Génération de nombres au hasard
4 Organisation des données en Splus
a Vecteurs
b Matrices
Exemple 3
c Listes
Exemple 4
d Data frames
5 Opérateurs en Splus
a Opérateurs arithmétiques
Exemple 5
b Autres opérateurs
Exemple 6
c Extraction de sous-ensembles
Exemples 7
6 Travailler avec les data frames
Exemple 8 (coagulation du sang)
Exemple 9 (dialyse : fichier kidney)
Exemple 10 (le même, avec factor)
Création automatique d'une variable de type catégoriel
Création manuelle d'une variable de type catégoriel
7 Ordonner une variable ou tout un tableau
8 Graphes : la fonction plot
9 Remarques importantes
1
2
3
4
5
6
7
8
9
7
10
11
12
13
11
14
15
16
17
18
Chapitre 2 : Probabilités, Statistique Descriptive et Tests Elementaires
1 Lois de probabilité usuelles
2 Statistiques descriptives : summary, hist, stem
3 Quelques tests élémentaires : (t.test, var.test, wilcox.test)
Chapitre 3 : Tests d'adéquation
1 Evaluation graphique : cdf.compare
2 Le test du chi-deux : chisq.gof et chisq.test
2 Le test de Kolmogorov-Smirnov : ks.gof
C. Huber
19
21
26
32
34
43
2
Chapitre 4 : Régression linéaire
1 Régression multiple
Exemple 1 : données hospitalières
2 Procédure de détermination du modèle
3 Comment tester la validité du modèle
Exemple 2 : ANOVA1 (coagulation)
Exemple 3 : ANOVA2 (dialyse)
Chapitre 5 : Courbes ROC. Bootstrap
1 Définitions : sensibilité, spécificité, courbe ROC
2 Un exemple simple : insuffisance respiratoire
3 Un exemple fondé sur la régression logistique
4. Estimation de l'erreur grâce au bootstrap
a Définition de la technique bootstrap
b Exemple 1 : la médiane
c Exemple 2 : écart-type de l'aire sous la courbe ROC
Chapitre 6 : Modèles pour les durées de survie censurées
1 Notions fondamentales
2 Estimation non-paramétrique de la fonction de survie
3 Modèle de Cox à hasards proportionnels
4 Examen des résidus
5 Exemple d'utilisation du modèle de Cox (dialyse)
Chapitre 7 : Régression logistique
1 Les lois exponentielles
2 Construction du modèle linéaire généralisé
3 La régression logistique
4 Comment tester si le modèle est convenable ?
5 Le modèle de régression logistique général
6 Exemple 1 (données sur la grippe)
7 Exemple 2 (maladies coronariennes)
C. Huber
46
47
49
65
69
70
72
75
76
77
79
80
81
82
85
86
87
88
92
Introduction
1
Chapitre 1
Introduction : Mise en route de SPLUS.
Notions de base.
SPLUS n'est pas un simple logiciel de statistique comme MINITAB, SPSS ou SAS. C'est plutôt
un environnement de programmation, intermédiaire entre un langage et un logiciel : il a la
puissance d'un langage de bas niveau en même temps que la facilité d'emploi d'un logiciel de
statistique de haut niveau. Il a aussi des capacités exploratoires et graphiques de très bonne
qualité.
Possibilités d'extensions : on peut ajouter à Splus des fonctions en Splus et adapter le langage
exactement à ce qu'on désire faire. On peut aussi écrire du code en Fortran et en C et
l'incorporer comme une fonction dans Splus.
SPLUS dispose aussi d'une excellente documentation en ligne : utiliser l'ordre
? nom_de_la_fonction
On obtient alors l'information complète au sujet de la syntaxe de cette fonction et de ce qu'on
peut en attendre.
Attention : Splus fait la distinction entre les majuscules et les minuscules.
1. Les Objets en splus :
Tout en SPLUS est un "objet" : "object" en anglais : scalaires (scalar), vecteurs (vector),
tableaux (matrix, data.frame, list, array) et fonctions (function). Les opérateurs en Splus
sont performants : ils peuvent s'appliquer à différents types d'objets.
Les objets peuvent être créés et stockés de façon permanente sur le disque, ou bien créés
temporairement à l'intérieur de fonctions. Dans le second cas, ils disparaissent une fois que
la fonction a été exécutée. Le répertoire dans lequel les objets permanents sont stockés
s'appelle “_Data”. Pour supprimer des objets : remove("nom_de_l'objet"). Faire ?remove
pour connaître les autres options. Pour supprimer tous les objets d'un répertoire:
remove(objects(patt="*"))
l'astérisque représentant n'importe quelle suite de caractères.
Chaque fois qu'on se réfère à un objet de Splus, Splus le recherche dans l'ordre :
1.
Dans l'environnement local (désigné par "frame"), en général à l'intérieur
d'une fonction ou d'une base de données (un data.frame),
2.
dans le répertoire _Data,
3.
dans les répertoires du système et dans les bibliothèques (libraries).
Pour connaître les divers niveaux de cette recherche, taper la commande:
C. Huber
Introduction
2
search()
Attention : Si vous créez un objet qui a le même nom qu'un objet déjà défini dans Splus,
c'est VOTRE objet qui se substitue à l'objet Splus. Si par exemple vous utilisez c, vous
perdrez la fonction c de Splus (combiner). De même pour t qui en Splus désigne le test de
Student. Les commentaires sont précédés du signe #. Ils ne sont pas reconnus comme des
commandes par Splus.
2. Les quatre modes de données en Splus :
Il y a quatre modes de base pour les données :
numeric
(numérique) is.numeric(x) teste si x est numérique
(T ou F : 1=True, 0=False).
character
(alpha)
is.character(x) teste si x est alpha.
logical
(logique)
is.logical(x) teste si x est logique.
complex
(complexe)
as.numeric(x) convertit x en numérique.
3. Comment saisir des données dans l'environnement splus?
a Ordre d'affectation <- et combinaison c :
x <- 5
# affecte à x la valeur 5. (en abrégé : x _5).
Le signe # précède les commentaires, non lus par Splus.
Conséquence de la notation abrégée : on ne peut pas utiliser le signe "_" à l'intérieur d'un
nom. Seulement des points. Par exemple "fonction.base" est une nom valable.
y <- c(5,7,9,3)
# affecte à y le vecteur (5,7,9,3). c veut dire "combiner" .
b fonction scan() :
Si les données sont en plus grand nombre, on a intérêt à utiliser la fonction scan(). Voici un
exemple :
z<-scan( )
5 7 9 3
# passer à la ligne après scan()
# taper la suite des valeurs séparées par des blancs
# passer à la ligne après la dernière entrée:
# une entrée vide signale à Splus la fin de scan().
Exemple 1 :
20 rats ont reçu un produit censé induire des tumeurs cancéreuses au bout d'un certain
temps. 10 ont été tirés au sort parmi les 20 et ont reçu un traitement chimiothérapique, les
10 autres non. Les durées observées, en jours, sont les suivantes:
Traités par la chimiothérapie x 101
Non traités
y 74
91 92 98 77 89 88 79 96 81
84 66 32 73 80 18 72 82 69
On utilisera c() pour définir x et scan() pour définir y.
C. Huber
Introduction
3
c Importation d'un fichier existant :
Si on dispose déjà des données dans un fichier WORD ou WORDPAD , on peut utiliser
l'option IMPORT du menu fichier en SPLUS.
Exemple 2 :
On veut créer un objet de type data.frame en SPLUS à partir de données qui sont dans un
fichier texte mantel.txt sur la disquette a:.
Il s'agit, pour chaque ligne, des durées jusqu'à l'apparition de tumeurs chez trois rats d'une
même portée, un traité et deux témoins. Les 50 premières portées sont constituées
uniquement de femelles et les cinquante suivantes uniquement de mâles. Chez certains rats,
la tumeur n'est jamais apparue : ils ont alors un 0 dans la colonne "cens".
Le fichier a 100 lignes :
id
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
dur1
101
91
104
91
104
98
77
91
89
104
88
96
104
79
96
61
82
63
70
104
89
104
91
104
39
104
103
65
93
86
85
104
104
95
104
104
81
104
67
92
104
104
104
63
104
104
87
cens1
0
0
0
0
0
0
0
0
0
0
1
0
1
0
1
0
0
0
1
0
1
0
0
0
1
0
1
0
0
0
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
dur2
49
104
102
104
104
62
97
98
104
104
96
71
94
104
104
88
77
104
104
104
91
80
70
104
45
53
69
104
104
104
72
100
63
104
104
104
104
93
104
98
104
89
104
32
83
98
104
cens2
1
0
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
C. Huber
dur3
104
102
104
102
104
77
79
76
104
98
104
91
77
99
104
85
104
102
77
102
90
92
92
101
50
102
91
91
103
75
104
102
104
95
74
102
69
80
68
83
104
89
102
51
40
78
104
cens3
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
0
0
0
0
0
1
0
0
0
0
0
1
0
0
Introduction
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
104
104
87
89
104
78
104
104
90
86
91
34
104
76
23
103
104
102
87
80
104
45
104
94
67
104
104
104
104
76
104
80
51
72
102
73
88
92
67
104
104
55
81
49
94
89
104
88
104
103
104
104
92
0
0
0
0
0
0
0
0
0
1
0
1
0
0
0
1
0
1
0
1
0
1
0
1
0
0
0
0
0
0
0
1
0
1
0
1
0
1
0
0
0
0
0
0
0
1
0
0
0
1
0
0
0
104
104
104
104
104
104
91
81
104
55
104
104
104
87
104
73
71
104
51
104
83
79
104
104
84
104
104
101
94
84
103
81
104
95
98
104
54
104
84
98
104
104
82
83
104
104
89
79
69
91
75
104
104
0
0
0
0
0
0
0
1
0
1
0
0
0
0
0
1
1
0
0
0
0
0
0
0
0
0
0
1
0
1
0
1
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
1
0
0
4
102
104
94
104
102
104
102
64
55
94
102
54
102
74
102
84
90
80
102
73
102
104
96
104
94
104
99
94
102
78
102
76
91
104
102
66
39
102
54
73
87
104
102
77
102
104
77
99
102
104
64
79
102
0
0
0
0
0
0
0
1
0
0
0
1
0
0
0
1
0
0
0
0
0
0
0
0
0
0
0
0
0
1
0
0
0
0
0
1
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
1
0
Cliquer sur le menu file de SPLUS, choisir Import data from file et, dans la fenêtre de
dialogue qui apparaît, aller d’abord dans l’onglet option pour y choisir : en face de name
row : 1, en face de Delimiters : un blanc, si le fichier a pour première ligne les noms des
colonnes et pour séparateurs des blancs comme dans l’exemple ci-dessus mantel.txt. Aller
ensuite dans l’onglet Specs et mettre le nom qu’on veut donner au fichier que l'on veut
importer (from a:\) dans la case intitulée File name. Puis cliquer sur ouvrir.
On a ainsi créé un data.frame qu'on peut observer grâce à l'object.browser qui s'ouvre
automatiquement. Si on veut changer le nom d'une variable, double-cliquer sur ce nom et
taper le nouveau nom.
d Génération systématique de nombres : rep (répétition) et seq (séquences)
C. Huber
Introduction
5
rep :
La fonction rep est très utile : son premier argument est le contenu et le second le
nombre des répétitions. On vérifiera que
rep(0,10)
# répète 0, 10 fois.
rep(c(1,2,3),4)
# répète 4 fois la séquence 1,2,3.
rep(c(1,2,3),c(4,2,7)) # répète 1, 4 fois, 2, 2fois et 3, 7 fois.
seq :
La fonction seq a pour arguments : le début, la fin et le pas de la séquence.
s1 <- seq(from=-4,to=5, by=.3)
s1 # pour afficher s1.
[1] -4.0 -3.7 -3.4 -3.1 -2.8 -2.5 -2.2 -1.9 -1.6 -1.3 -1.0 -0.7 -0.4 -0.1
[15] 0.2 0.5 0.8 1.1 1.4 1.7 2.0 2.3 2.6 2.9 3.2 3.5 3.8 4.1
[29] 4.4 4.7 5.0
Quelques autres façons d'obtenir une séquence :
1:15
# donne tous les entiers de 1 à 15
letters[1:3] # donne les minuscules a b c.
LETTERS[3:6] # donne les majuscules C D E.
e Génération de nombres au hasard :
La génération de nombres au hasard est utile dans les simulations.
r (comme random) suivi du nom d'une loi engendre des variables aléatoires indépendantes
qui suivent cette loi : normale, béta, Poisson, etc.. . Pour en avoir la liste, taper ?
probability.
Exemple :
x<- rnorm(100)
# génère 100 variables normales N(0,1) indépendantes
# et les stocke dans le vecteur x
rnorm(100) # affiche 100 nombres aléatoires à l'écran,
# mais ne les sauvegarde pas si on a oublié de les affecter à
# une variable x. Cependant, on peut rattraper ça en faisant
x<-.Last.value
#
.Last.value contient le vecteur des 100 valeurs :
#
elles seront stockées dans x.
Autres nombres aléatoires :
runif(50, min=2, max=10) # engendre 50 valeurs distribuées uniformément sur (2,10)
rexp(10, rate=2)
# engendre 10 variables exponentielles de moyenne ½.
4 Organisation des données en Splus :
a vecteurs (vector) (déjà introduits ci-dessus : exemple c(1,5,6)).
C. Huber
Introduction
6
b matrices (matrix) : matrix(data, nrows= , ncol= , byrow = ).
Exemples 3 :
y <- matrix(1:20,nrow=5)
# 1:20 désigne la suite des nombres entiers
# de 1 à 20,
# dont l'autre écriture est
# c(1,2,3,…,20) (sans pointillés).
y
# affichage de y
[,1] [,2] [,3] [,4]
[1,] 1 6 11 16
[2,] 2 7 12 17
[3,] 3 8 13 18
[4,] 4 9 14 19
[5,] 5 10 15 20
Remarque :
SPLUS stocke les matrices en colonnes. Pour le forcer à les stocker par ligne,
utiliser byrow=T.
y <- matrix(1:20,nrow=5,byrow=T)
y
[,1] [,2] [,3] [,4]
[1,] 1 2 3 4
[2,] 5 6 7 8
[3,] 9 10 11 12
[4,] 13 14 15 16
[5,] 17 18 19 20
Pour créer des matrices on peut utiliser des abréviations comme suit :
# Nous avons donné une seule valeur numérique.
# Elle est répétée autant de fois qu'il le faut pour
# remplir la matrice.
[,1] [,2] [,3] [,4]
[1,] 1 1 1 1
[2,] 1 1 1 1
[3,] 1 1 1 1
[4,] 1 1 1 1
[5,] 1 1 1 1
matrix(1,5,4)
Si on donne moins de valeurs qu'il n'en faut, SPLUS utilisera les mêmes en les répétant
jusqu'à ce que la matrice soit remplie.
matrix(c(1,2,3),5,4,byrow=T)
[,1] [,2] [,3] [,4]
C. Huber
Introduction
7
[1,] 1 2 3 1
[2,] 2 3 1 2
[3,] 3 1 2 3
[4,] 1 2 3 1
[5,] 2 3 1 2
Warning messages:
Replacement length not a multiple of number of elements to replace in:
data[1:ll] <- old
On peut utiliser des nombres aléatoires pour remplir une matrice :
y<-matrix(rnorm(10),ncol=5)
y
[,1]
[,2]
[,3]
[,4]
[,5]
[1,] -0.6349455 0.5476715 -0.2105945 -0.5090833 -0.02591333
[2,] -0.2517527 -0.2930510 1.4006490 0.9879103 -1.04293702
Pour accéder aux éléments d'une matrice ou d'une sous-matrice :
y
[,1]
[,2]
[,3]
[,4]
[,5]
[1,] -0.6349455 0.5476715 -0.2105945 -0.5090833 -0.02591333
[2,] -0.2517527 -0.2930510 1.4006490 0.9879103 -1.04293702
y[1,3]
[1] -0.2105945
# accès à l'élément
y[1:2,c(1,3,5)]
# accès à une sous-matrice.
[,1]
[,2]
[,3]
[1,] -0.6349455 -0.2105945 -0.02591333
[2,] -0.2517527 1.4006490 -1.04293702
x<-y[2,]
# accès à la ligne 2
x
[1] -0.2517527 -0.2930510 1.4006490 0.9879103 -1.0429370
x<-y[,4]
# accès à la colonne 4
x
[1] -0.5090833 0.9879103
c Listes (list)
Les matrices sont limitées par le fait qu'elles ne contiennent que des entrées du même type:
soit numériques, soit alpha. De plus la longueur de toutes les colonnes et de toutes les lignes
sont fixées. Lorsqu'on a des données de longueur variable et de différents types, il faut utiliser
C. Huber
Introduction
8
une list. En utilisant une liste (list), on peut transférer des données d'un objet d'une fonction à
une autre. En règle générale, les résultats de fonctions en Splus sont des lists.
Exemple 4 :
mylist<-list(a=c(1,2,3),b=c("a","b"))
mylist$a # accès à la 1re entrée.
[1] 1 2 3
mylist$b
# accès à la 2ème entrée.
[1] "a" "b"
d data.frame :
C'est un hybride entre une matrice et une liste: toutes les colonnes doivent être de la même
longueur, mais elles peuvent être de différents modes. Comme pour les matrices, on peut
utiliser les indices.
Une matrice peut être convertie en un data.frame en utilisant la fonction data.frame :
d.frame<-data.frame(y)
# où y est une matrice.
Pour définir un data.frame, on peut aussi importer un fichier dans Splus comme expliqué
plus haut. On peut pour cela employer la fonction read.table.
Exemple :
Si le fichier fich.txt se trouve dans le répertoire z de la disquette a:, et a 3 colonnes
nommées A, B et C, on fera:
fichier<-read.table("a:\\z\\fich.txt",col.names=c("A","B","C"))
Quand on lit un fichier texte pour en faire un objet de type data.frame en Splus, on peut
avoir une ligne qui contient les noms des colonnes comme on l'a fait dans l'exemple 2
mantel.rat, devenu mantel en Splus. Cependant, pour pouvoir être capable de se référer aux
variables par leurs noms, on doit utiliser:
attach(mantel)
On aura pu auparavant s'assurer du nom donné au nouveau data.frame par SPLUS en tapant
objects()
et utiliser ce nom dans la fonction ‘attach’. Ne pas attacher un data.frame avant de lui avoir
ajouté toutes les variables désirées. Si par exemple on veut rajouter le sexe à mantel.txt, on
crée la colonne sex, qui donne 0 pour une femelle et 1 pour un mâle, et on appelle mantels
le data.frame mantel augmenté de cette information:
sex<-c(rep(0,50),rep(1,50))
mantels<-data.frame(mantel,sex)
C. Huber
Introduction
9
Pour revenir à l'état précédent, annuler attach et ne plus avoir le fichier mante.txt au
premier plan, on doit taper:
detach("mantel")
Attention aux guillemets, qu'il n'y a pas dans attach().
5 Opérateurs en SPLUS :
a Opérateurs arithmétiques:
Les opérations usuelles : + - * / ^ (addition, soustraction, multiplication, division,
exponentiation). Quand on applique ces opérateurs à des matrices a and b de même
dimension, ces opérations ont lieu élément par élément.
Exemple 5 :
a<-matrix(1:12,3,4,byrow=T)
a
[,1] [,2] [,3] [,4]
[1,] 1 2 3 4
[2,] 5 6 7 8
[3,] 9 10 11 12
b<-matrix(2,3,4)
b
[,1] [,2] [,3] [,4]
[1,] 2 2 2 2
[2,] 2 2 2 2
[3,] 2 2 2 2
a^b
[,1] [,2] [,3] [,4]
[1,] 1 4 9 16
[2,] 25 36 49 64
[3,] 81 100 121 144
a*b
[,1] [,2] [,3] [,4]
[1,] 2 4 6 8
[2,] 10 12 14 16
[3,] 18 20 22 24
b Autres opérateurs :
division entière :
division modulo :
5%/%3
[1] 1
%/%:
%% :
C. Huber
Introduction
10
5/3
[1] 1.666667
5%%3
[1] 2
#
5:3 = 1 reste 2
Multiplication des matrices : % * %
Reprenons les matrices a et b. Transposons b. Nous pouvons maintenant multiplier a et b en
tant que matrices :
# affiche la transposée de b
t(b)
[,1] [,2] [,3]
[1,] 2 2 2
[2,] 2 2 2
[3,] 2 2 2
[4,] 2 2 2
a % * % t(b)
# effectue le produit matriciel de a et de t(b)
[,1] [,2] [,3]
[1,] 20 20 20
[2,] 52 52 52
[3,] 84 84 84
Opérateurs logiques : ><
>=
<=
==
Différent de : ><
>=
<=
égal : == (deux signes =)
Notons qu'en SPLUS ‘=’ n'existe pas. L'affectation se fait par ‘<-‘ , et la condition d'égalité
par double égale.. Quand ces opérateurs sont appliqués à des vecteurs ou à des matrices,
la comparaison a lieu élément par élément.
Exemple 6 :
x<-runif(5)
x
[1] 0.3860282 0.7095107 0.2220855 0.8128106 0.2053301
y<-runif(5)
x>y
[1] T T T T F
Extraction de sous-ensembles des données (indices) :
L'opérateur subscript est une parenthèse carrée : []. C'est la différence entre l'appel à une
fonction, gérée par des parenthèses (), et les tableaux. Pour une matrice ou un data.frame
l'indexation est :
x[e1,e2]
C. Huber
Introduction
où e1 et e2 sont des expressions numériques ou logiques.
Exemples 7:
1.
x<-matrix(floor(runif(15,0,10)),3,5) # floor(x) = le plus grand entier <= x.
x
# runif(n,a,b) donne n nombres aléatoires de (a,b).
[,1] [,2] [,3] [,4] [,5]
[1,] 2 0 5 7 5
[2,] 3 7 6 8 6
[3,] 6 4 8 6 3
x[3,5]
[1] 3
x[1:3,5]
# sélectionne un élément d'une matrice.
# sélectionne une sous-matrice.La sous-matrice a une dimension
# nulle (NULL).
[1] 5 6 3
x[c(1,3),5] # sélectionne une sous-matrice de dimension nulle.
[1] 5 3
x[1,]
# sélectionne la ligne1. Pas d'attribut de dimension.
[1] 2 0 5 7 5
x[,3]
# sélectionne la colonne 3. Pas d'attribut de dimension.
[1] 5 6 8
x[c(1,3),3:5] # sélectionne une sous-matrice. La dimension de la sous-matrice
# est c(2,3)
[,1] [,2] [,3]
[1,] 5 7 5
[2,] 8 6 3
x[,2]==4
# pour savoir quelles sont les lignes qui ont un 4 en colonne 2 :
[1] F F T
# la seule ligne qui est dans ce cas est la ligne 3.
y<-x[x[,2]==4,] # sélectionne la ligne 3 et la met dans y.
y
[1] 6 4 8 6 3
2.
x<-2*(1:10)
x
# on veut choisir les éléments n° 1,5,6,10 du vecteur x:
[1] 2 4 6 8 10 12 14 16 18 20
choix<-c(1,0,0,0,1,1,0,0,0,1)
x[choix]
[1] 1 1 1 1
# ne marche pas !
C. Huber
11
Introduction
> x[as.logical(choix)] # marche !
[1] 2 10 12 20
Essayer choix<-c(1,5,6,10) : obtient on le résultat voulu?
6 Travailler avec les data.frame :
Exemple 8 :
Considérons l'expérience suivante (Box et al (1978)):
Les durées de coagulation sanguine pour quatre régimes différents A, B, C, and D ont été
observés. Les résultats sont les suivants :
A
62
60
63
59
Régime
B C
63 68
67 66
71 71
64 67
65 68
66 68
D
56
62
60
61
63
64
63
59
Comment construire le data.frame correspondant ? On a besoin d'autant de lignes qu'il y a de
sujets dans l'expérience, et dans chaque ligne, du régime et de la durée de coagulation.
Cela donne ici :
coag <- scan()
# début de l'entrée des données.
62 60 63 59
63 67 71 64 65 66
68 66 71 67 68 68
56 62 60 61 63 64 63 59
# une ligne vide prévient Splus de la fin des entrées.
# pour créer les noms des traitements (régime) :
regime<- factor(rep(LETTERS[1:4],c(4,6,6,8)))
regime
# AAAABBBBBBCCCCCCDDDDDDDD
coag.df <- data.frame(regime,coag)
# crée un data.frame coag.df
coag.df
# affiche le data.frame à l'écran.
regime coag
1
2
3
4
5
A
A
A
A
B
62
60
63
59
63
C. Huber
12
Introduction
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
B
B
B
B
B
C
C
C
C
C
C
D
D
D
D
D
D
D
D
13
67
71
64
65
66
68
66
71
67
68
68
56
62
60
61
63
64
63
59
On a ainsi obtenu les données sous la forme dont on a besoin pour une analyse ultérieure.
Exemple 9 :
Supposons que vous ayez un fichier texte nommé kidney.txt dans le répertoire a:\data. Il s'agit
de données sur la récurrence d'infections au point d'insertion d'un cathéter, pour des patients
ayant une affection rénale et qui utilisent un équipement de dialyse portable. Les cathéters
peuvent être ôtés pour des raisons autres qu'une infection, et, dans ce cas, la durée est dite
censurée. Chaque patient a exactement deux observations. (McGilchrist and Aisbett,
Biometrics 47, 461-66, 1991).
Les variables sont : patient, infectime (durée jusqu'à l'infection), cens (censuré (0) ou pas (1)),
age, sex (1=masculin, 2=féminin), disease(le type de la maladie) (0=Glomerulo Nephritis,
1=Acute Nephritis, 2=Polycystic Kidney Disease, 3=Other), cens : 1 = l'infection a eu lieu ; 0
= retiré de l'étude ou mort avant l'infection.
Le voici :
patient
1
1
2
2
3
3
4
4
5
5
6
6
7
7
8
8
9
9
10
10
11
infectime
8
16
23
13
22
28
447
318
30
12
24
245
7
9
511
30
53
196
15
154
7
cens
1
1
1
0
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
age
28
28
48
48
32
32
31
32
10
10
16
17
51
51
55
56
69
69
51
52
44
C. Huber
sex
1
1
2
2
1
1
2
2
1
1
2
2
1
1
2
2
2
2
1
1
2
disease
3
3
0
0
3
3
3
3
3
3
3
3
0
0
0
0
1
1
0
0
1
frailty
2.3
2.3
1.9
1.9
1.2
1.2
0.5
0.5
1.5
1.5
1.1
1.1
3.0
3.0
0.5
0.5
0.7
0.7
0.4
0.4
0.6
Introduction
11
12
12
13
13
14
14
15
15
16
16
17
17
18
18
19
19
20
20
21
21
22
22
23
23
24
24
25
25
26
26
27
27
28
28
29
29
30
30
31
31
32
32
33
33
34
34
35
35
36
36
37
37
38
38
333
141
8
96
38
149
70
536
25
17
4
185
177
292
114
22
159
15
108
152
562
402
24
13
66
39
46
12
40
113
201
132
156
34
30
2
25
130
26
27
58
5
43
152
30
190
5
119
8
54
16
6
78
63
8
1
1
0
1
1
0
0
1
0
1
0
1
1
1
1
0
0
1
0
1
1
1
0
1
1
1
0
1
1
0
1
1
1
1
1
1
1
1
1
1
1
0
1
1
1
1
0
1
1
0
0
0
1
1
0
44
34
34
35
35
42
42
17
17
60
60
60
60
43
44
53
53
44
44
46
47
30
30
62
63
42
43
43
43
57
58
10
10
52
52
53
53
54
54
56
56
50
51
57
57
44
45
22
22
42
42
52
52
60
60
14
2
2
2
2
2
2
2
2
2
1
1
2
2
2
2
2
2
2
2
1
1
2
2
2
2
2
2
1
1
2
2
2
2
2
2
1
1
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
1
1
1
3
3
1
1
1
1
3
3
1
1
3
3
3
3
0
0
3
3
2
2
3
3
1
1
1
1
1
1
1
1
0
0
1
1
0
0
0
0
1
1
1
1
2
2
0
0
3
3
3
3
2
2
2
2
0.6
1.2
1.2
1.4
1.4
0.4
0.4
0.4
0.4
1.1
1.1
0.8
0.8
0.8
0.8
0.5
0.5
1.3
1.3
0.2
0.2
0.6
0.6
1.7
1.7
1.0
1.0
0.7
0.7
0.5
0.5
1.1
1.1
1.8
1.8
1.5
1.5
1.5
1.5
1.7
1.7
1.3
1.3
2.9
2.9
0.7
0.7
2.2
2.2
0.7
0.7
2.1
2.1
1.2
1.2
En utilisant “import” comme indiqué auparavant, on crée un data.frame appelé “kidney”. On
peut vouloir créer d'abord de nouvelles variables à ajouter au tableau, dont on pourrait avoir
besoin plus tard pour l'analyse. Vous pouvez avoir l'intention d'ajuster aux données un modèle
de Cox à hasards proportionnels. Dans ce modèle, la durée de survie est la durée jusqu'à
l'infection, l'indicateur de censure est cens, et les variables explicatives sont le sexe, l'âge et le
type de la maladie. Cependant, le type de la maladie est une variable catégorielle.
Aussi est il nécessaire, soit d'utiliser la commande as.factor (première méthode ci-dessous),
soit de définir quatre variables indicatrices (deuxième méthode, manuelle et plus longue).
C. Huber
Introduction
15
a. Création automatique d'une variable de type catégoriel : la fonction as.factor :
Quand une variable catégorielle est introduite comme variable explicative dans une régression,
ses valeurs ne signifient rien. Si elle prend k valeurs distinctes, on doit faire entrer dans
l'équation de régression k-1 variables. Dans l'exemple 9 ci-dessus, la variable disease prend 4
valeurs : 0-3, et dis1,dis2,dis3 pourrait être un choix de variables binaires pour remplacer dis.
Cela peut être fait de manière automatique en SPLUS par la commande as.factor :
attach(kidney)
dis.fac<-as.factor(disease)
puis en l'ajoutant au data.frame kidney :
kidney1<-cbind(kidney,dis.fac)
attach(kidney1)
La nouvelle variable s'est vu attribuer le nom X2 dans le fichier étendu kidney1. On doit
changer le nom de cette colonne pour le rendre plus informatif :
dimnames(kidney1)[[2]]
# donne les noms des variables de kidney1
dimnames(kidney1)[[2]][8]<-"maladie"
b. Création manuelle d'une variable de type catégoriel (factor) :
Comme exemple de manipulation de data.frame, nous allons procéder de la deuxième
manière, qui explique ce que fait la fonction as.factor. Les nouvelles variables que nous créons
sont les suivantes :
Exemple 10 :
dis0 = 1 si disease = 0 et 0 sinon
dis1 = 1 si disease = 1 et 0 sinon
dis2 = 1 si disease = 2 et 0 sinon
dis3 = 1 si disease = 3 et 0 sinon
En fait on n'a besoin que de trois variables, puisque disease prend seulement 4 valeurs en tout :
0,1,2,3. Mais nous voulons trouver lesquelles il vaut mieux garder, aussi commençons nous
malgré tout avec les quatre.
Pour créer ces variables comme des colonnes dans une matrice 76x4 , faire :
nn<-nrow(kidney)
# nrow est la fonction qui compte le nombre des lignes
# d'une matrice ou d'un data.frame.
nn
C. Huber
Introduction
16
[1] 76
kidney[1,]
# affiche la première ligne de kidney
patient infectime cens age sex disease frailty
1
1
8
1
28
1
3
2.3
dis <- matrix(0,nn,4)
# crée une matrice pleine de zeros pour recevoir
# les variables dis0-dis4, ayant nn lignes.
for(i in 0:3){
# boucle iterative
dis[,i+1]<-kidney[,6]==i}
dis[1:4,]
#
affiche les lignes 1 à 4 de la nouvelle matrice
# [,1] [,2] [,3] [,4]
# [1,]
0
0
0
1
# [2,]
0
0
0
1
# [3,]
1
0
0
0
# [4,]
1
0
0
0
kidney[1:4,6]
# pour vérifier que nous avons construit
# les bonnes variables.
# [1] 3 3 0 0
dis <- as.data.frame(dis)
# transforme la matrice dis en un data.frame
dis[1:4,]
#
V1 V2 V3 V4
# 1 0 0 0 1
# 2 0 0 0 1
Il reste maintenant à changer les noms V1-V4 en dis0-dis3. On peut le faire en utilisant l'object
browser dont l'icône apparaît au haut de la fenêtre de commande. Dans le browser , cliquer sur
data.frame pour trouver dis, cliquer dessus, puis cliquer deux fois sur chaque en-tête de colonne
tour à tour pour le changer. Nous sommes maintenant prêts pour fusionner le data.frame kidney
avec le data.frame dis pour en faire un nouveau data.frame qu'on sauvegardera sous le nom
kidney1. Pour ce faire, on utilise la commande cbind, pour “column bind” :
kidney1<-cbind(kidney,dis)
kidney1[1:4,]
1
2
patient infectime cens age sex disease frailty dis0 dis1 dis2 dis3
1
8
1 28
1
3
2.3
0
0
0
1
1
16
1 28
1
3
2.3
0
0
0
1
Attachons maintenant le data.frame kidney1 de façon à avoir à disposition toutes les colonnes
avec leurs noms :
attach(kidney1)
7. Ordonner une variable ou tout un tableau (data.frame) : sort, order, rank
Pour ordonner une variable par ordre croissant, employer la commande sort:
Exemple:
C. Huber
Introduction
17
Quels sont les âges, par ordre croissant des patients atteints de maladie du rein du fichier
"kidney1" ?
attach(kidney1)
age.ord<-sort(age)
Essayer la commande order :
ord<-order(age)
ord
On vérifiera que order(age) donne la suite des identificateurs des sujets par ordre croissant de
leur âge, ce que l'on appelle : les anti-rangs.
Pour ordonner l'ensemble du tableau (data.frame) kidney1 par ordre croissant des âges, on va
donc les réorganiser en choisissant les lignes selon la suite ord = order(age) en faisant :
kidney1.ord<-kidney1[order(age),]
Vérifier le résultat en affichant kidney1.ord.
La commande rank donne les rangs des valeurs de la variable. Le vérifier sur
rg<-rank(age)
rg
8. Graphes : la fonction plot :
Supposons maintenant que nous voulions faire un dessin représentant une fonction, par
exemple f(x) = 1/((2()exp(-x2/2), entre –3 et +3. On fera
x<-seq(-3,+3,by=0.01)
y<-(1/sqrt(2*pi))*exp(-(x^2/2))
plot(x,y,type="l",xlab="valeurs de x",ylab="densite")
title(main="Densite de la loi normale standard")
win.graph() # pour ouvrir la fenêtre graphique
# si elle ne s'est pas ouverte automatiquement
L'argument type de la fonction plot permet de choisir :
- un nuage de points : type = "p" ,
- une ligne joignant les points (x,y) : type = "l" .
Le type de la ligne, se choisit par l'argument lty; on peut choisir
- une ligne pleine par lty=1,
- pointillée par lty = 2,
- etc...
L'argument col de plot définit la couleur. On peut choisir
- la couleur noire par col = 1,
- bleue par col = 2,
- etc..
L'argument lwd donne l'épaisseur du trait:
- lwd = 4 donne un trait plus épais que lwd = 2 ou 3.
Essayer de changer les valeurs de tous ces arguments sur le dessin précédent.
C. Huber
Introduction
18
Nous verrons au chapitre suivant que la densité normale est incorporée dans Splus et qu'on
peut donc obtenir directement le dessin de cette fonction (par dnorm()), ainsi que la fonction
de répartition correspondante (par pnorm()).
9. Remarques importantes :
Ces deux remarques concernent l'environnement Splus que l'on peut créer pour séparer les
dossiers relatifs aux différents travaux que l'on traite en utilisant ce logiciel.
Remarque 1 : Pour faire une icône de Splus spécifique d'un projet :
0. Créer le répertoire où on veut que soit stocké le travail effectué avec Splus.
Par exemple : C:\data\essai.
1. Créer un raccourci. du fichier exécutable de Splus , nommé Splus.exe, et situé dans le
répertoire C:\Program Files\splus45\cmd, et le mettre sur le bureau.
2. Le sélectionner, appuyer sur le bouton droit de la souris et choisir : propriétés.
3. Dans l'onglet raccourci il doit y avoir :
dans démarrer en : "C:\Program Files\splus45\cmd".
dans cible : "C:\Program Files\splus45\cmd\SPLUS.EXE"
Y ajouter S_PROJ="chemin_du_répertoire_de_travail"
(qui est dans notre exemple "C:\data\essai")
(Selon les cas il faut encadrer ou non ce chemin par des guillemets)
4.
5.
Aller dans changer d'icône pour changer la physionomie de l'icône.
Remarque 2 : Pour créer son propre répertoire de données :
Pour créer son propre répertoire de données (_data file) qui contiendra TOUS LES
OBJETS de Splus que l'on est amené à créer lors d'une session par des commandes dans
la fenêtre de commandes, on peut :
1. Ouvrir Window Explorer.
2. Dans le répertoire C:\ créer un nouveau dossier et lui donner un nom (le vôtre par
exemple).
3. Supposons que votre nom soit durand. Créer alors un nouveau dossier dans durand, qu'on
appellera _data.
4. Ouvrir SPLUS.
5. Taper les lignes suivantes qui créent la fonction .First puis l'appliquent :
.First <- function()
{ attach("c:\\durand\\_data", 1)
cat("On attache le repertoire durand
\n
en position 1") }
.First( )
Noter que l'ordre cat " " signifie imprimer à l'écran ce qui est entre guillemets, et que \n
est l'ordre de passage à la ligne.
C. Huber
Introduction
19
Comme plusieurs personnes utilisent le même ordinateur que vous, il faudra répéter
l'étape 5 chaque fois que vous ouvrirez une session de SPLUS. Si quelqu'un a enlevé ou
changé votre fonction .First, il faudra repartir de l'étape 1.
C. Huber
Probabilités et Statistique Descriptive
20
Chapitre 2.
Lois de Probabilité, Statistiques Descriptives et Tests
Elémentaires.
1. Lois de probabilité usuelles :
Il est très facile d'obtenir la fonction de répartition (cumulative probabilities), la
densité et les quantiles pour les lois usuelles discrètes et continues.
Pour savoir lesquelles sont disponibles, taper à l'invite > :
?probability
et SPLUS donnera une liste des distributions pour lesquelles la fonction de
répartition et la génération de variables sont disponibles. Cliquer sur le nom de
celle pour laquelle vous voulez obtenir de l'information. Si vous le faîtes sur la
loi normale (gaussienne) vous verrez, parmi d'autres explications :
dnorm(x, mean=0, sd=1)
pnorm(q, mean=0, sd=1)
qnorm(p, mean=0, sd=1)
rnorm(n, mean=0, sd=1)
densité en x de la loi normale N(0,1).
f. r. ( cumulative probability) de
la loi normale N(0,1) : P(X ≤ x).
quantile de N(0,1) pour la probabilité p.
génère n variables aléatoires (random)
normales N(0,1) indépendantes.
Traçons maintenant la courbe de la densité normale N(0,1) entre –4 et +4:
x<-c(-(.01)*(400:1),.01*(0:400))
length(x)
# donne le nombre des éléments du vecteur : 801
par(mfrow=c(2,1))
# dit à SPLUS de créer deux dessins par page
# sur 2 lignes.(c(1,2): sur 2 colonnes, l'essayer).
plot(x,dnorm(x,0,1),type="l",lty=1,lwd=3,col=2)
# trace la densité de la normale standard.
# Pour donner un titre en haut du graphe :
title(" densite normale standard ")
win.graph()
# pour ouvrir la fenêtre graphique
Traçons maintenant la densité normale N(2,3) entre –10 et +10:
# pour ajouter des noms (labels) aux axes :
plot(3*x+2,dnorm(3*x+2,2,3),type="l",lty=1,lwd=3,col=
3, xlab="x", ylab="f(x)")
title("densite normale de moyenne 2 et ecart-type 3")
C. Huber
Probabilités et Statistique Descriptive
21
0.3
0.2
0.1
0.0
dnorm(x, 0, 1)
0.4
densite normale standard
-4
-2
0
2
4
x
0.08
0.0
0.04
f(x)
0.12
densite normale de moyenne 2 et ecart-type 3
-10
-5
0
5
10
x
En fait, si on veut comparer ces deux densités, il faudrait les tracer sur le même graphique, ce
qu'on peut faire par la commande:
x<- seq(-4,+8,0.01)
y<-dnorm(x,0,1)
y
0.0
0.1
0.2
0.3
0.4
plot(x, y,type= "l",lty=1,lwd=4,col=2)
lines(x, dnorm(x,2,3),type= "l",lty=1,lwd=4,col=3)
# lines pour ajouter une courbe au même graphique.
-4
-2
0
2
4
6
8
x
# Pour obtenir la valeur de P[N(1,1) ≤ 4] faire
pnorm(4,1,1)
# [1] 0.9986501
# Pour obtenir le 95 percentile de N(m=2, σ=3) faire
qnorm(.95, 2,3)
#[1] 6.934561
Génération d'échantillons aléatoires :
On peut engendrer 100 variables normales N(1,2) (remarquer qu'ici 2 est l'écarttype (sd = standard deviation)) en faisant :
y <- rnorm(100,mean=1,sd=2)
C. Huber
Probabilités et Statistique Descriptive
22
2. Statistiques descriptives : summary, hist, stem :
Pour un échantillon y :
0
5
10
15
20
summary(y) # donne le minimum, maximum, les quantiles de y,..
hist(y)
# donne l'histogramme de y, tel quel ou selon un nombre
# fixé de classes : nclass = 3, ou selon des coupures
# fixées : breaks = c(min(y)-0.01, -2,1,0,1,2, max(y)
stem(y)
# donne le diagramme en tiges et feuilles.
-2
0
2
4
6
y
# pour obtenir un histogramme
# pour le vecteur y
# des 100 variables N(1,2), en 8 classes.
# Essayer avec les points de coupure : -3, -1, 1, 3.
hist(y,nclass=8)
Tiges et feuilles : stem
stem(y)
# pour obtenir un affichage en tige et feuilles.
Pour vérifier la signification, faire
sort(y)
N = 100 Median = 1.216908
Quartiles = -0.1948993, 2.517407
Decimal point is at the colon
-4 : 8
-3 : 631
-2 : 44430
-1 : 6543211
-0 : 87655322222210
0 : 012222244566778
1 : 012222455555567789
2 : 00111124444468889999
3 : 012355669
4 : 122369
5 : 79
L'histogramme ne révèle pas d'asymétrie (skewness). Le dessin de "tige et
feuilles" non plus. Les deux montrent cependant des queues plus lourdes que
celles de la normale standard.
C. Huber
Probabilités et Statistique Descriptive
23
Comparaison des quantiles de l'échantillon à ceux de la normale N(0,1) : qqnorm
2
0
-2
-4
quantiles d'un echantillon de 100 N(1,2)
4
On utilise ensuite un QQ plot pour vérifier si elles ressemblent à un échantillon
issu d'une distribution normale standard N(0,1).
qqnorm(y,ylab="quantiles d'un echantillon de 100
N(1,2)")
qqline(y)
-2
-1
0
1
2
Quantiles of Standard Normal
Le graphe créé par qqnorm montre que les queues de la distribution empirique
des y engendrés sont un peu plus lourdes que celles de la normale.
Remarque : on peut faire un qqplot pour comparer les quantiles de deux
échantillons:
x<-rnorm(100,1,4)
y<-rnorm(100,1,2)
win.graph()
qqplot(x,y)
Exemple : le fichier kidney (ou kidney1) des infections de catheters:
Nous allons maintenant donner quelques statistiques descriptives pour
certaines des variables de kidney1 :
attach(kidney1)
kidney1[1,]
patient infectime cens age sex disease frailty dis0 dis1 dis2 dis3 dis.fac
1
1
8
1 28 1
3
2.3
0 0
0 1 3
summary(infectime)
Min. 1st Qu. Median Mean 3rd Qu. Max.
2
16
39.5 101.6
149.8 562
Attention :
Comparer ce que l'on obtient en faisant summary d'une variable
numérique, disease, et de sa transformée en "factor", dis.fac:
summary(disease)
Min. 1st Qu. Median Mean 3rd Qu. Max.
0
1
1 1.553
3
3
C. Huber
Probabilités et Statistique Descriptive
24
summary(dis.fac)
0 1 2 3
18 24 8 26
Bien que disease et dis.fac aient exactement les mêmes valeurs, leurs
"résumés" (summary) sont très différents. dans le cas de dis.fac, ce sont
les fréquences des modalités qui sont données.
stem(infectime)
# On ne perd pas les valeurs de la variable.
N = 76 Median = 39.5
Quartiles = 16, 150.5
Decimal point is 2 places to the right of the colon
0 : 0000111111111111112222222222333333334444
0 : 55566778
1 : 01112334
1 : 555566889
2 : 004
2:9
3 : 23
3:
4:0
High: 447 511 536 562.
0
10
20
30
40
50
Ce dessin montre une durée d'infection qui est extrêmement asymétrique
(skewed). Cette asymétrie est visible aussi sur l'histogramme:
hist(infectime)
0
100
200
300
400
500
600
infectime
Si nous devions utiliser cette variable dans une régression linéaire, on pourrait
souhaiter la transformer pour la rendre plus proche d'une normale. On essaye la
transformation log:
stem(log(infectime))
C. Huber
Probabilités et Statistique Descriptive
25
N = 76 Median = 3.67622
Quartiles = 2.77259, 5.01391
Decimal point is at the colon
0:7
1:4
1 : 66899
2 : 11112
2 : 556677888
3 : 11122223334444
3 : 567788
4 : 0011224
4 : 67778999
5 : 00000122233
5 : 5788
6 : 01233
0
5
10
15
20
log(infectime) n'est pas normal, mais paraît moins asymétrique, comme on le
voit aussi sur l'histogramme:
hist(log(infectime)
0
2
4
6
log(infectime)
Boîte à moustaches : boxplot
Faisons maintenant un "boxplot" pour log(infectime) :
Un boxplot, aussi appelée "boîte à moustaches" (box-and-whiskers plot),
requiert seulement 5 paramètres décrivant l'échantillon : le minimum, le premier
quartile Q1, la médiane Q2, le troisième quartile Q3, et le maximum. La boîte
est bornée par Q1 et Q3, et les moustaches par min et max. C'est très utile pour
une appréciation rapide de la symétrie d'un échantillont et en particulier pour la
comparaison de plusieurs sous-échantillons.
z <- log(infectime)
boxplot(z, sub = "log(infection time)")
# donne un seul box plot pour l'échantillon
C. Huber
26
1
2
3
4
5
6
Probabilités et Statistique Descriptive
log(infection time)
Comparaison de sous-populations: plot.factor:
Si nous voulons comparer la forme du log-infection-time pour plusieurs souspopulations, par maladie, on utilise le facteur maladie dis.fac (voir l'exemple
10 au chapître1) dans plot.factor. Le premier argument doit appartenir à la
classe factor et c'est lui qui définit les sous-populations pour lesquelles sont
dessinés les boxplot.
plot.factor(dis.fac, log(infectime),data=
kidney1,xlab="Maladie")
title("Boxplots de log(infect-time) par type de
maladie du rein")
4
3
1
2
log(infectime)
5
6
Boxplots de log(infect-time) par type de maladie du rein
0
1
2
3
Maladie
On remarque une différence importante entre les médianes log(infectime) pour
les différents types de maladie. La variabilité de log(infectime) n'est pas la
même non plus pour les différents types de maladie.
Finalement, utilisons un QQ plot normal pour tester la normalité de z :
qqnorm(z) #
title("QQ normal plot for log(infection time)")
qqline(log(infectime))
C. Huber
Probabilités et Statistique Descriptive
27
1
2
3
z
4
5
6
QQ normal plot for log(infection time)
-2
-1
0
1
2
Quantiles of Standard Normal
summary(log(infectime))
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.6931 2.773 3.676
3.821 5.009 6.332
En fait la courbe est très non-linéaire, et z n'est pas du tout normal. Les quantiles
et le boxplot montrent cela très bien.
3. Quelques tests élémentaires en SPLUS : binom.test, prop.test,
t.test, var.test,wilcox.test
a. Inférence statistique pour des proportions et des effectifs :
1. Un seul échantillon d’échecs (0) et succès (1) :*
Supposons qu’ayant tiré au sort n = 100 patients atteints d’une même
maladie, on ait dans l’échantillon ainsi constitué, 42 femmes et 58
hommes.
On saisit ces valeurs en notant 0 pour femme et 1 pour homme.
sex<-c(rep(0,42),rep(1,58))
On se demande si cet échantillon suffit à prouver que cette maladie est
plus répandue chez les hommes que chez les femmes. Si p est la
probabilité qu’un patient tiré au sort soit un homme, on teste donc
l’hypothèse :
Ho : p = 0.5
(il y a autant d’hommes que de femmes)
contre l’alternative :
H1 : p > 0.5
(Il y a plus d’hommes que de femmes)
z<-binom.test(58,100,p=0.5,alt = "g")
L'argument alt ="g" précise que l'alternative est p est plus grand que 0.5,
"g" pour "greater than".
Si l'alternative est bilatérale, H'1 : p ≠ 0.5, on ne précise pas l'hypothèse
alternative:
C. Huber
Probabilités et Statistique Descriptive
28
z1<- binom.test(58,100,p=0.5)
Si l'alternative est unilatérale dans l'autre sens, H"1 : p <0.5,
on précise alt="l", dans binom.test, pour "less than" :
z2<- binom.test(58,100,p=0.5,alt = "l")
z$p.value donne le degré de signification du test.
Si on ne précise pas p, p vaut 0.5 par défaut.
Intervalle de confiance pour une proportion : prop.test
Pour obtenir un intervalle de confiance, de coefficient de confiance 0.98
pour la probabilité p d'être un homme pour un patient tiré au sort, on
utilise l'ordre suivant:
z3<-prop.test(58,100,conf.level = 0.98)
Par défaut, si on ne précise pas le niveau de confiance par conf.level,
celui-ci vaut 0.95.
2. Deux échantillons d'échecs succès : prop.test
Exemple: Le vaccin Salk contre la poliomyélite avait fait l'objet, avant sa
mise sur le marché, d'un essai clinique sur deux très grands groupes de
personnes : Parmi n1 = 200 745 personnes vaccinées, il y a eu x1 = 57
cas de polio, et, parmi n2 = 201 229 personnes non vaccinées (atant reçu
un placebo), x2 = 142 cas.
Saisie des données:
n<-c(200 745, 201 229)
x<-c(57,142)
Le premier vecteur, n, contient les effectifs de chacun des deux groupes,
et le second, x, les nombres de cas correspondants. On veut tester
l'efficacité du vaccin, donc, en appelant p 1 la probabilité d'être atteint
quand on est vacciné et p 2 quand on ne l'est pas, on veut prouver
l'alternative à Ho (p1 = p 2) : H1: p1 < p 2.
z<-prop.test(x,n,alt="l")
z$p.value donne le degré de signification du test qui vaut ici : 1.433 10 -9.
Le test est donc extrêmement significatif.
Remarques:
1. On peut tester simultanément : Ho : p1 = p 01, p2 = p02, contre H1 :non
H0. Exemple : Salk pour p01 = 0.0002 et p02 = 0.0006:
p01 <-0.0002
p02 <- 0.0006:
z<-prop.test(x,n,p=c(p01,p02))
C. Huber
Probabilités et Statistique Descriptive
29
On trouve zz$p.value = 0.00599. Quelle est la conclusion ?
2. z$conf.int donne un intervalle de confiance pour la différence p1-p2
à 95%, si z <-prop.test(x,n) est un test bilatéral, sans indication sur le
coefficient de confiance.
3. Plus de deux échantillons : prop.test
Exemple: Quatre études différentes au sujet de patients atteints de cancer
du poumon ont donné les résultats suivants (Fleiss):
Etude
Nombre de
patients
86
93
136
82
1
2
3
4
Nombre de
fumeurs
83
90
129
70
Question : peut on considérer qu'il y a la même proportion de fumeurs
dans les quatre études ? Soit Ho : p1=p2=p3=p4.
x<-c(83,90,129,70)
n<-c(86,93,136,82)
prop.test(x,n)$p.value vaut 0.00558.
Quelle est la conclusion ?
(On conclut, avec un très bon degré de signification qu'il n'y a pas
d'homogénéité entre les quatre études.
Remarque:
Lorsqu'il y a plus de deux échantillons, prop.test ne calcule pas
d'intervalles de confiance.
4. Tableaux de contingence et tests d'indépendance : chisq.test
Exemple: vaccin Salk
Reprenons l'exemple du vaccin Salk, mais avec une précision
supplémentaire sur le type de poliomyélite: paralysante ou non.
Pas de polio
Vaccinés
Placebo
Total
200 688
201 087
401 775
polio non
paralytique
24
27
51
polio
paralytique
33
115
148
Total
200 745
201 229
401 974
Ce tableau de contingence croise deux variables catégorielles, la
vaccination, à deux états, et la maladie, à trois états. Nous avons donc
2x3 = 6 effectifs.
a. Saisie des données:
salk.mat<-rbind(c(200 688, 24, 33),
c(201 087, 27, 115))
C. Huber
Probabilités et Statistique Descriptive
30
Remarque: On peut obtenir le même résultat par:
salk.mat<-matrix(c(200 688, 24, 33), c(201
087, 27, 115),2, 3, byrow=T)
Si on ne met pas byrow=T, la matrice se remplit par colonnes, ce
qui ne donne pas du tout le même tableau. L'essayer.
b. Test de l'hypothèse d'indépendance:
z<-chisq.test(salk.mat)
donne χ2 =45.4224 pour 2 degrés de liberté (ddl, ou df en anglais),
avec un degré de signification très proche de 0.
Remarque: Si les données ne sont pas fournies sous la forme d'un tableau de
contingence, mais comme un fichier relatif à des patients, soit un par ligne, voici
comment on procède sur un exemple:
Exemple : essai clinique sur le propanolol et survie à 28 jours:
med
survie
prop
oui
control
non
control
oui
prop
oui
med<-c("prop","control","control","prop")
survie<-c(1,0,1,1)
Pour obtenir le tableau de contingence correspondant:
tableau<-table(med,survie)
Exemple:Etude de l'effet de la vitamine C sur une
néphropathie.
Pour mesurer l'effet de la vitamine C sur la Cytose néphropathique on a
obtenu les résultats suivants sur 64 patients :
Amélioration clinique
Oui
Non
Vitamine C
24
8
Pas de Vitamine C
29
3
Est il clair que la Vitamine C est un traitement efficace contre la Cytose
néphropathique ?
On commence par tester qu'il y a indépendance entre vitamine C et Cytose
néphropathique. La manière la plus simple de saisir les données est de faire :
NC <- matrix(c(24,29,8,3),2,2)
# les données entrent par colonne!
NC
[,1] [,2]
[1,] 24 8
[2,] 29 3
chisq.test(NC)
Pearson's chi-square test with Yates' continuity correction
data: NC
C. Huber
Probabilités et Statistique Descriptive
31
X-square = 1.7564, df = 1, p-value = 0.1851
Au niveau 5% on ne peut pas conclure que la vitamine C ait un effet
quelconque.
c. Tests de comparaison de deux échantillons d'une variable
continue : Student et Wilcoxon
Dans le data frame kidney1 nous avons la variable infectime qui est en fait le
temps jusqu'à l'infection ou le temps jusqu'à ce qu'on quitte l'étude, selon celui
qui arrive en premier. Mais on a aussi le sexe des patients. Pour tester
l'hypothèse nulle :
Ho : La durée moyenne chez les hommes = la durée moyenne chez les femmes.
Avec le test classique t, en supposant que les variances des deux populations
sont égales, on fait un test t : t.test :
X1 − X 2
t=
(n1 − 1) S12 + (n2 − 1) S 22
(n1 + n2 − 2)
t.test(infectime[sex==1],infectime[sex==2])
(Standard t-Test ) la sortie commence ici :
data: infectime[sex == 1] and infectime[sex == 2]
t = -1.706, df = 74, p-value = 0.0922 do not reject the null hypothesis.
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
for mean(male) – mean(female)
-124.551228 9.651228
sample estimates:
mean of x mean of y
59.3
116.75
Remarque
Nous devrions nous restreindre aux cas 'cens=1'. Le faire. Et nous devrions aussi
tester tout d'abord l'égalité des variances. Pour tester l'hypothèse nulle
H0 : variance(infectime (homme) )= variance (infectime (femme))
on emploie la fonction var.test pour faire un test F d'égalité de variances :
var.test(infectime[sex==1],infectime[sex==2])
C'est le test F d'égalité des variances :
( X i − X ) 2 / (n1 − 1)
∑
F=
∑ (Yi − Y ) 2 / (n2 −1)
data: infectime[sex == 1] and infectime[sex == 2]
C. Huber
Probabilités et Statistique Descriptive
32
F = 0.9354, num df = 19, denom df = 55, p-value = 0.9093
alternative hypothesis: true ratio of variances is not equal to 1
95 percent confidence interval:
0.4710643 2.1338385
sample estimates:
variance of x variance of y
15896.43
16994.85
Il n'y a aucune preuve contre l'égalité des variances. Aussi était il convenable de
faire un test t avec égalité des variances. Mais nous savons que la durée jusqu'à
l'infection (infectime) n'est pas normale. Aussi devrions nous plutôt faire un test
de Wilcoxon, qui ne fait aucune hypothèse de ce genre, pour tester l'égalité des
distributions pour les hommes et les femmes, (ce qui implique l'égalité des
moyennes). Pour cela employer la fonction wilcox.test :
wilcox.test(infectime[sex==1],infectime[sex==2])
# c'est le test de Wilcoxon fondé sur la somme des rangs.
data: infectime[sex == 1] and infectime[sex == 2]
rank-sum normal statistic with correction Z = -2.9614, p-value = 0.0031
alternative hypothesis: true mu is not equal to 0 reject the null hypothesis
Warning messages:
1: cannot compute exact p-value for n larger than 50 in: wil.rank.sum(x, y, al
ternative, exact, correct)
Les deux tests , t et Wilcoxon, donnent des résultats apparemment
contradictoires. Mais comme la normalité n'est pas vérifiée, c'est la conclusion
du test de Wilcoxon que nous conserverons.
Remarquons d'ailleurs que t teste l'égalité des moyennes et Wilcoxon l'égalité
des lois à partir de celle des médianes. Or l'égalité des lois entraîne celle des
moyennes, mais pas l'inverse. Il n'est donc pas impossible d'avoir l'égalité des
moyennes sans avoir pour cela l'égalité des lois.
Remarquons que aucun de ces deux tests ne fera la différence entre deux lois
normales de même moyenne et de variances différentes : par contre, le test de
Kolmogorov-Smirnov, qui viendra au chapitre 3, lui, fait la différence.
C. Huber
Tests d'adéquation
33
Chapitre 3.
Tests d'adéquation (fit) :
Le test du Chi-deux et le test de Kolmogorov-Smirnov.
(degré de signification (p-value) calculé par Bootstrap)
Lorsqu'on se demande si la loi de probabilité d'un échantillon appartient à une certaine famille de
lois:
les lois normales, ou
les lois exponentielles, ou
les lois gammas, bétas, ou
les lois de Poisson, etc...
on peut commencer par avoir une approche graphique en comparant les quantiles des observations
aux quantiles de la loi qui dans la famille est la plus proche par la fonction
cdf.compare
Cette commande compare les quantiles de l'échantillon x aux quantiles d'une loi de la famille. Le
premier argument est l'échantillon x , le second le type de la loi, les suivants les paramètres qui
définissent la loi à l'intérieur de sa famille. Le dessin correspondant donne une idée de la proximité
des deux lois mais ensuite, si elles ne sont pas trop éloignées, il faut faire un test pour pouvoir
conclure valablement.
les deux tests de fit les plus courants sont le test du Chi-deux correspondant aux commandes
chisq.test
chisq.gof
et le test de Kolmogorov-Smirnov dont la commande est
ks.gof
Nous allons générer des échantillons aléatoires à partir de lois standards connues et utiliser ensuite
ces tests de fit pour voir s'ils rejettent ou non l'hypothèse nulle H0 selon laquelle l'échantillon
provient de la loi qui à servi à le générer.
Les tests ne devraient pas rejeter H0.
1. Evaluation graphique : cdf.compare
Avant d'effectuer un test d'ajustement, on commence en général par faire un graphe
permettant de se rendre compte si la loi pressentie pour l'échantillon a des chances d'être
acceptée. Voici une des possibilité que donne Splus : La fonction cdf.compare.
C. Huber
34
Tests d'adéquation
x <- rnorm(100)
cdf.compare(x, distribution = "normal")
0.0
0.2
0.4
0.6
0.8
1.0
Empirical and Hypothesized normal CDFs
-4
-3
-2
-1
0
1
2
solid line is the empirical d.f.
cdf.compare(x, distribution = "exponential")
0.0
0.2
0.4
0.6
0.8
1.0
Empirical and Hypothesized exponential CDFs
-4
-3
-2
-1
0
1
2
solid line is the empirical d.f.
x <- rexp(100)
cdf.compare(x, distribution = "exponential")
x<-rbinom(100,3,.2)
cdf.compare(x, distribution = "binomial",size=3,p=.2)
x<-rpois(100,3)
cdf.compare(x, distribution = "poisson",lambda=5)
Exemple:
Pour la durée jusqu'à l'infection (infectime) du fichier sur
les maladies du rein (kidney1), faire cdf.compare,
C. Huber
35
Tests d'adéquation
- d'abord pour la loi normale standard:
cdf.compare(infectime,dist="normal")
0.0
0.2
0.4
0.6
0.8
1.0
Empirical and Hypothesized normal CDFs
0
100
200
300
400
500
solid line is the empirical d.f.
- puis pour la loi normale qui a la même moyenne que infectime et la même variance qu'elle:
cdf.compare(infectime,dist="normal",mean=mean(infectime),sd=sq
rt(var(infectime)))
0.0
0.2
0.4
0.6
0.8
1.0
Empirical and Hypothesized normal CDFs
0
100
200
300
400
500
solid line is the empirical d.f.
Le deuxième dessin montre évidemment une bien meilleure adéquation.
2. Le test du Chi-deux : les fonctions chisq.gof et chisq.test
Définition du test:
Supposons que X est le vecteur des observations, de longueur n. Pour mener à bien le test
du chi-deux, on doit d'abord diviser la droite réelle en un nombre fini k d'intervalles
disjoints, et calculer pour chaque intervalle I :
C. Huber
Tests d'adéquation
36
L'effectif Observé[i] = #{X[j] : X[j] ∈ intervalle I} noté Obs[i]
L'effectif Attendu[i] = n P F[X ∈ intervalle I] noté Exp[i]
L'effectif attendu est l'espérance (expectation en anglais)de l'effectif sous la loi F. La
statistique du chi-deux pour l'hypothèse nulle H0 : X~F est donnée par
T = ∑ {Obs[i] –Exp[i]}2 / Exp[i]
Le test rejette H0 si T > χ2 (k-1) où la valeur critique est déterminée en utilisant une table du
chi-deux à k-1 degrés de liberté. Si la distribution F à laquelle on se réfère n'est précisée qu'à
m paramètres près, on doit estimer ces paramètres par maximum de vraisemblance (sur les
classes), et le nombre des degrés de liberté est réduit d'autant : il vaut k-m-l. Cela à
condition que m désigne le nombre des paramètres indépendants.
La fonction chisq.gof:
a. Sans argument:
x<-rexp(100)
z<-chisq.gof(x,distribution="normal")
z
Chi-square Goodness of Fit Test
data: x
Chi-square = 167.28, df = 12, p-value = 0
alternative hypothesis:
True cdf does not equal the normal Distn.
for at least one sample point.
Exemple: l'appliquer à la variable infectime et log(infectime)
x<-infectime
z<-chisq.gof(x,distribution="normal",mean=mean(x),
sd=sqrt(var(x)))
z
data: x
Chi-square = 89.4737, df = 11, p-value = 0
xx<-log(infectime)
zz<-chisq.gof(xx,distribution="normal",mean=mean(xx),
sd=sqrt(var(xx)))
zz
data: xx
Chi-square = 9.8947, df = 11, p-value = 0.5399
La conclusion est que la durée jusqu'à l'infectioj n'est pas normale du tout, le degré de
signification étant "extrêmement nul", alors que lson logarithme peut être considéré comme
tel.
C. Huber
Tests d'adéquation
37
Attention:
En effectuant ce test, nous avons estimé à partir de l'échantillon deux paramètres: il faut le
signaler par l'argument n.param.est = 2 dans la fonction cuisq.gof. Que devienent alors les
résultats ci-dessus?
xx<-log(infectime)
zzz<-chisq.gof(xx,distribution="normal",mean=mean(xx),
sd=sqrt(var(xx)),n.param.est=2)
zzz
data: xx
Chi-square = 9.8947, df = 9, p-value = 0.3591
La conclusion est la même: le logarithme de la durée jusqu'à l'infection peut être considéré
comme normal (ce qui n'est pas le cas de la durée proprement dite, infectime).
b. Avec un nombre de classes spécifié : n.classes
zc<- chisq.gof(x,distribution="normal", n.classes=5)
zc
Chi-square Goodness of Fit Test
data: x
Chi-square = 79.9, df = 4, p-value = 0
alternative hypothesis:
True cdf does not equal the normal Distn.
for at least one sample point.
c. Avec des points de coupure spécifiés : cut.points
Essayons le test du chi-deux avec des intervalles qui dépendent des données et qui sont
définis par leurs points frontières obtenus par la fonction cut.points :
x<-rpois(100,3)
x
4 3 0 4 0 0 3 5
3 2 3 4 1 2 4 3
1 1 2 3 4 5 2 2
0 1 5 1 3 2 2 1
3
3
7
4
2 1 2 4 2 5 2 2 1 0 5 1 2 4 6 3 4 4 1 0 1
4 5 8 1 3 3 1 3 1 4 2 3 4 3 1 4 1 2 0 2 2
3 2 0 3 7 0 4 1 3 1 2 5 5 5 6 5 2 8 5 1 0
3
coupures<-c(min(x)-1,quantile(x))
coupures
0%
25% 50% 75% 100%
-1
0
1
3
4
8
Il y a 5 intervalles et 4 degrés de liberté. Les intervalles sont (-1 0], (0 1], (1, 3],
C. Huber
Tests d'adéquation
38
(3 4], (4,8]. Notons que le premier intervalle d'entiers est (-1,0] qui est simplement l'entier
0.
Chisq<-chisq.gof(x,dist="poisson",lambda=3,
cut.points=coupures)
Warning messages:
Expected counts < 5. Chi-squared approximation may not
be appropriate. in: chisq.gof(x, dist = "poisson", lambda = 3, cut.points = c(min(x) - 1,
...
Chisq
# Affiche la valeur de la statistique chi deux::
4.987597
Il résulte du "warning" de SPLUS pour le test de Poisson(3) qu'il est douteux que la statistique
T du test suive effectivement une loi du chi-deux. Aussi allons nous utiliser le bootstrap pour
obtenir la valeur critique bootstrap de la loi de T. Bien que Splus ait une fonction bootstrap que
nous avons essayé d'utiliser, le programme s'est bloqué. Il semble en effet que lorsque la
statistique utilisée dans le bootstrap est un peu complexe, Splus sature sa mémoire. Aussi avons
nous programmé une routine de bootstrap directe qui calcule la loi de la statistique de test sous
l'hypothèse nulle. Comme ici elle est complétement connue (c'est la loi de Poisson de paramètre
3), cela revient à une simple simulation.
On a besoin de Bb= nombre de tirages bootstrap ;
Bb<-1000
n <- length(x)
# simulation de même taille que l'échantillon x
Boot.chi<- matrix(0,Bb,1)
Lambda.theo<-3
for (bb in 1:Bb)
{
y <- rpois( n, Lambda.theo)
Bins <- c(min(y)-1,quantile(y))
# quantile : "les 5 nombres"min, Q1, Q2, Q3, max
# fondements de la boîte à moustaches (Tukey).
y.max <-Bins[6]
Bins<-unique(floor(Bins))
# unique(y) enlève les répétitions dans y.
# print(Bins)
k <- length(Bins)
if(k<=1){print(bb);print(Bins);next}
# si k<=1, on n'a pas d'intervalle valable : on va par next à la
# prochaîne itération de la boucle. Si on avait voulu sortir
# de la boucle, on aurait mis stop.
Obs <- matrix(0,k,1)
# fréquences observées
Exp <- matrix(0,k,1)
C. Huber
Tests d'adéquation
39
# fréquences attendues sous Poisson(Lambda.theo)
for (jj in 1:(k-1))
{
Obs[jj] <- sum(y<=Bins[jj+1]
& y > Bins[jj])
Exp[jj] <-n * (ppois(Bins[jj+1],
Lambda.theo) –
ppois(Bins[jj],Lambda.theo))
}
Exp[k] <- (1 – ppois(y.max,Lambda.theo))*n
Obs[k] <- 0
Chisq <- sum((Obs-Exp)^2 / Exp)
Boot.chi[bb] <- Chisq
}
Mean <- mean(Boot.chi)
Var <- var(Boot.chi)
Critical.value.95 <- sort(Boot.chi)[1000*.95]
Critical.value.95
# Affiche :
[1] 10.4315
Mean
[1] 4.740271
Var
[1,] 8.960227
Comme la valeur observée de la statistique du chi-deux vaut
4.987597 << 10.4315,
le test Bootstrap ne rejette pas H0. Le degré de signification bootstrap vaut :
Bootstrap.p.value <- sum(Boot.chi > 4.987597)/1000
Bootstrap.p.value
# Affiche :
0.372
Le degré de signification Bootstrap est de 0.372 au lieu de 0.2885725 comme le donne
l'approximation du chi deux :
1-pchisq(4.987597, df = 4, ncp=0) # affiche
[1] 0.2885725
valeur obtenue par approximation asymptotique. Notons aussi que la moyenne de la
distribution bootstrap est 4.740271 et sa variance 8.960227. Pour la distribution chi-deux, la
variance est toujours égale à deux fois la moyenne, qui est à son tour égale au nombre de
C. Huber
Tests d'adéquation
40
degrés de liberté. La distribution bootstrap de la statistique T du chi-deux paraît donc assez
voisine, au moins sur ce plan-là, d'une distribution du chi-deux à 4 degrés de liberté.
Mais, pour examiner de plus près la distribution bootstrap de T faisons :
stem(Boot.chi)
# Affiche :
N = 1000 Median = 4.156777
Quartiles = 2.650716, 6.15224
Decimal point is at the colon
0 : 23344
0 : 5555555566667777778888888888899999
1 : 000000000111111111222223333333334444444444
1 : 555555555555555555566666666666677777777788888888888888888999999x
2 : 0000000011111111122222222222333333333333333444444444444444
2 : 555555555555555555555666666666666667777777777777777777888888888x
3 : 000000000000000111111111111111111222222222223333333333333333444x
3 : 555555555555555556666666666666666667777777788888888888888888888x
4 : 000000000000000000001111111111111111112222222222222222223333333x
4 : 555555555555556666666666666777777788888888888888888999999999999x
5 : 000000000000001111111111222222222233333333333333344444444444444x
5 : 555555566666666677777777788888888888889999999
6 : 0000000011111111122222233333334444444
6 : 5555555555555566666666777777777777888899999999999999
7 : 0000001111111111222223333444
7 : 5555555666666666667777778888999
8 : 0000001111112222333344
8 : 56667889
9 : 000011233444444
9 : 56677788889
10 : 0022333333344
10 : 567779
11 : 012334444
11 : 9999
12 : 00234
12 : 889
13 : 01
High: 13.20846 13.23913 13.23933 13.40341 13.67238 13.72838 13.94078
High: 13.98775 14.04006 14.14536 14.18454 14.99237 15.09345 15.22190
High: 15.35661 15.45370 15.65704 18.14057 19.50034 19.83742 23.28470
C. Huber
Tests d'adéquation
41
Ce dessin montre une distribution étalée vers la droite, mais qui ne ressemble pas à une
distribution de chi-deux.
Estimons maintenant lambda en utilisant la moyenne de l'échantillon, et refaisons le test du chideux :
Chisq<-chisq.gof(x,dist="poisson",lambda=mean(x),
cut.points=c(min(x)-1,quantile(x)))
Chisq
# Affiche :
Chi-square Goodness of Fit Test
data: x
Chi-square = 3.7871, df = 4, p-value = 0.4356
alternative hypothesis:
True cdf does not equal the poisson Distn. for at least one sample point.
Nous savons bien sûr que le nombre de degrés de liberté est maintenant égal à 3, ce que nous
aurions dû en fait lui dire par l'argument n.param.est =1. Cependant, même avec 3 degrés de
liberté, le test ne rejette pas l'hypothèse que l'échantillon provient d'une loi de Poisson de moyenne
non spécifiée. Nous pouvons à nouveau essayer un bootstrap, appelé bootstrap paramétrique car
les paramètres de la loi de tirage sont estimés sur l'échantillon. Nous devons seulement remplacer
dans le programme précédent lambda =3 par lambda = mean(x).
Voici le programme :
On a besoin de Bb= nombre de tirages bootstrap ; x le vecteur des valeurs observées
Bb<-1000
n <- length(x)
# simulation de même taille que l'échantillon x
lambda.theo<-3
Boot.chi<- matrix(0,Bb,1)
for (bb in 1:Bb)
{
y <- rpois( n,lambda.theo)
lambda.emp<-mean(y)
Bins <- c(min(y)-1,quantile(y))
# quantile : "les 5 nombres"min, Q1, Q2, Q3, max
# fondements de la boîte à moustaches (Tukey).
y.max <-Bins[6]
Bins<-unique(floor(Bins))
# unique(y) enlève les répétitions dans y.
# print(Bins)
k <- length(Bins)
if(k<=1){print(bb);print(Bins);next}
# si k<=1, on n'a pas d'intervalle valable : on va par next à la
# prochaîne itération de la boucle. Si on avait voulu sortir
C. Huber
Tests d'adéquation
42
# de la boucle, on aurait mis stop.
Obs <- matrix(0,k,1)
# fréquences observées
Exp <- matrix(0,k,1)
# fréquences attendues sous Poisson(lambda.theo)
for (jj in 1:(k-1))
{
Obs[jj] <- sum(y<=Bins[jj+1]
& y > Bins[jj])
Exp[jj] <-n * (ppois(Bins[jj+1],
lambda.theo) –
ppois(Bins[jj],lambda.emp))
}
Exp[k] <- (1 – ppois(y.max,lambda.emp))*n
Obs[k] <- 0
Chisq <- sum((Obs-Exp)^2 / Exp)
Boot.chi[bb] <- Chisq
}
Mean <- mean(Boot.chi)
Var <- var(Boot.chi)
Critical.value.95 <- sort(Boot.chi)[1000*.95]
Critical.value.95
# Affichage :
13.02983
Mean
5.519774
Var
17.69588
mean(x)
2.73
Critical.value.95
13.02983
Boo.p.value <- sum(Boot.chi > 2.0909)/1000
Boo.p.value
0.85
Il est clair qu'on ne rejette pas l'hypothèse nulle selon laquelle l'échantillon provient d'une loi
de Poisson. Le degré de signification (p-value) bootstrap vaut .85 plutôt que la valeur
asymptotique 0.791.
Test de fit du Chi-deux pour une variable catégorielle : direct et par chisq.test
Exemple:
La loi d'un certain indicateur de la teneur en plomb du cheveu humain normal est donnée
par :
C. Huber
Tests d'adéquation
43
x 1 2 3 4 5
P(x) .1 .2 .4 .2 .1
Un échantillon de 20 nouveau-nés dans un quartier pauvre a été testé et on a trouvé que,
parmi les 20, 4 valaient 1, 8 valaient 2, 6 valaient 3, et 2 valaient 4 pour l'indicateur x .
x
frequence
1 2 3 4 5
4 8 6 2 0
On peut utiliser le test du chi-deux pour vérifier si cet échantillon obéit bien à la distribution
spécifiée. Cependant, nous ne pouvons pas utiliser chisq.gof parce que la distribution
spécifiée n'est aucune de celles qui sont traitées par chisq.gof. Aussi devons nous écrire
directement un programme en Splus pour faire le test ou bien la commande chisq.test.
Programmation directe du test du chi-deux pour une variable catégorielle:
Obs <- matrix(c(4,8,6,2,0),5,1)
Exp <- matrix (20*c(.1, .2, .4, .2, .1),5,1)
Chi <- sum((Obs-Exp)^2/Exp)
Df <- 4
P.value <- 1 – pchisq(Chi,df=Df)
# pchisq=f.r. du chi-deux
Critical.value <- qchisq(.95,df=Df)
# qchisq= fonction des quantiles
# de la loi du chi-deux
# Affichage
Chi
[1] 9.5
P.value
[1] 0.04974725
Critical.value
[1] 9.487729
Le test du chi-deux est à peine significatif au niveau 5%. Il y a une faible suspicion que les
nouveau-nés ont une loi de la teneur en plomb dans les cheveux légèrement différente de
celle de la population.
La fonction chisq.test:
Elle peut être utilisée pour tester qu'un échantillon d'une variable catégorielle, c'est à dire de
type "factor", suit une distribution donnée. Elle a pour argument une matrice qui a pour
première ligne les effectifs de l'échantillon et pour deuxième ligne leurs espérances si la loi
que l'on veut tester est vraie.
C. Huber
44
Tests d'adéquation
Exemple:
Dans le fichier kidney, on se demande si la maladie (dis.fac) est équirépartie sur les quatre
maladies possibles. Faire un test du chi-deux:
attach(kidney1)
x<-dis.fac
eff<-as.vector(summary(dis.fac))
eff.tot<-sum(eff)
esp<-eff.tot*rep(0.25,4)
eff
# 18 24 8 26
esp
# 19 19 19 19
tab<-matrix(c(eff,esp),nrow=2,byrow=T)
res<-chisq.test(tab)
res
Pearson's chi-square
correction
test
without
Yates'
continuity
data: tab
X-square = 6.1788, df = 3, p-value = 0.1032
En conclusion, on ne peut donc pas rejeter l'hypothèse que les quatre types de maladie sont
aussi bien représentées l'une que l'autre dans l'échantillon.
Exercice : On pourra appliquer ce test aux données sur la teneur en plomb des cheveux et
comparer au programme direct.
2. Le test d'ajustement de Kolmogorov-Smirnov : fonction ks.gof
Le test de Kolmogorov-Smirnov ne demande pas de créer des intervalles particulier. Sa
statistique de test est donnée par :
S = sup {| F n (x) – F(x) | : -∞ ≤ x ≤ ∞ }
= max { max(|Fn(Xi-1) – F(Xi)| , |Fn(Xi) – F(Xi)| ) : i=1,…n}
où Fn est la f.r. empirique de l'échantillon et est définie pour tout x comme
Fn = (le nombre des ( Xi <= x) )/ n
Le test rejette H0 quand S est plus grand qu'une valeur critique déterminée à partir de la
distribution asymptotique (c'est à dire pour n grand) de la statistique de Kolmogorov.
C. Huber
Tests d'adéquation
45
Nous allons utiliser à nouveau un échantillon x de taille n = 1000 d'une loi de Poisson
(lambda =3) , et tester qu'il provient d'une loi de Poisson. Malheureusement, le test de K-S
n'accepte en Splus que des échantillons de taille inférieure ou égale à 50. Aussi utiliserons
nous seulement les 50 premières observations :
ks.gof(x[1:50],dist="poisson",lambda=3)
Test de Kolmogorov-Smirnov pour un échantillon
Distribution supposée = Poisson
data: x[1:50]
ks = 0.2672, p-value = 0.3911
alternative hypothesis:
True cdf is not the poisson distn. with the specified parameters
A nouveau, en général, on ne connaît pas lambda, et on utilise à la place lambda=mean(x), et
faisons ensuite un bootstrap pour trouver la p-value.
Le test de Kolmogorov-Smirnov pour une loi non standard :
Poursuivant avec le même exemple, on pourrait vouloir utiliser le test unilatéral de K-S pour
tester :
H0 : F >= F0 contre
H1 : F < F0 (c'est à dire : (F0(x) >= F(x) pour tout x)
où F0 est la fonction de répartition de la teneur en plomb dans la population et F celle de la
population des nouveau-nés. Nous devons à nouveau programmer le test directement :
F0 <- matrix (c(.1, .3, .7, .9, 1.0),5,1) # f.r; de la population
Fn <- matrix(c(4,12,18,20,20)/20,5,1) # f.r. empirique
Fn # affiche la f.r. empirique de la teneur en plomb.
[,1]
[1,] 0.2
[2,] 0.6
[3,] 0.9
[4,] 1.0
[5,] 1.0
F0 # affiche la f.r. de la teneur en plomb dans la population.
[,1]
[1,] 0.1
[2,] 0.3
C. Huber
Tests d'adéquation
46
[3,] 0.7
[4,] 0.9
[5,] 1.0
KS <- max(Fn - F0)
# le test unilatéral de Kolmogorov Smirnov
KS
[1] 0.3
Pour obtenir le degré de signification bootstrap :
Bb <- 1000
Boot.KS <- matrix(0,Bb,1)
n <- 20
for(bb in 1:Bb) {
y <- sample(x, size = n, replace = T)
Fn <- matrix(0,5,1)
for(ii in 1:5) {Fn[ii] <- sum(y<=ii)/n}
Boot.KS[bb] <- max(Fn – F0)
# le test unilatéral de Kolmogorov Smirnov
}
Boot.KS <- sort(Boot.KS)
# Affichage :
Boot.KS[Bb*.95]
# valeur critique bootstrap du test K-S.
[1] 0.45
sum(Boot.KS > .3)/Bb
# degré de signification bootstrap.
[1] 0.489
On voit que l'hypothèse nulle que les nouveau-nés ont une teneur en plomb
stochastiquement plus grande que la population générale n'est pas rejetée à 5%, ni même à
40% (niveau).
C. Huber
Régression linéaire
47
Chapitre 4.
REGRESSION LINEAIRE MULTIPLE.
1. Définition du modèle de Régression Linéaire Multiple :
C'est un modèle qui consiste à supposer qu'une variable réponse y est une combinaison linéaire de plusieurs
variables explicatives x, avec des poids β qu'il faut évaluer, à une erreur aléatoire près
ε qui suit une
loi normale. Ce que l'on espère du modèle c'est que la part aléatoire qui demeure dans l'erreur
ε
est faible. Autement dit, on espère que la variance σ de l'erreur est faible par rapport à la
variabilité générale de y
y = Σ β i xi + ε
y
x1, x2,…, xp
ε
=
=
=
(4.1)
la variable réponse,
les variables explicatives,
l'erreur : normale N(0,σ
σ2)
On a n observations : n équations :
y1 = Σ β i xi1 + ε 1
y2 = Σ β i xi2 + ε 2
…………………
yn = Σ β i xin + ε ν
1re observation
2ème observation
………………….
ème
n
observation (dernière observation)
Exemple 1 :
données HOSPITALIERES :
n = 113 hôpitaux. p = 10.
On cherche à savoir quelles sont les caractéristiques de l'hôpital qui permettent
de prédire le mieux possible le nombre moyen de jours (y = ave.hosp.days )
qu'un patient reste dans cet hôpital.
Id
ave.hosp.days
ave.age.pat
infect.risk
:
:
:
:
numéro d'identification de l'hôpital.
nombre moyen de jours passés à l'hôpital par patient.
âge moyen des patients.
score de risque d'infection.
C. Huber
Régression linéaire
cultrat
xrayrat
nbeds
Medschl
region
patday
nurses
pctservice
:
:
:
:
:
:
:
:
48
taux de patients (en % ) pour qui une culture est requise.
taux de patients (en % ) pour qui une radio est requise.
Nombre de lits hospitaliers.
L'hôpital est il associé à un CHU ? (oui=1, non=2).
Région du pays: 1=N-E, 2=N-O, 3 =Sud , 4= Ouest .
nombre moyen de patients présents par jour.
nombre moyen d'infirmières dans l'hôpital.
pourcentage de services offerts par l'hôpital.
Ecriture matricielle :
Y = Xβ + ε
Y nx1
Xnxp
β px1
ε nx1
σ
Estimateurs sans biais des β :
observée
observée
à estimer
non observée
à estimer
b = (XT X)-1XTY
L (b) = N( β , Σ(b))
Σ( )) :
E(b) = β
Σ(b)
Σ( ) = σ2(X TX)-1
(4.2)
(4.3)
Estimateur sans biais de σ2 :
Estimateur(σ
σ2) = s2 = ErrSS / (n-p)
(4.4)
ErrSS = (y - X b)T (y - X b).
Résidus :
e estime l'erreur ε .
e=y-Xb
(4.5)
Valeurs prédites (fitted values) :
^
y = Hy = X( X T X ) −1 X T y
PROCEDURE DE DETERMINATION DU MODELE.
1) Commencer par le modèle qui contient toutes les covariables (p),
C. Huber
(4.6)
Régression linéaire
49
2) Eliminer à chaque étape la covariable qui a la plus grande p-value
3) jusqu'à ce que les covariables restantes aient une p-value inférieure
à une limite donnée, par exemple: .05.
Traitement de l'exemple 1 : 113 hôpitaux
1) Importer le fichier texte des données (hosp) avec les en-tête de colonnes,
pour en faire un data.frame en SPLUS appelé regex.
2) Attacher le data.frame pour avoir à disposition toutes les variables :
attach(regex)
regex[1,]
# affiche la première ligne du data.frame.
id ave.hosp.days ave.age.pat infect.risk cultrat xrayrat nbeds medschl region
1
7.13
55.7
4.1
9
39.6
279
2
4
patday nurses pctservice
207
241 60
3) Ajuster une régression linéaire multiple grâce à la fonction lm
(pour linear model) :
reg.lm1 <- lm(ave.hosp.days ~ ave.age.pat +
infect.risk +
cultrat + xrayrat + nbeds + medschl +
as.factor(region) + patday + nurses +
pctservice)
Remarque importante :
La région est une variable catégorielle, même si elle est codée de 1 à 4. Pour
cette raison, on l'inclut comme facteur (as.factor(region)) plutôt que region,
dans le modèle. Alors, lm (linear model) sait qu'il faut la traiter comme une
variable catégorielle, ce qui est équivalent à générer 3 variables binaires.
reg.lm1 est un objet SPLUS qui est une liste (list) de tous les résultats de
l'analyse. La fonction summary en extrait un résumé :
summary(reg.lm1)
Call: lm(formula = ave.hosp.days ~ ave.age.pat + infect.risk + cultrat + xrayrat +
nbeds + medschl + as.factor(region) + patday + nurses + pctservice)
Residuals : (les ei)
Min
1Q
Median
3Q
Max
-2.305 -0.6608 -0.0272
0.5862
6.3
C. Huber
Régression linéaire
50
Comme les résidus ne sont pas standardisés, il est difficile de juger si 6.3 est
grand, mais, comparé au plus petit, qui vaut –2.3, il paraît grand.
Coefficients: (les coefficients b pour le modèle complet lm1)
Value
Std. Error
t value
(Intercept)
2.6260
1.8579
1.4134
ave.age.pat
0.0799
0.0283
2.8275
infect.risk
0.4397
0.1273
3.4538
cultrat
0.0055
0.0160
0.3470
xrayrat
0.0127
0.0071
1.7753
nbeds
-0.0049
0.0036
-1.3464
medschl
-0.2666
0.4411
-0.6045
as.factor(region)1 -0.4065
0.1757
-2.3135
as.factor(region)2 -0.2506
0.0944
-2.6560
as.factor(region)3 -0.3059
0.0911
-3.3572
patday
0.0152
0.0044
3.4321
nurses
-0.0059
0.0022
-2.6560
pctservice
-0.0122
0.0138
-0.8842
Pr(>|t|)
0.1606
0.0057
0.0008
0.7293
0.0789
0.1812
0.5469
0.0227
0.0092
0.0011
0.0009
0.0092
0.3787
Pr(>|t|) est la p-value (dds) pour le test des hypothèses nulles successives :
H0 : toutes les autres variables, excepté celle-ci, sont dans le modèle.
H1: toutes les variables sont dans le modèle.
Les variables pour lesquelles l'hypothèse nulle n'est pas rejetée sont cultrat,
nbeds, medschl, et pctsrvice. Elles seront supprimées dans le modèle suivant.
Comment est testée la validité du modèle ?
Ho : β = 0
H1 : β ≠ 0
Le test est fondé sur LA PROPORTION DE VARIABILITE DE Y
EXPLIQUEE par la régression.
Statistique du test : le rapport F :
F = (RegSS/p) / (ErrSS/(n-p))
ErrSS :
RegSS :
somme des carrés des erreurs, donnée par (4.4)
la somme des carrés des écarts expliquée par la régression
C. Huber
(4.7)
Régression linéaire
51
RegSS = TotSS –ErrSS
TotSS = Σ (yi – y-bar)2
(4.8)
(4.9)
TotSS = somme des carrés des erreurs pour le modèle nul (sans covariables),
Sous H0 : F ∼ Fisherp,(n-p)
dds := p-value := P[Fp,n-p > F observé | H0 est vraie]
(4.10)
calculé directement à partir de la table de F de SPLUS.
Pour le modèle lm1 :
Residual standard error: 1.231 on 100 degrees of freedom
Multiple R-Squared: 0.6299
F-statistic: 14.18 on 12 and 100 degrees of freedom, the p-value is 1.11e-016
Le test F rejette l'hypothèse que tous les beta sont nuls. La valeur du R2
multiple est assez élevée. Des modèles plus parcimonieux, avec moins de
variables, auront une valeur de R 2 probablement plus faible.
Corrélation des coefficients: (pour le modèle complet lm1)
(Intercept) ave.age.pat infect.risk cultrat xrayrat nbeds
ave.age.pat
infect.risk
cultrat
xrayrat
nbeds
medschl
as.factor(region)1
as.factor(region)2
as.factor(region)3
patday
nurses
pctservice
-0.7828
0.1574
-0.3202
-0.2200
0.1412
-0.4777
-0.3125
-0.0942
-0.1332
-0.1698
-0.0313
-0.2289
-0.1643
0.2918
-0.0169
-0.0630
-0.0377
0.2503
-0.0010
0.0512
0.0526
0.0553
-0.0005
medschl
as.factor(region)1 as.factor(region)2
as.factor(region)1 0.1226
as.factor(region)2 -0.0621
as.factor(region)3 0.1657
patday
0.2887
nurses
0.0528
-0.4977
-0.2916
0.1845
-0.1926
-0.0926
0.0142
-0.2302
-0.2430
-0.0231
-0.1822
0.1277
0.1163
0.2486
0.0594
C. Huber
-0.1549
-0.1561 0.0032
0.2426 0.0229 -0.1790
0.2394 0.0731 -0.2745
0.1039 0.1752 -0.1295
0.2381 0.1099 -0.1874
0.2316 0.0141 -0.8861
-0.0913 0.0137 -0.2388
0.0997 0.0246 -0.2643
0.0806
0.0651
0.0112
Régression linéaire
pctservice
0.1095
0.1098
0.2352
as.factor(region)3 patday
patday
nurses
pctservice
52
0.2968
-0.1464
0.0870
nurses
-0.1186
0.1384 -0.1909
Il y a quelques corrélations élevées parmi les variables. Or ce n'est pas
souhaitable car cela conduit à des estimateurs instables des coefficients beta.
Mais comme nous avons déjà décidé d'éliminer du modèle certaines variables,
on peut espérer que cela a fait disparaître certaines des corrélations les plus
élevées. Voici le second modèle qui ne contient ni cultrat, ni nbeds, ni medschl,
ni pctsrvices.
reg.lm2<-lm(ave.hosp.days ~ ave.age.pat + infect.risk
+ xrayrat + as.factor(region) + patday + nurses)
summary(reg.lm2)
Residuals:
Min
-2.368
1Q
Median
-0.7176 -0.02696
3Q
0.5994
Max
6.268
Coefficients:
Value
(Intercept)
2.0039
ave.age.pat 0.0724
infect.risk
0.4559
xrayrat
0.0136
as.factor(region)1 -0.4658
as.factor(region)2 -0.2527
as.factor(region)3 -0.3216
patday
0.0097
nurses
-0.0072
Std. Error
1.5574
0.0268
0.1086
0.0070
0.1653
0.0906
0.0871
0.0019
0.0021
t value
1.2867
2.6961
4.1992
1.9231
-2.8173
-2.7890
-3.6935
5.1522
-3.5009
Pr(>|t|)
0.2011
0.0082
0.0001
0.0572
0.0058
0.0063
0.0004
0.0000
0.0007
Tous les tests t partiels (partiels parce que toutes les autres variables sont
laissées dans le modèle quand on teste la nullité d'un coefficient particulier),
sont significatifs à près de 5%. C'est donc un modèle qui mérite d'être considéré.
Les signes des coefficients de la régression s'interprètent bien. La variable
réponse est le nombre de jours passés à l'hôpital. L'âge moyen des patients, le
risque d'infection, le taux de rayons X, le nombre moyen de patients par jour à
l'hôpital, sont toutes corrélées positivement avec la variable réponse.
Pour tester un modèle M0 à p0 < p covariables (plus parcimonieux),
C. Huber
Régression linéaire
53
contre le modèle M1 à p covariables :
F = [RegSS(M 0) – RegSS(M 1)]/(p-p0) / (ErrSS(M1)/(n-p))
(4.11)
Sous M0 F ∼ Fisherp-p0,n-p .
nombre des covariables
↓
:
ErrSS ↑
mais
stabilité ↑
Au lieu de comparer les sommes des carrés des erreurs ErrSS, on compare les
coefficients de Mallow Cp :
Cp = {ErrSS(p)/ s2(k)} – (n – 2p)
(4.12)
ErrSS(p) = somme des carrés résiduels de (4.4) pour un modèle à p variables,
s2(k)
= estimateur de la variance de l'erreur issu de (4.4)
pour le modèle complet comprenant les k>p variables.
Si le modèle à p covariables est correct, on a deux estimateurs de σ2 :
s2(k) =ErrSS(k)/(n-k)
s2(p)=ErrSS(p)/(n-p)
Donc Cp est à peu près égal à (n-p)-(n-2p) = p. On peut donc comparer
différents modèles en utilisant Cp, et chercher le modèle qui a la valeur de
Cp/p la plus proche de 1.
Une fois le modèle choisi
Regarder de près les résidus :
⇒
on doit le valider.
certains sont ils anormalement grands ?
SPLUS a une fonction qui donne plusieurs graphes :
• Les résidus e i en fonction des valeurs prédites (yi-hat)=Hyi
• Un Q-Q plot des résidus ordonnés en fonction des quantiles correspondants
de la loi normale standard.
• La distance de Cook pour chacune des observations.
Si le Q-Q plot est presque linéaire, les résidus suivent approximativement une
loi normale (comme le modèle le suppose).
La distance de Cook pour une observation i est donnée par
C. Huber
Régression linéaire
n
Di =
^
54
^
∑ ( y j − y j (i ) )
j =1
( p + 1) s 2
2
2
 ei 
hii
=
.

 s. e.(ei )  ( p + 1)(1 − hii )
(4.13)
La distance de Cook, Di , est une mesure standardisée de l'écart de
l'observation i par rapport à sa prédiction et de la variation dans les
données prédites due à la suppression de l'observation i.
Premier terme dans Di = le résidu standardisé pour l'observation i.
Second terme
= une mesure de l'influence de cette observation
sur l'estimation du modèle.
hii
= le ième élément diagonal de la matrice H-chapeau.
S'il est grand, cette observation exerce
une grande influence sur le modèle
et change toute la régression.
yj(i) – chapeau
= les valeurs prédites pour les observations yj
lorsqu'on a enlevé l'observation i.
La distance de Cook est une distance globale qui mesure à la fois, pour une
observation donnée, le fait d'être à l'écart du gros des observations, et celui
d'avoir un effet exceptionnellement important sur le modèle.
Les résidus standardisés sont préférables aux résidus :
résidus standards = ei / s.e.( ei)
non donnés par SPLUS,
mais calculés en utilisant les éléments diagonaux de la matrice H chapeau et les
résidus ordinaires e i :
s.e.( ei) = s *sqrt(1- hii)
(4.14)
s = l'écart-type du modèle calculé par SPLUS.
La statistique R 2 multiple est une mesure du pouvoir prédictif du modèle. Pour
un modèle à p covariables et une seule réponse, il est défini comme :
R2 = Correlation(y, y-hat)2
Les deux statistiques R2 et F sont reliées par
C. Huber
(4.15)
Régression linéaire
55
F = ((R2 / (1- R2))((n-p)/p)
où F est le rapport F de (4.7). Les statistiques F et R2 sont toutes les deux des
mesures de l'adéquation du modèle.
Pour le modèle lm2 :
Il y a quelques corrélations élevées parmi les variables. Or ce n'est pas
souhaitable car cela conduit à des estimateurs instables des coefficients beta.
Mais comme nous avons déjà décidé d'éliminer du modèle certaines variables,
on peut espérer que cela a fait disparaître certaines des corrélations les plus
élevées. Voici le second modèle qui ne contient ni cultrat, ni nbeds, ni medschl,
ni pctsrvices.
reg.lm2<-lm(ave.hosp.days ~ ave.age.pat + infect.risk
+ xrayrat + as.factor(region) + patday + nurses)
summary(reg.lm2)
Residuals: (modèle lm2)
Min
1Q
Median 3Q
-2.368 -0.7176 -0.02696 0.5994
Coefficients: (modèle lm2)
Value
(Intercept) 2.0039
ave.age.pat 0.0724
infect.risk 0.4559
xrayrat
0.0136
as.factor(region)1 -0.4658
as.factor(region)2 -0.2527
as.factor(region)3 -0.3216
patday
0.0097
nurses
-0.0072
Max
6.268
Std. Error
1.5574
0.0268
0.1086
0.0070
0.1653
0.0906
0.0871
0.0019
0.0021
t value
1.2867
2.6961
4.1992
1.9231
-2.8173
-2.7890
-3.6935
5.1522
-3.5009
Pr(>|t|)
0.2011
0.0082
0.0001
0.0572
0.0058
0.0063
0.0004
0.0000
0.0007
Tous les tests t partiels (partiels parce que toutes les autres variables sont
laissées dans le modèle quand on teste la nullité d'un coefficient particulier),
sont significatifs à près de 5%. C'est donc un modèle qui mérite d'être considéré.
Les signes des coefficients de la régression s'interprètent bien. La variable
réponse est le nombre de jours passés à l'hôpital. L'âge moyen des patients, le
risque d'infection, le taux de rayons X, le nombre moyen de patients par jour à
l'hôpital, sont toutes corrélées positivement avec la variable réponse.
Surprenant : la corrélation négative avec le nombre des infirmières. Il arrive
parfois qu'un signe surprenant soit dû à une colinéarité, c'est à dire à une
corrélation très élevée parmi les variables. C'est le cas ici entre patday et nurses.
C. Huber
Régression linéaire
56
Pour le modèle lm2 :
Residual standard error: 1.232 on 104 degrees of freedom
Multiple R-Squared: 0.6143
F-statistic: 20.7 on 8 and 104 degrees of freedom, the p-value is 0
Remarquons que cet R2 n'est pas beaucoup plus petit que celui du modèle
complet..
Correlation of Coefficients:
(Intercept) ave.age.pat infect.risk xrayrat as.factor(region)1
ave.age.pat -0.9302
infect.risk
-0.0737 -0.0335
xrayrat
-0.2888 0.0383 -0.4282
as.factor(region)1 -0.2503 0.2024
0.0712
0.1105
as.factor(region)2 -0.0753 -0.0437
0.1154
0.1994
0.0815
as.factor(region)3 -0.0041 -0.0110 -0.1079
0.1461
0.0217
patday
0.0342 -0.0501 -0.1101
0.0680
-0.0357
nurses
-0.0797 0.0847 -0.0836
0.0002
0.0286
as.factor(region)2 as.factor(region)3 patday
as.factor(region)3 0.0476
patday
-0.0539
0.2537
nurses
0.0560
-0.1829
-0.8930
Quelques graphes de diagnostic pour ce modèle :
par(mfrow=c(2,2))
plot(reg.lm2)
0.4
0.6
0.8
1.0
0.0
0.20
Cook's Distance
0.0
43
-2
0.2
112
47
0.10
6
4
0
2
ave.hosp.days
0
2
4
-2
0.0
0.30
Residuals
6
Fitted Values
# crée 4 graphes sur la même page.
0.2
0.4
0.6
0.8
1.0
0
20
40
60
80
100
Index
f-value
Le graphe des résidus en fonction des valeurs ajustées montre que plusieurs
observations, identifiées par SPLUS comme étant la 47 et, peut être la 101 et la
43, pourraient être des outliers (valeurs aberrantes). Le graphe des coefficients
C. Huber
Régression linéaire
57
de Cook montre que certaines des observations ont des coefficients de Cook
très importants. Quand on choisit le meilleur modèle, on est amené à ôter une
observation ou même davantage. Pour l'instant, essayons de voir si le fait de
laisser tomber une autre variable du modèle 2 est justifié, en utilisant la fonction
drop :
drop1(reg.lm2)
Single term deletions (Suppression de facteurs un par un)
On part du modèle lm2 :
ave.hosp.days ~ ave.age.pat + infect.risk + xrayrat + + as.factor(region) +
patday + nurses
Df
Sum of Sq
RSS
Cp
<none>
157.8524
185.1730
ave.age.pat
1
11.03305
168.8854
193.1704
infect.risk
1
26.76431
184.6167
208.9017
xrayrat
1
5.61331
163.4657
187.7507
as.factor(region) 3
40.84912
198.7015
216.9152
patday
1
40.29053
198.1429
222.4279
nurses
1
18.60275
176.4551
200.7401
Aucun des modèles ayant une variable de moins que le modèle 2 n'a une valeur
de Cp inférieure à celle du modèle courant lm2.
On conserve donc le modèle courant lm2.
En SPLUS Cp n'est pas exactement le coefficient de Mallows, mais le critère
AIC (Akaike Information Criterion) :
AIC
= 2 (-maximized log likelihood + # paramètres)
= deviance + 2p .
qui, pour le modèle linéaire normal, est relié au coefficient de Mallows par
la formule :
AIC = (Cp + n) s2(modèle complet)
Cela explique pourquoi les valeurs de Cp ci-dessus sont si grandes et éloignées
de p qui vaut 5 :
Ici s 2(modèle complet) = 1.2322 pour 104 degrés de liberté pour le modèle lm2.
Aussi les valeurs de AIC se traduisent en Cp = (AIC/1.2322) – 113 = 9.0. Ce
n'est pas une très bonne valeur. Aucun des modèles ci-dessus n'a une valeur de
C. Huber
Régression linéaire
58
Cp proche de 1, mais notre modèle a un Cp plus petit que n'importe quel modèle
plus petit (ayant moins de paramètres); aussi retenons nous ce modèle.
Mais nous pouvons encore essayer de chercher le plus petit modèle possible, en
comparant tous les modèles grâce aux critères AIC ou Cp. Pour cela,
commençons par le modèle nul (sans covariable) et effectuons une addition pas
à pas des covariables grâce à la fonction step :
reg.lm0 <- lm(ave.hosp.days~1, data=regex)
step(reg.lm0,~ave.age.pat + infect.risk + cultrat
+ xrayrat + nbeds + medschl
+ as.factor(region)
+ patday + nurses + pctservice)
Start: AIC= 416.5177
ave.hosp.days ~ 1
Single term additions (addition de facteurs un par un)
Model:
ave.hosp.days ~ 1
scale: 3.653664
Df
<none>
ave.age.pat
infect.risk
cultrat
xrayrat
nbeds
medschl
as.factor(region)
patday
nurses
pctservice
1
1
1
1
1
1
3
1
1
1
Sum of Sq
14.6041
116.4459
43.6719
59.8644
68.5419
36.0841
103.5542
91.8953
47.4069
51.7271
Step: AIC= 307.3792
ave.hosp.days ~ infect.risk
Single term deletions
Model:
ave.hosp.days ~ infect.risk
C. Huber
RSS
409.2104
394.6063
292.7645
365.5385
349.3460
340.6684
373.1263
305.6562
317.3150
361.8035
357.4832
Cp
416.5177
409.2209
307.3792 AIC min.
380.1532
363.9607
355.2831
387.7409
334.8855
331.9297
376.4181
372.0979
Régression linéaire
59
scale: 3.653664
<none>
infect.risk
Df
Sum of Sq
1
116.4459
RSS
292.7645
409.2104
Cp
307.3792
416.5177 ***ne pas supprimer
infect.risk
Single term additions
Model:
ave.hosp.days ~ infect.risk
scale: 3.653664
Df
<none>
ave.age.pat
cultrat
xrayrat
nbeds
medschl
as.factor(region)
patday
nurses
pctservice
1
1
1
1
1
3
1
1
1
Sum of Sq
14.51410
0.48032
10.18592
22.20532
12.89706
71.96682
35.01970
8.21158
9.04647
RSS
292.7645
278.2504
292.2842
282.5786
270.5592
279.8675
20.7977
257.7448
284.5529
283.7181
Cp
307.3792
300.1724
314.2062
304.5006
292.4812
301.7894
257.3343 ***
279.6668
306.4749
305.6400
add as.factor(region).
Step: AIC= 257.3343
ave.hosp.days ~ infect.risk + as.factor(region)
On regarde maintenant si des termes peuvent être supprimés ou pas :
Single term deletions
Model:
ave.hosp.days ~ infect.risk + as.factor(region)
scale: 3.653664
Df Sum of Sq
<none>
RSS
220.7977
C. Huber
Cp
257.3343
Régression linéaire
infect.risk
as.factor(region)
1 84.85849
3 71.96682
305.6562
292.7645
60
334.8855 ***ne pas enlever
307.3792 ***ne pas enlever
Essayons d'ajouter des termes au modèle :
Addition d'un seul terme au modèle :
Model:
ave.hosp.days ~ infect.risk + as.factor(region)
scale: 3.653664
<none>
ave.age.pat
cultrat
xrayrat
nbeds
medschl
patday
nurses
pctservice
Df
Sum of Sq
1
1
1
1
1
1
1
1
11.18836
2.02117
2.08909
18.51643
11.47893
25.67973
6.16604
3.90313
RSS
220.7977
209.6093
218.7765
218.7086
202.2813
209.3188
195.1180
214.6317
216.8946
Cp
257.3343
253.4533
262.6205
262.5526
246.1252
253.1627
238.9619 *** AIC le plus bas:
258.4756
ajouter
260.7385
Step: AIC= 238.9619
ave.hosp.days ~ infect.risk + as.factor(region) + patday
Suppression d'un seul terme :
Model:
ave.hosp.days ~ infect.risk + as.factor(region) + patday
scale: 3.653664
Df Sum of Sq
RSS
<none>
195.1180
infect.risk
1 42.47691 237.5949
as.factor(region) 3 62.62685 257.7448
patday
1 25.67973 220.7977
Cp
238.9619***rien à enlever
274.1315 ***pour réduire l'AIC
279.6668
257.3343
Addition d'un seul terme
Model:
ave.hosp.days ~ infect.risk + as.factor(region) + patday
scale: 3.653664
Df
Sum of Sq
RSS
C. Huber
Cp
Régression linéaire
<none>
ave.age.pat
cultrat
xrayrat
nbeds
medschl
nurses
pctservice
1
1
1
1
1
1
1
13.04477
0.71858
4.97092
10.60844
0.21356
21.19919
7.21208
195.1180
182.0732
194.3994
190.1471
184.5095
194.9044
173.9188
187.9059
61
238.9619
233.2245
245.5507
241.2984
235.6608
246.0557
225.0701***AIC le plus bas:
239.0572
ajouter nurses.
Step: AIC= 225.0701
ave.hosp.days ~ infect.risk + as.factor(region) + patday + nurses
Suppression d'un seul terme :
ave.hosp.days ~ infect.risk + as.factor(region) + patday + nurses
Df
<none>
infect.risk
as.factor(region)
patday
nurses
1
3
1
1
Sum of Sq
RSS
173.9188
47.747 221.6662
55.443 229.3624
40.712 214.6317
21.199 195.1180
Cp
225.0701***aucun terme à
265.5102
supprimer
258.5917
258.4756
238.9619
Addition d'un seul terme
ave.hosp.days ~ infect.risk + as.factor(region) + patday + nurses
<none>
ave.age.pat
cultrat
xrayrat
nbeds
medschl
pctservice
Df Sum of Sq
RSS
173.9188
1 10.4530 163.4657
1 0.02582 173.8930
1 5.03334 168.8854
1 3.72763 170.1912
1 0.60214 173.3166
1 2.28498 171.6338
Cp
225.0701
221.9243 ***ajouter
232.3516
227.3441
228.6498
231.7753
230.0924
Step: AIC= 221.9243
ave.hosp.days ~ infect.risk + as.factor(region) + patday + nurses + ave.age.pat
Suppression d'un seul terme :
ave.hosp.days ~ infect.risk + as.factor(region) + patday + nurses + ave.age.pat
<none>
infect.risk
Df
Sum of Sq
1
46.88
RSS
163.46
210.35
C. Huber
Cp
221.9243
261.5040
Régression linéaire
as.factor(region)
patday
nurses
ave.age.pat
3
1
1
1
51.89
38.44
18.60
10.45
215.36
201.91
182.07
173.91
62
251.9000 ***rien à supprimer
253.0655
233.2245
225.0701
Addition d'un seul terme
Model:
ave.hosp.days ~ infect.risk + as.factor(region) + patday + nurses + ave.age.pat
Df
Sum of Sq
RSS
Cp
<none>
163.4657
221.9243
cultrat
1
0.769685
162.6960
228.4620
xrayrat
1
5.613306
157.8524
223.6183
nbeds
1
4.286508
159.1792
224.9451
medschl
1
1.385230
162.0805
227.8464
pctservice
1
2.612122
160.8536
226.6195
Conclusion : Rien à ajouter
On voit que lors de ce processus pas à pas, le modèle finalement choisi est
celui-ci :
lm(formula = ave.hosp.days ~ infect.risk + as.factor(region) + patday + nurses +
ave.age.pat, data = regex)
Coefficients:
(Intercept) infect.risk as.factor(region)1 as.factor(region)2 as.factor(region)3
2.868829 0.5452886
-0.5009072
-0.2874727
-0.3461238
patday
nurses
ave.age.pat
0.009444597 -0.007200022 0.07040985
Degrees of freedom: 113 total; 105 residual
Residual standard error (on weighted scale): 1.247724
Le modèle suggéré a 5 variables (dont une catégorielle à 3 degrés de liberté).
Notre modèle 2 (lm2) avait 6 variables :
ave.age.pat + infect.risk + xrayrat + as.factor(region) + patday + nurses
AIC = 185.17
Le "meilleur" modèle (lm5) obtenu par la procédure pas à pas a 5 variables:
infect.risk + as.factor(region) + patday + nurses + ave.age.pat
AIC = 221.92.
hosp.lm5<-lm(ave.hosp.days ~ infect.risk +
as.factor(region) + patday + nurses + ave.age.pat)
C. Huber
Régression linéaire
63
summary(hosp.lm5)
Call: lm(formula = ave.hosp.days ~ infect.risk + as.factor(region) + patday +
nurses + ave.age.pat, data = regex)
Residuals:
Min
1Q
Median
3Q
Max
-2.645 -0.7749
-0.1307 0.7114
6.455
Coefficients: (modèle hosp.lm5)
Value
Std. Error
t value
(Intercept)
2.8688
1.5101
1.8998
infect.risk
0.5453
0.0994
5.4879
as.factor(region)1
-0.5009
0.1664
-3.0101
as.factor(region)2
-0.2875
0.0899
-3.1965
as.factor(region)3
-0.3461
0.0872
-3.9671
patday
0.0094
0.0019
4.9696
nurses
-0.0072
0.0021
-3.4572
ave.age.pat
0.0704
0.0272
2.5912
Residual standard error: 1.248 on 105 degrees of freedom
Multiple R-Squared: 0.6005
F-statistic: 22.55 on 7 and 105 degrees of freedom, the p-value is 0
Pr(>|t|)
0.0602
0.0000
0.0033
0.0018
0.0001
0.0000
0.0008
0.0109
hats <- lm.influence(hosp.lm5)$hat
std.resid <- resid(hosp.lm5)/((1.066)*(1-hats)^.5)
std.resid[abs(std.resid)>2]
n°obs
résidu std
10
-2.194446
43
3.003277
65
-2.118423
101
2.547981
112
3.475738
Nous avons calculé les résidus standardisés (std.resid) selon la formule (4.14) cidessus. Le plus grand résidu (non standardisé) dépasse 6, mais après
standardisation, le plus grand des résidus devient beaucoup plus raisonnable :
3.48.
On a maintenant l'impression qu'on ne doit exclure aucune observation. Traçons
les résidus standardisés en fonction des valeurs prédites dans le modèle
sélectionné par la procédure pas à pas.
plot(fitted(hosp.lm5),std.resid,xlab="fitted",
ylab="standardized residuals")
title("Donnees hospitalieres : Meilleur Modele pas
a pas")
C. Huber
Régression linéaire
64
Bien qu'il y ait quelques grand résidus, pour les observations 10, 43, 65, 101, et
112, le graphe ci-dessous montre qu'on ne devrait pas rejeter le modèle
simplement à cause de 5 grands résidus standardisés.
Les résidus standardisés sont approximativement des variables normales
standard. La probabilité que l'une d'elles excède 2 en valeur absolue est environ
.05. Aussi, sur 113 résidus il faudrait s'attendre à en avoir 113*.05 = 5.65 plus
grands que 2 en valeur absolue.
Modèle ml5 :
2
0
-2
standardized residuals
4
6
Donnees hospitalieres : Meilleur Modele pas a pas
8
10
12
fitted
Faisons la même analyse pour le modèle ml2 :
(voir les commandes ci-dessous)
C. Huber
14
16
Régression linéaire
65
2
-2
0
residus standardises
4
6
Donnees hospitalieres : Meilleur Modele obtenu directement
8
10
12
14
16
valeurs predites
1) résidus standardisés pour le modèle lm2
hats.2 <- lm.influence(reg.lm2)$hat
std.resid.2 <- resid(reg.lm2)/((1.066)*(1-hats.2)^.5)
2) résidus standardisés supérieurs à 2 pour lm2
std.resid.2[abs(std.resid.2)>2]
10
43
-2.320521 3.086785
47
6.148208
87
-2.120281
101
2.491783
112
2.541829
Pour obtenir le graphe :
plot(fitted(reg.lm2),std.resid.2,xlab="valeurs
predites",ylab="residus standardises")
title("Donnees hospitalieres : Meilleur Modele obtenu
directement")
Il est difficile de choisir entre les deux modèles :
Modèle reg.lm2 :
ave.hosp.days ~ ave.age.pat + infect.risk + as.factor(region) + patday + nurses
+ xrayrat
Model hosp.lm5 :
C. Huber
Régression linéaire
66
ave.hosp.days ~ ave.age.pat + infect.risk + as.factor(region) + patday + nurses
La différence entre les deux modèles consiste en l'addition de xrayrat dans le
modèle lm2. Cela abaisse la valeur du AIC , mais donne des valeurs plus
grandes aux résidus standardisés. Aussi l'un et l'autre choix peuvent se justifier.
Une autre façon de choisir entre ces deux modèles est de les valider en estimant
le modèle sur les 57 premières observations et ensuite en prédisant les valeurs
des 56 observations restantes grâce aux coefficients estimés. Cette méthode est
appelée la validation croisée (cross validation, en anglais).
Exemple 2.
ANALYSE de VARIANCE à UN FACTEUR :
Durée de coagulation du sang pour 4 régimes A B C D .
Objectif : déterminer s'il existe une différence significative entre les distributions
de la durée de coagulation pour les différents traitements (diet,
régime).
Le data.frame coag.df contient les variables :
coag (coagulation time) et regime (diet) pour chaque sujet expérimental.
L'ANOVA à un facteur est une généralisation directe du test t pour deux
échantillons. Ici, on compare les moyennes de k populations au lieu de
seulement 2, et nos données consistent en k échantillons indépendants qui
n'ont pas forcément la même taille. Les populations sont supposées normales,
de même variance, mais avec des moyennes qui ne sont pas forcément les
mêmes.
On teste
H0 :µ 1 = µ 2 =...= µ κ contre
H1 : au moins deux des k moyennes sont différentes.
On suppose que σ1 = σ2 =...= σκ
Ce n'est en fait rien d'autre qu'un problème de régression linéaire avec une
variable explicative catégorielle. Par conséquent, tous les outils développés
pour la régression linéaire vont pouvoir être utilisés ici aussi.
Commençons pas tracer le graphe représentant les données :
par(mfrow=c(1,2))
# crée deux graphes sur une seule page.
plot.design(coag.df)
# trace les moyennes des facteurs:
# les moyennes pour chaque sousC. Huber
Régression linéaire
67
# échantillon correspondant à un
# régime particulier.
plot.design(coag.df,fun=median)
# trace les médianes des facteurs.
title("Moyennes et medianes du temps de coagulation
pour 4 regimes")
# titre du graphe design.
par(mfrow=c(1,1))
# crée un graphe par page
plot.factor(coag.df)
# boxplot pour la coagulation
# pour chaque régime.
diet subpopulation title("BOX PLOTS FOR COAGULATION
TIMES IN 4 DIETS")
On note des différences très importantes entre les médianes pour les différents
régimes, ainsi que quelques différences entre les distributions du temps de
coagulation. Cela entraîne que le régime va être un facteur prédictif très
important pour la durée de coagulation.
Pour confirmer cette hypothèse, faisons une analyse de variance :
coag.aov <- aov(coag ~ diet, coag.df)
# effectue l'analyse de variance.
summary(coag.aov)
Df Sum of Sq
diet 3
228
Residuals 20
112
Mean Sq
76.0
5.6
F Value
13.57143
Pr(F)
0.00004658471
Ce degré de signification (0.00004658471) très faible confirme notre
hypothèse selon laquelle le régime est un excellent prédicteur de la durée
coagulation.
hist(resid(coag.aov))
# trace l'histogramme des résidus.
Les résidus ne sont pas standardisés.
Les résidus standardisés (ou studentisés), qui ne sont pas fournis par SPLUS,
sont beaucoup plus utiles pour détecter les outliers, les points leviers et la non
normalité des résidus.
Il faut créer une fonction : on l'appellera stdres
Elle peut être utilisée avec n'importe quel modèle linéaire créé par la fonction
C. Huber
Régression linéaire
68
lm ou aov.
Notre fonction stdres a comme arguments : l'écart-type du modèle (standard
error of the model), le nombre des observations n, le nombre des covariables
p (count (k-1) pour un factor à k catégories), le vecteur des résidus, et le
vecteur des points leviers qui contient les éléments diagonaux de la matrice
chapeau H .
Initialisons les arguments pour notre exemple.
stderror <- 2.366432
n<-24
p<-3
residuals <- resid(coag.aov)
lev<-lm.influence(coag.aov)$hat
# levier(leverage).
La fonction "résidus studentisés" : stdres
# Voici la fonction stdres qui donne les résidus studentisés.
stdres<-function(n, p, residuals, lev, stderror)
{
standres <- residuals/(stderror *
(1 - lev)^0.5)
stdresid <- standres/((n - p - standres^2)
/(n - p - 1))^0.5
return(stdresid)
}
Appelons la fonction stdres :
stdres.aov <- stdres(n, p, residuals, lev, stderror)
stdres.aov
# affiche les résidus studentisés.
1
2
3
4
5
6
7
0.4789131 -0.4789131 0.9747403 -0.9747403 -1.422136 0.4540766 2.617119
8
9
10
11
12
13
-0.9225312 -0.4540766 6.269345e-017 1.379256e-016 -0.9225312 1.422136
14
15
16
17
18
19
-0.4540766 1.379256e-016 1.379256e-016 -2.533473 0.4430246 -0.4430246
20
21
22
23
24
-1.223651e-017 0.8993875 1.384533 0.8993875 -0.8993875
Utilisons les résidus studentisés pour faire quelques graphes de diagnostic :
C. Huber
Régression linéaire
69
plot(fitted(coag.aov),stdres.aov,xlab="valeurs
predites pour la duree de coagulation",
ylab="residus standardises de coagulation")
title(" RESIDUS STANDARDISES VS VALEURS PREDITES
POUR LA COAGULATION")
1
0
-1
-2
residus standardises de coagulation
2
RESIDUS STANDARDISES VS VALEURS PREDITES
POUR LA COAGULATION
62
64
66
68
valeurs
predites pour la duree de coagulation
plot(coag,stdres.aov,xlab=" coagulation",
ylab="residus studentises de la coagulation")
title("RESIDUS STANDARDISES VS VALEURS OBSERVEES
POUR LA COAGULATION ")
C. Huber
Régression linéaire
70
1
0
-1
-2
residus studentises de la coagulation
2
RESIDUS STANDARDISES VS VALEURS OBSERVEES
POUR LA COAGULATION
60
65
70
coagulation
Aucun de ces tracés ne suggère de problème avec notre analyse de variance.
Exemple 3 : ANALYSE DE VARIANCE A DEUX FACTEURS
kidney1
Données sur la récurrence d'infections au point d'insertion d'un cathéter, pour
des patients ayant une affection rénale et qui utilisent un équipement de
dialyse portable.
On cherche à faire une analyse de variance à deux facteurs pour la durée
d'infection par rapport au sexe et au type de la maladie.
options(contrasts="contr.treatment")
# pour interpréter les niveaux du facteur
# comme des différences par rapport au niveau 1.
infect.lm <- lm(infectime~sex + dis.fac,
data=kidney1)
summary(infect.lm)
Call: lm(formula = infectime ~ sex + dis.fac, data = kidney1)
Residuals:
Min 1Q
-158.9 -75.1
Median
-35.21
3Q
31.48
C. Huber
Max
464.2
Régression linéaire
71
Coefficients:
Value Std. Error t value
(Intercept)
-22.4631 64.7526 -0.3469
sex
67.1112 34.3540 1.9535
dis.fac1 -30.1574 40.4138 -0.7462
dis.fac2 53.1713 54.8191 0.9699
dis.fac3 33.4971 39.4987 0.8481
Pr(>|t|)
0.7297
0.0547
0.4580
0.3354 dis.fac peut être jeté.
0.3993
Residual standard error: 128.3 on 71 degrees of freedom
Multiple R-Squared: 0.09069
F-statistic: 1.77 on 4 and 71 degrees of freedom, the p-value is 0.1444
Correlation of Coefficients:
(Intercept) sex dis.fac1 dis.fac2
sex
-0.8842
dis.fac1
-0.2242 -0.1417
dis.fac2
-0.3500
0.1044 0.3980
dis.fac3
-0.2787 -0.0892 0.5856 0.4131
Laisser dis.fac :
infect.aov.1 <- aov(infectime~ as.factor(sex) ,
data=kidney1)
summary(infect.aov.1)
Df
S um of Sq Mean Sq
F Value
Pr(F)
as.factor(sex) 1
48639
48638.98
2.91028 0.09221072
Residuals 74
1236749
16712.82
C'est équivalent au test t pour deux échantillons.
Residual standard error: 129.3 on 74 degrees of freedom
Multiple R-Squared: 0.03784
F-statistic: 2.91 on 1 and 74 degrees of freedom, the p-value is 0.09221
Pour faire une analyse de variance avec interaction entre sexe et
factor(disease)=dis.fac faire
infect.aov <- aov(infectime~ sex*dis.fac ,
data=kidney1)
infect.aov
Call:
aov(formula = infectime ~ sex * dis.fac, data = kidney1)
C. Huber
Régression linéaire
72
Terms:
sex
Sum of Squares 48639
Deg. of Freedom
1
dis.fac
67930
3
sex:dis.fac Residuals
105336
1063482
3
68
Residual standard error: 125.0578
summary(infect.aov)
Df
sex
1
dis.fac
3
sex:dis.fac 3
Residuals 68
Sum of Sq Mean Sq
48639
48638.98
67930
22643.31
105336
35112.11
1063482
15639.45
F Value
3.110019
1.447833
2.245099
Pr(F)
0.0823042
0.2365862
0.0909007
On ne peut pas dire grand-chose car ni les effets principaux du sexe et du type
de la maladie, ni leur interaction ne sont significatifs. Mais nous pouvons
essayer le sexe et l'interaction sex : dis.fac :
infect.aov.2 <- aov(infectime~ sex + sex:dis.fac ,
data=kidney1)
summary(infect.aov.2)
Df
sex 1
sex:dis.fac 3
Residuals 71
Sum of Sq
48639
62101
1174647
Mean Sq
48638.98
20700.46
16544.33
F Value Pr(F)
2.939919 0.0907773
1.251212 0.2977584
Mais on ne voit rien là non plus. Aucune combinaison du sexe et du type de
la maladie n'explique la variation de la durée d'infection. Bien sûr, rappelons
nous que ces durées sont censurées : c'est le minimum entre la durée
d'infection et la durée jusqu'au retrait de l'étude par départ de l'hôpital par
exemple.
Nous étudierons plus à fond cet exemple dans le chapitre sur les durées de
survie censurées. Ici, nous regardons seulement le mécanisme de l'ANOVA.
Essayons pour finir la transformation en log de l'infectime pour le rendre plus
normal, et voyons ce qui arrive :
.
loginfect <-log(infectime)
infect.aov.3 <- aov(loginfect~ sex*dis.fac ,
data=kidney1)
summary(infect.aov.3)
Df Sum of Sq
C. Huber
Mean Sq
F Value
Pr(F)
Régression linéaire
sex 1
dis.fac 3
sex:dis.fac 3
Residuals 68
73
16.2654 16.26542 10.15409 0.0021754
4.4036 1.46787 0.91635 0.4377560
8.2796 2.75985 1.72291 0.1704922
108.9264 1.60186
Cette transformation fait peu de différence, aussi essayerons nous le test non
paramétrique de Kruskal-Wallis, kruskal.test , qui ne suppose pas la
normalité.
Faisons d'abord deux analyses de variance à un facteur : pour le factor
maladie (dis.fac), puis pour le factor sex.
kruskal.test(infectime,dis.fac)
Kruskal-Wallis rank sum test
data: infectime and dis.fac
# dis.fac est disease factor
Kruskal-Wallis chi-square = 1.1862, df = 3, p-value = 0.7563
alternative hypothesis: two.sided
La maladie par elle-même n'est pas un élément déterminant pour
expliquer la variation de la durée jusqu'à l'infection.
kruskal.test(infectime,as.factor(sex))
Kruskal-Wallis rank sum test
data: infectime and as.factor(sex)
Kruskal-Wallis chi-square = 8.8049, df = 1, p-value = 0.003
alternative hypothesis: two.sided
Par contre le sexe est significatif : il contribue à expliquer la variabilité
de la durée d'infection.
Malheureusement, le test de Kruskal-Wallis ne fait pas d'analyse de variance
à deux facteurs.
On crée donc un nouveau facteur dd à 8 catégories, une pour chacune des
combinaisons de sex (1-2) et de maladie (0-3).
dd<-matrix(0,76,1)
ind1<-(sex==1)*(1:76)
# ind1 contient les indices des patients mâles.
ind<-(sex==2)*(1:76)
# ind contient les indices des patients
# féminins.
C. Huber
Régression linéaire
74
dis<-matrix(disease,76,1)
# fait une matrice du vecteur des maladies.
dd[ind,1]<-dis[ind,1]
# dd sera égal aux maladies pour les femmes.
dd[ind1,1]<-dis[ind1,1]+4
# dd sera égal à 4 + maladie pour les mâles.
kruskal.test(infectime,as.factor(dd))
# kruskal.test requiert un facteur (factor)
# comme second argument
Kruskal-Wallis rank sum test
data: infectime and as.factor(dd)
Kruskal-Wallis chi-square = 14.7834, df = 7, p-value = 0.0389
alternative hypothesis: two.sided
On conclut que le sexe, la maladie et leur interaction sont significatifs pour
expliquer la durée d'infection. Cette procédure n'est cependant pas
satisfaisante car nous ne savons pas comment faire une analyse de variance
pour les deux facteurs et leur interaction. C'est cependant instructif de voir
comment manipuler les matrices et leurs indices.
Remarque importante à propos des transformations :
On est parfois amené à transformer des variables pour se rapprocher des
hypothèses de normalité et avoir une variance constante pour la variable
réponse. Or même si les variables binomiales et Poisson sont presque
normales asymptotiquement, elles ont une variance qui est liée à la moyenne,
qui, elle, est variable d'une observation à l'autre ; elle est modélisée comme
une fonction linéaire des variables explicatives. En particulier, si la variable
réponse Y est un effectif qui suit une loi de Poisson, on la transforme en
prenant √ Y car cela stabilise la variance. En effet, la "delta-méthode" nous dit
que si
n ( X n − µ ) → N(0, σ 2 ) , alors si g est une fonction dérivable,
2
n (g( X n ) − g(µ)) → N(0, σ 2 (g' (µ)) 2 ) . Or pour une variable de Poisson, σ = λ, ce
qui donne g'(λ) = 1/√λ. De même pour la binomiale, g est telle que sin(g(x))
= √x, soit g(x) = Arcsin(√
√ x).
C. Huber
Courbes ROC. Bootstrap.
75
Chapitre 5 .
Sensibilité et spécificité d'une procédure de discrimination.
La courbe ROC.
Utilisation du Bootstrap.
1 Définitions:
Supposons qu'une population soit répartie en deux classes notées respectivement 1 et 0.
Mais, au lieu d'observer directement la classe à laquelle appartient un sujet, on dispose de
renseignements sur chaque sujet, permettant d'en déduire la classe à laquelle il appartient
avec une certaine probabilité d'erreur.
C'est par exemple la situation dans laquelle on se trouve en général lorsqu'on établit un
diagnostic, ou, lorsqu'en épidémiologie on cherche à classer les sujets en sujets "à risque" ou
non par rapport à une maladie donnée.
On a donc recours à une procédure de classification. La qualité de la méthode de
classification est mesurée par deux indicateurs, sa sensibilité et sa spécificité, définies cidessous :
1 Sensibilité se :
c'est la proportion des cas, parmi les sujet de type 1, qui sont bien
classés par la procédure. Autrement dit, si on note X la vraie classe du sujet
et Y la classe du sujet prédite par la méthode de classification,
se = P(Y=1| X=1) .
2 Spécificité sp : c'est la proportion des cas, parmi les sujets de type 0, qui sont bien classés
par la procédure, c'est à dire
sp = P(Y=0| X=0) .
3 Courbe ROC : c'est la courbe représentative de la sensibilité en fonction de (1 – la
spécificité), c'est à dire
se = f(1 – sp) .
On utilise la courbe ROC (de l'anglais : Receiver Operating Characteristic curve) d'une
procédure de discrimination pour mesurer globalement sa capacité à affecter correctement les
sujets à leur classe respective.
2. Un exemple simple:
INSUFFISANCE RESPIRATOIRE : SENSIBILITE, SPECIFICITE ET COURBE
ROC :
La quantité d'air, en litres, rejetée par un sujet sain lors d'une expiration forcée est une
variable aléatoire X qui est supposée normale N(µ = 2.65 ; σ2 =0.5) et la capacité respiratoire
C. Huber
Courbes ROC. Bootstrap.
Y est la somme de deux expirations forcées successives séparées par un intervalle de deux
minutes et supposées indépendantes.
a) Quelle est la loi de la capacité respiratoire d'un sujet sain ?
b) Quelle est la probabilité pour qu'un sujet sain ait une capacité respiratoire inférieure à 3.3
?
Une maladie M entraîne une insuffisance respiratoire chez les sujets qui en sont atteints. La
loi de leur capacité respiratoire Y' est chez eux normale N(µ = 2.8 ; σ2 =1).
c) Quelle est la probabilité p' pour qu'un sujet atteint de M ait une capacité respiratoire
inférieure à 3.3 ?
d) Si l'on se fonde sur l'observation de Y pour diagnostiquer M, quelle règle de diagnostic
proposez vous ?
e) Avec cette règle quels sont les risques d'erreur que vous prenez ?
f) Si on appelle D le diagnostic, qui vaut 1 si on diagnostique M et 0 sinon, on appelle
sensibilité (se) la probabilité d'un bon diagnostic de M et spécificité (sp) la probabilité
d'un bon diagnostic de non M.
Quand on change de seuil, comment évolue se en fonction de 1-sp ?
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
a) La loi de la somme de deux v.a. normales indépendantes est normale de moyenne la
somme des moyennes et de variance la somme des variances:
L(Y) = N((µ
µ = 5.3 ; σ2 = 1)
b) p = P(Y < 1.3) = P(Z+5.3 < 3.3) = P(Z < -2) = 1 - P(Z < 2)
= 1 – 0.9772
= 0.0228
c) p' = P(Y' < 3.3) = P(Z+2.8 < 3.3) = P(Z < 0.5) = 1 - P(Z < 0.5)
= 1 – 0.6915
= 0.3085
d) D = 1 si y < 3.3
et D = 0 si y > 3.3.
Risques d'erreur : P(D=0| M=1) = P(Y'>3.3) = 1- p' = 0.6915
P(D=1| M=0) = P(Y<3.3) = p = 0.0228
Bonne spécificité, mais très mauvaise sensibilité.
Choisir plutôt un seuil plus élevé, par exemple celui c qui égalise les deux erreurs :
P(Z+5.3 < c) = P(Z+2.8 > c) ⇒ c-2.8 = -(c-5.3) ⇒ c = (5.3+2.8)/2 = 4.05
Ordres en Splus :
# Valeurs utiles de x étant données les deux lois normales :
seuil<-seq(0,8,0.05)
win.graph()
plot(seuil,dnorm(seuil,5.3,1),type="l",lty=1,lwd=3,
col=1,xlab = "y",ylab="densite de y et y'")
lines(seuil,dnorm(seuil,2.8,1),lty=1,lwd=3,col=2)
lines(c(4.05,4.05),c(-0.2,dnorm(4.05,2.8,1)),
C. Huber
76
Courbes ROC. Bootstrap.
77
0.2
0.0
0.1
densite de y et y'
0.3
0.4
lwd=2,col=1,lty=1)
lines(c(3.3,3.3),c(-0.2,dnorm(3.3,2.8,1)),
lwd=2,col=3,lty=1)
0
2
4
6
8
y
sens<-pnorm(seuil-2.8)
un.moins.spec<-pnorm(seuil-5.3)
seuil
sens
spec
win.graph()
plot(un.moins.spec,sens,type="l",xlab="1 specificite",ylab="sensibilite")
title(main="courbe ROC")
0.6
0.4
0.0
0.2
sensibilite
0.8
1.0
courbe ROC
0.0
0.2
0.4
0.6
0.8
1.0
1 - specificite
3. Un exemple fondé sur la régression logistique :
Au chapître sur la régression logistique, nous verrons que ce type de régression permet
d'estimer la probabilité qu'une variable réponse soit égale à 1 quand on connaît les valeurs de
plusieurs paramètres du sujet. Dans l'exemple des maladies coronariennes (fichier coronary,
p. 79), la réponse est l'existence ou non d'une maladie coronarienne à partir de deux
indicateurs : l'âge et le poids. La classification est entre (coron=1) et (coron = 0 ) étant
donnés l'âge et le poids. Sur la base de ces probabilités, on peut définir une règle de
classification de la façon suivante :
C. Huber
Courbes ROC. Bootstrap.
78
Si la probabilité est supérieure à a, classer la personne comme 1 (coron =1).
Si au contraire la probabilité est inférieure à a, la classer comme 0 (coron =0).
Bien que le seuil a = 0.5 paraisse a priori une valeur raisonnable, il n'est pas du tout évident
que ce soit exact. Pour chaque valeur de a on peut calculer la spécificité et la sensibilité, et,
quand a varie, la courbe ROC visualise les qualités de la règle de classification
correspondante.
La sensibilité est la proportion des déclarés positifs (type 1: coron =1) parmi les positifs, et
la spécificité la proportion des déclarés négatifs (type 0 : coron = 0) parmi les négatifs. Si on
choisit a = 0, on aura se = 1 et sp = 0. Cela donne le point (1,1) de la courbe ROC. En
revanche, si on choisit a = 1, on aura le point (0,0) de la courbe ROC.
La courbe ROC est croissante de (0,0) jusqu'à (1,1). Plus elle est éloignée et au-dessus de
la diagonale, meilleure est la règle de classification. Utilisons pour cela la prédiction qui est
faite de la probabilité π d'avoir une maladie coronarienne à partir de l'âge et du poids:
log(π/(1-π))= β0 + β 1 âge + β 2 poids
On obtient les trois coefficients de la régression logistique en faisant:
coron.glm.AWT<-glm(coron ~ age+wt , family = binomial,
data=coronary)
summary( coron.glm.AWT)
Et on obtient les valeurs prédites pour π en faisant :
Fit<-fitted.values(coron.glm.AWT)
mmin<-min(Fit)
mmax<-max(Fit)
mmin
# [1] 0.02003988
mmax
# [1] 0.480566
rocc<-matrix(0,24,2)
for(ii in (1:24))
{ # sensibilité
rocc[ii,1]<-sum(coron*(Fit > ii*.02))/sum(coron)
# 1 - spécificité
rocc[ii,2]<-1-sum((1-coron)*(Fit<=ii*.02))/sum(1coron)
}
rocc
[,1]
[,2]
[1,] 1.00000000 1.000000000
[2,] 1.00000000 0.850574713
[3,] 0.96153846 0.706896552
[4,] 0.92307692 0.568965517
[5,] 0.73076923 0.454022989
[6,] 0.69230769 0.339080460
C. Huber
Courbes ROC. Bootstrap.
[7,] 0.61538462 0.270114943
[8,] 0.57692308 0.241379310
[9,] 0.46153846 0.189655172
Remarquons que les deux quantités sensibilité = rocc[,1] et 1 - spécificité = rocc[,2] sont
décroissantes. Pour obtenir un tableau croissant, on doit réordonner par :
ord <- order(rocc[,2], rocc[,1])
# ce qui ordonne la deuxième colonne
# et sépare les ex-aequo en utilisant l'autre colonne.
ord
# ord contient les indices des éléments du tableau ordonné.
[1] 23 24 22 21 20 19 17 18 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
rocc1 <- rocc[ord,]
rocc1
[,1]
[,2]
[1,] 0.00000000 0.005747126
[2,] 0.00000000 0.005747126
[3,] 0.07692308 0.011494253
[4,] 0.11538462 0.011494253
[5,] 0.11538462 0.017241379
[6,] 0.11538462 0.034482759
[7,] 0.11538462 0.040229885
[8,] 0.11538462 0.040229885
[9,] 0.19230769 0.051724138
[10,] 0.19230769 0.068965517
[11,] 0.26923077 0.074712644
[12,] 0.34615385 0.097701149
#
Calcul de l'aire au-dessous de la courbe ROC
roca<-0
for(ii in 2:24)
{
delta <- rocc1[ii,2] - rocc1[ii-1,2]
roca <- roca + delta * ((rocc1[ii,1]-rocc1[ii-1])/2
+ rocc1[ii-1,1])
}
#
Aire au-dessous de la courbe :
roca
[1] 0.7390584
L'aire au-dessous de la courbe ROC vaut .74, ce qui indique un pouvoir de prédiction
assez élevé pour ce modèle logistique.
Traçons maintenant la courbe ROC elle-même :
plot(rocc1[,2],rocc1[,1],
xlab="1-Specificite",ylab="Sensibilite")
C. Huber
79
Courbes ROC. Bootstrap.
80
title("COURBE ROC POUR LE MODELE LOGISTIQUE :
coron ~ age + weight")
Maintenant que nous disposons de la courbe ROC, il devient clair que le choix de a pour
classer de futurs patients dont on connaît l'âge et le poids n'est pas évident. Les deux
indicateurs que sont la sensibilité et la spécificité sont importants tous les deux, et le choix du
seuil a doit ménager un bon équilibre entre les deux.
Bien sûr, l'aire au-dessous de la courbe, roca, ainsi que la courbe elle-même, sont aléatoires.
Il est donc intéressant d'avoir un estimateur de leur écart-type.
Comme nous ne disposons pas de formule (asymptotique) pour estimer cette erreur, nous
allons utiliser des techniques de rééchantillonnage, le BOOTSTRAP et le JACKNIFE pour ce
faire.
Le BOOTSTRAP
Pour comprendre le bootstrap, nous allons commencer avec un exemple simple. Supposons
que nous ayons un vecteur y de n = 15 observations :
y<-c(5,7,3,3,2,4,0,8,0,1,7,3,5,4,6)
y
median(y)
# [1] 4
Nous avons déjà calculé la médiane de y. Si maintenant nous voulons estimer la variance de
cet estimateur de la médiane, nous ne disposons pas comme pour la moyenne d'un estimateur
simple de cette variance. Nous allons donc construire un estimateur bootstrap de cette
variance comme suit :
1. Tirer un échantillon de taille n=15 de y avec remise.
2. Calculer la médiane de l'échantillon ainsi obtenu.
3. Stocker la médiane de l'échantillon Bootstrap dans un vecteur appelé boot.
4. Retourner en 1.
On répète ces étapes un grand nombre de fois, soit B = 1 000 fois par exemple. On obtient
ainsi un échantillon bootstrap de médianes de taille 1 000.
Ce que l'on réalise ainsi est essentiellement une reconstruction de la manière dont a été obtenu
l'échantillon et sa médiane en rééchantillonnant y à partir de lui-même.
POPULATION
→
ECHANTILLON
ECHANTILLON
→
ECHANTILLON BOOTSTRAP
La variabilité d'échantillonnage dans la population se reflète dans la variabilité
d'échantillonnage dans la sous-population y : mais, alors que nous n'avons pas accès à la
variabilité de median(y), dans la population totale, le vecteur boot illustre la variabilité de
median(y bootstrap) quand on échantillonne dans la sous-population y. Aussi utiliserons nous
la variance du vecteur boot pour estimer la variance de median(y).
Il est clair que ce que nous venons de faire pour la médiane, nous pouvons le répéter pour
estimer la variance d'autres statistiques. La suite des opérations est toujours la même :
C. Huber
Courbes ROC. Bootstrap.
1
2.
3.
4.
81
Faire un tirage au hasard avec remise de taille 15, dans l'échantillon y.
Calculer la statistique pour le nouvel échantillon bootstrap obtenu en 1.
Stocker la statistique ainsi calculée dans un vecteur nommé boot.
Retourner en 1.
Répéter ces étapes B=1000 fois.
Cependant, nous pouvons faire beaucoup d'autres choses grâce au bootstrap. Par exemple,
nous pouvons obtenir un intervalle de confiance pour le paramètre d'intérêt sans faire aucune
supposition sur la loi de son estimateur. Tout ce dont nous avons besoin, c'est d'ordonner le
vecteur boot par ordre croissant. Alors, un intervalle de confiance de coefficient de confiance
(1-a)*100% , pour le paramètre estimé par la statistique considérée, qui dans notre cas est la
médiane est donné par l'intervalle :
( boot [ B*(a/2)] , boot[B*(1-a/2)] )
Tout cela est obtenu par la fonction bootstrap en SPLUS.
Exemple 1 :
boot.obj1 <- bootstrap(y, statistic = median, B=1000,
seed = 0, trace = F)
summary(boot.obj1)
# Affichage des résultats :
Call:
bootstrap(data = y, statistic = median, B = 1000, seed = 0, trace = F)
Number of Replications: 1000
Summary Statistics:
Observed Bias
median
4
-0.133
Mean SE
3.867 0.9199
Empirical Percentiles:
2.5% 5% 95% 97.5%
median 2 3 5
6
BCa Percentiles:
2.5% 5% 95% 97.5%
median 2
2 5
5
On déduit de ce résumé que :
La médiane observée valait 4. La moyenne du vecteur boot, qui devrait être un bon estimateur
de la médiane de l'échantillon vaut 3.867. Donc, un estimateur du biais de la médiane de
l'échantillon comme estimateur de la médiane de la population est :
moyenne(médianes bootstrap) - médiane(y)
C. Huber
Courbes ROC. Bootstrap.
82
c'est à dire ici -.133. D'autre part, l'estimateur bootstrap de l'écart-type de la médiane vaut
.9199. On remarquera que ce n'est pas cet estimateur qui est utilisé pour obtenir l'intervalle de
confiance pour la médiane.
La première méthode, appelée celle des percentiles, et décrite ci-dessus, donne pour intervalle
de confiance bootstrap à 95% pour la médiane de la population [2 ; 6].
La deuxième méthode, appelée BCa (corrected percentile method, l'intervalle de confiance
bootstrap à 95% vaut [2 ; 5].
Le vecteur boot de médianes bootstrap est donné par SPLUS et peut être utilisé pour tracer un
histogramme bootstrap de la médiane pour la population :
Plot(boot.obj1)
Exemple 2 :
Cherchons maintenant un estimateur bootstrap de l'écart-type de l'aire sous la courbe ROC, et
un intervalle de confiance bootstrap à 95% pour cette aire.
Voici le programme Splus correspondant :
# rappelons que Fit<-fitted.values(coron.glm.AWT) (cf regression logistique)
mmin <- min(Fit)
mmax <- max(Fit)
delta <- .01
# nn = nombre des points en lesquels
# la sensibilité et la spécificité seront calculées.
nn <- floor((mmax - mmin) / delta)
boot.frame <- data.frame(coron, Fit)
attach(boot.frame)
ROCarea <- function(coron, Fit,nn)
{
rocc<-matrix(0,nn,2)
for(ii in (1:nn))
{
rocc[ii,1]<-sum(coron*
(Fit >ii*.02))/sum(coron)
# sensitivite
rocc[ii,2]<-1-sum
((1-coron)*(Fit<=ii*.02))/sum(1-coron)
#1-specificite
}
ord <- order(rocc[,2], rocc[,1])
rocc1 <- rocc[ord,]
# coron et Fit ordonnes
roca<-0
for(ii in 2:nn)
{
delta <- rocc1[ii,2] - rocc1[ii-1,2]
roca <- roca + delta * ((rocc1[ii,1]-rocc1[ii1])/2 + rocc1[ii-1,1])
}
C. Huber
Courbes ROC. Bootstrap.
83
c(roca)
# roca = area under the ROC curve is
# returned by the function ROCarea
}
# fin de la fonction ROCarea
boot.obj1 <- bootstrap(data = boot.frame, statistic =
ROCarea(coron,Fit,nn), B=1000, seed=0)
summary(boot.obj1)
# Affichage des résultats :
Call:
bootstrap(data = boot.frame, statistic =
ROCarea(coron, Fit, nn), B = 1000, seed = 0)
Number of Replications: 1000
Summary Statistics:
Observed Bias Mean
SE
Param 0.7391 -0.00477 0.7343 0.04811
Empirical Percentiles:
2.5% 5% 95% 97.5%
Param 0.634 0.6532 0.8101 0.826
BCa Percentiles:
2.5% 5% 95% 97.5%
Param 0.6364 0.6572 0.8134 0.8267
plot(boot.obj1)
A partir de l'histogramme de la distribution bootstrap de l'aire au-dessous de la courbe ROC,
on constate que cette distribution est remarquablement normale. Pour vérifier cette
impression, on peut faire :
qqnorm(boot.obj1)
qui donne un graphe des quantiles de la loi normale contre les quantiles des données (QQ
plot) pour l'échantillon bootstrapé des aires sous la courbe ROC. La linéarité de la courbe
obtenue suggère bien qu'en effet la loi de l'aire sous la courbe ROC est approximativement
normale.
L'intervalle de confiance à 95%, obtenu grâce aux percentiles corrigé, pour l'aire sous la
courbe ROC, vaut [.6364 ; .8267].
Par ailleurs, l'estimateur bootstrap de l'écart-type de l'estimateur de l'aire vaut .0481.
Remarquons que si on utilisait l'intervalle de confiance à 95% fondé sur l'approximation
normale, on aurait .7391 +/- 1.96*.0481 , soit [.645 ; .833]. Cet intervalle est très proche de
celui donné par les percentiles bootstrap, ce qui était prévisible après le tracé du QQ plot.
C. Huber
Courbes ROC. Bootstrap.
C. Huber
84
Modèles pour des durées de survie.
85
Chapitre 6 .
MODELES POUR DES DUREES DE SURVIE AVEC CENSURE .
1 Quelques notions fondamentales
Par définition, la fonction de survie S(t) est la probabilité qu'une personne survive au-delà
du temps t. Cela s'écrit formellement
S(t) = 1 – F(t) = P[T >t]
Et le taux de hasard, ou taux d'incidence (hazard rate en anglais) h(t) est
h(t) = f(t) / S(t).
où f(t) est la densité, f(t) = F'(t) = -S'(t). L'interprétation du taux de hasard est h(t) dt = P[le
patient meure durant un bref intervalle de temps dt autour de l'instant t, sachant qu'il a survécu
jusqu'à cet instant t]. Un taux de hasard constant indique une survie exponentielle. Cela arrive
dans la désagrégation des particules et dans certaines maladies mortelles comme les cancers
métastatiques.
Le taux de hasard cumulé H(t) est défini comme l'intégrale of h(u) for 0 to t :
H(t) =
t
∫ h(u) du
0
Cette fonction en elle-même n'a pas une signification particulièrement intuitive, mais elle est
utile en analyse de survie car elle peut être facilement estimée à partir des données, même
quand celles-ci sont censurées. Rappelons qu'on a une censure droite en t si la personne a été
retirée vivante de l'étude à ce moment là.
Les durées de survie avec censure se présentent comme des couples (T i, δi), i=1,2,…,n, où δi
=1 s'il s'agit d'une mort et 0 autrement. Soit
t1* < t 2* < t 3* . . . < t m*
les temps de mort (failure times) distincts et soit Yi (t) les fonctions indicatrices des
personnes à risque à l'instant t : Yi (t) = 1 si une mort est observée et 0 sinon. Notons le
r( t ) =
n
∑ Y (t )
i=1
i
nombre des personnes à risque à l'instant t. Si d(t) compte le nombre des morts qui ont lieu en
t, alors l'estimateur le plus courant de la fonction de survie S(t), l'estimateur de Kaplan-Meier,
est donné par
^
d (t j )
S (t ) = ∏ (1 −
)
r (t j )
j :t j < t
C. Huber
Modèles pour des durées de survie.
86
où r(t) = Σ Yi (t), le nombre des personnes présentes, ou "à risque" à l'instant t.
L'estimateur de sa variance, appelé estimateur de Greenwood, est
^ 2
S (t ) ∑
j :t j < t
d (t j )
r (t j )(r (t j ) − d (t j ))
Les intervalles de confiance (à 95%), pour S(t) sont calculés comme S(t) +/- 1.96 se(S).
2
Estimation non paramétrique de la fonction de survie : Kaplan-Meier :
Exemple :
kidney.Kaplan.Meier<-survfit(Surv(infectime,cense)~
sex,data= kidney)
plot(kidney.Kaplan.Meier,lty=1:2,col=c(1,3),xlab ="durees de
survie",
ylab="Survie de Kaplan-Meier selon le sexe")
title(main="Comparaison des durees jusqu'à l'infection selon le
sexe",cex=0.8)
legend(400,0.8,c("Hommes","Femmes"),cex=1,lty=c(1,2), col=c(1,3))
0.8
0.2
0.4
0.6
Hommes
Femmes
0.0
Survie de Kaplan-Meier selon le sexe
1.0
Comparaison des durees jusqu'à l'infection selon le sexe
0
100
200
300
400
500
durees de survie
3 Le modèle à hasards proportionnels de Cox :
Le modèle de régression le plus utilisé pour les durées de survie est le modèle de Cox, ou
modèle à hasard proportionnel. Si Zi(t) est le vecteur des covariables du sujet i à l'instant t, le
modèle suppose que le taux de hasard pour cette personne est
C. Huber
Modèles pour des durées de survie.
87
λ(t | Z i(t)) = λ 0(t)r i(β,t)
où le score de risque r i(β,t) est donné par
r i(β,t) = exp(βΤ Zi(t)),
et λ0(t) est un taux de hasard de base, non spécifié, commun à tous les sujets de l'étude. Le
vecteur β
des coefficients de la régression n'inclut pas la constante, qui est incluse dans
le taux de hasard de base. Supposons qu'une mort ait lieu à l'instant t*. Conditionnellement à
cette mort, la probabilité que cela soit arrivé au sujet i qui est à risque à l'instant t* est égale à
Li (β ) =
λ 0 (t *)ri ( β , t *)
=
∑ Yj (t*) λ 0 (t *)rj ( β , t*)
j
ri ( β , t *)
∑ Yj (t *)rj ( β , t*)
j
Le produit de ces termes sur tous les temps de mort, au nombre de m, vaut donc
L( β ) = ∏ Li ( β )
i
C'est ce que l'on appelle la vraisemblance partielle de Cox. La maximisation du log de cette
vraisemblance partielle donne un estimateur pour β. Un estimateur de sa matrice de
variance-covariance est donné par l'inverse de la matrice des dérivées secondes du log
likelihood, qui est la matrice d'information.
L'estimateur du maximum de vraisemblance partielle des coefficients de régression st presque
aussi efficace que l'estimateur du maximum de vraisemblance quand un modèle paramétrique
est vrai et connu, comme par exemple le modèle de Weibull.
La méthode suppose que les durées de survie sont continues, ce qui exclut la possibilité d'exaequo. Cependant, ls données réelles sont souvent données en semaines, en mois ou en années
de telle sorte qu'il y a en fait des ex-aequo. Quelle devrait donc être la contribution à la
vraisemblance Lj(β) de tous les sujets j morts au même moment ? Il y a plusieurs solutions :
“exacte”, “d'Efron”, et "de Bleslau”; la solution exacte est celle qui prend le plus de temps, et
celle de Breslau celle qui en prend le moins, mais qui est aussi la moins bonne.
Stratification : quand les données sont stratifiées sur une variable catégorielle, une façon
d'en tenir compte consiste à autoriser un taux de hasard de base différent pour chaque strate.
En SPLUS on peut le faire en utilisant strata(var-name) dans la spécification du modèle.
En SPLUS le modèle à hasards proportionnels, avec les estimateurs par maximum de
vraisemblance parielle et leurs variances estimées sont donnés par la fonction coxph.
4 Examen des RESIDUS :
Comme dans la régression linéaire, les résidus offrent plusieurs possibilités pour tester
l'adéquation du modèle et l'influence des diverses observations sur les estimateurs. Pour des
covariables fixées dans le temps, les résidus de martingale sont
C. Huber
Modèles pour des durées de survie.
88
)
Yi (t j )ri ( β , t j )
resid i = δ i − ∑
)
j ≤ n ∑ Yl ( t j ) rl ( β , t j )
l ≤n
Ces résidus représentent essentiellement les indicateurs observés de survie moins l'espérance
des indicateurs de survie sous le modèle estimé.
Un autre type de résidus sont les résidus de la déviance :
deviance. resid i = sigm(resid i ) 2[ − resid i − δ i log(δ i − resid i )]
Les résidus de la déviance réduisent (?) l'asymétrie (?), et quand on en fait la somme des
carrés, on obtient la déviance. Mais, bien que les résidus de martingale puissent être très
asymétriques, ils sont préférables à ceux de la déviance pour identifier les sujets qui sont mal
prédits par le modèle.
La fonction qui donne les résidus en SPLUS est resid . On obtient les résidus de la déviance
en choisissant l'option type = ”deviance”. Par défaut, on obtient les résidus de martingale.
Pour identifier les points (les sujets) leviers, c'est à dire ceux dont la suppression aurait un fort
impact sur le changement du modèle estimé, on calcule les résidus dfbeta. Le changement de
chacune des composantes du vecteur beta des paramètres dû à la suppression de chacun des
sujets tour à tour est donné par Splus dans la fonction des résidus en demandant
type=”dfbeta”.
En SPLUS les résidus sont obtenus après avoir appliqué le modèle de Cox estimé par coxph,
en utilisant resid comme nous le faisons dans l'exemple ci-dessous.
TESTS pour les HASARDS PROPORTIONNELS :
Les tests d'adéquation sont fondés sur les résidus de Schoenfeld changés d'échelle à partir
d'un modèle ayant des betas dépendant du temps
λ(t;Z i(t)) = λ 0(t)r i(β(t),t).
c'est à dire un modèle de type Cox, avec toutes les covariables dans le modèle, mais où
chaque composante de beta peut être une fonction du temps. Si en ajustant ce modèle on
trouve graphiquement que les composantes de β(t) sont à peu près constantes dans le temps,
alors cela plaide en faveur du modèle à hasards proportionnels. Un test global de l'hypothèse
nulle H0 : β(t) = β est donné en SPLUS par la fonction cox.zph.
Cette fonction teste aussi individuellement chaque composante de beta.
5 EXEMPLE d'UTILISATION du MODELE de COX :
Pour faire l'analyse d'un modèle de Cox, faire :
options(contrasts='contr.treatment')
# pour s'assurer que les facteurs sont
# bien traités comme des facteurs.
kfit<-coxph(Surv(infectime,cense) ~ age + sex + X2,
C. Huber
Modèles pour des durées de survie.
kfit
89
data = kidney1)
# affiche le résultat de l'ajustement (fit.
Call:
coxph(formula = Surv(infectime, cense) ~ age + sex + X2, data = kidney1)
coef
exp(coef) se(coef) z
p
age 0.00318 1.003 0.0111 0.285 0.780000
# age may be removed
sex -1.48319 0.227 0.3582 -4.140 0.000035 # huge difference between the sexes
X21 0.26282 1.301 0.3779 0.695 0.490000
X22 -1.51907 0.219 0.5822 -2.609 0.009100 #only significant disease variable
X23 -0.08796 0.916 0.4064 -0.216 0.830000
Likelihood ratio test=17.6 on 5 df, p=0.00342 n= 76
Quand on utilise
options(contrasts='contr.treatment')
SPLUS crée trois variables :
X21 = dis1
X22 = dis2
X23 = dis3.
Les betas correspondants mettent en évidence les différences entre les divers niveaux et le
premier d'entre eux. Par exemple le taux d'incidence (taux de hasard) de la maladie 2 n'est que
.219 fois celui de la maladie 0 à tout instant. kfit suggère que l'âge n'a pas d'effet significatif.
Aussi essayons nous le modèle :
kfit1<-coxph(Surv(infectime,cense) ~ sex+X2 ,
data = kidney1)
kfit1
Call:
coxph(formula = Surv(infectime, cens) ~ sex + X2, data = kidney1)
coef exp(coef) se(coef)
z
p
sex -1.477 0.228 0.357 -4.140 0.000035
X21 0.274 1.315 0.375 0.730 0.470000
X22 -1.506 0.222 0.581 -2.591 0.009600
X23 -0.139 0.870 0.364 -0.383 0.700000
Likelihood ratio test=17.6 on 4 df, p=0.0015 n= 76
La différence considérable entre les hommes et les femmes (la valeur de Z est de – 4.14, c'est
à dire que les femmes ont un taux d'infection qui est seulement .228 fois celui des hommes
quel que soit le temps, puisque les femmes sont codées 2 et les hommes 1) permet de
soupçonner que le taux de base est peut être différent pour les deux sexes. Aussi allons nous
essayer d'autoriser un taux de base différent pour chacune des deux strates par sexe.
kfit2<-coxph(Surv(infectime,cense) ~
data = kidney1)
C. Huber
strata(sex) + X2,
Modèles pour des durées de survie.
90
kfit2
Call:
coxph(formula = Surv(infectime, cense) ~ strata(sex) + X2, data = kidney1)
coef exp(coef) se(coef) z
p
X21 0.270 1.309
0.376 0.718 0.47
X22 -0.676 0.509
0.558 -1.212 0.23
X23 -0.265 0.767
0.367 -0.723 0.47
Likelihood ratio test=4.27 on 3 df, p=0.234 n= 76
Pour choisir entre les deux modèles kfit1 and kfit2, on trace les graphes de leurs résidus de
déviance.
plot(resid(kfit1, type="deviance"))
plot(resid(kfit2, type="deviance"))
Un examen attentif des deux graphes ne permet pas de décider clairement entre les deux
modèles. On continue donc avec kfit1 car il permet de quantifier la différence entre les sexes.
Pour le modèle kfit1, regardons l'influence de chacune des observations sur les coefficients
beta. Aucune observation ne semble particulièrement influente.
Finalement, on peut tracer la fonction de survie pour la valeur moyenne des covariables
pour le modèle stratifié kfit2 :
par(mfrow=c(1,1))
plot(survfit(kfit2), lty=2:3)
# lty est le type de ligne (line type).
legend(500, .9, c("homme", "femme"), lty=2:3)
title("Courbes de survie pour les hommes et les femmes
\n dialysés ayant les covariables moyennes")
#
\n signifie :passage à la ligne
La différence des fonctions de survie pour les durées d'infection entre les hommes et les
femmes est évidente. Pour être complet, traçons aussi la courbe de survie de la durée
d'infection avec son intervalle de confiance à 95% pour le modèle kfit1.
plot(survfit(kfit1), lty=2:3)
title("Courbe de survie pour des patients sous dialyse
avec valeurs moyennes des covariables")
Résumé des ordres utilisés pour le modèle de Cox:
res<-coxph(Surv(duree,censure)~facteur1+facteur2,data=fichier)
res
donne les coefficients estimés de facteur1 et facteur2
summary(res)
donne les trois tests :
le test du rapport de vraisemblance: -2 [log(V(βο)−log(V(β_hat)]
le test de Wald ((β_hat - βο)/ se(β_hat)
le test du score : UtIU
summary(survfit(res))
donne la fonction de répartition, avec intervalle de confiance,
C. Huber
Modèles pour des durées de survie.
91
pour les facteurs 1 et 2 égaux à leurs valeurs moyennes.
plot(survfit(res)) donne un dessin de cette fonction de répartition.
Pour comparer deux modèles de Cox, notés res1 et res2:
a) Calculer :
L<-2*(res1$loglik[2]-res2$loglik[2])
car c'est la deuxième composante qui est la log vraisemblance du modèle, la première étant celle
du modèle nul, c'est à dire sans covariable.
b) Regarder quel est le nombre de degrés de liberté entre les deux modèles et calculer:
pchisq(L,df=difference)
Pour faire des strates:
res<-coxph(Surv(duree,censure)~facteur1+strata(facteur2), data=fichier)
C. Huber
92
Chapitre 7 .
Le BOOTSTRAP.
Pour comprendre le bootstrap, nous allons commencer avec un exemple simple. Supposons
que nous ayons un vecteur y de n = 15 observations :
y<-c(5,7,3,3,2,4,0,8,0,1,7,3,5,4,6)
y
median(y)
# [1] 4
Nous avons déjà calculé la médiane de y. Si maintenant nous voulons estimer la variance de
cet estimateur de la médiane, nous ne disposons pas comme pour la moyenne d'un estimateur
simple de cette varance. Nous allons donc construire un estimateur bootstrap de cette variance
comme suit :
1. Tirer un échantillon de taille n=15 de y avec remise.
2. Calculer la médiane de l'échantillon ainsi obtenu.
3. Stocker la médiane de l'échantillon Bootstrap dans un vecteur appelé boot.
4. Retourner en 1.
On répète ces étapes un grand nombre de fois, soit B = 1 000 fois par exemple. On obtient
ainsi un échantillon bootstrap de médianes de taille 1 000.
Ce que l'on réalise ainsi est essentiellement une reconstruction de la manière dont a été obtenu
l'échantillon et sa médiane en rééchantillonnant y à partir de lui-même.
POPULATION
→
ECHANTILLON
ECHANTILLON
→
ECHANTILLON BOOTSTRAP
La variabilité d'échantillonnage dans la population se reflète dans la variabilité
d'échantillonnage dans la sous-population y : mais, alors que nous n'avons pas accès à la
variabilité de median(y), dans la population totale, le vecteur boot illustre la variabilité de
median(y bootstrap) quand on échantillonne dans la sous-population y. Aussi utiliserons nous
la variance du vecteur boot pour estimer la variance de median(y).
Il est clair que ce que nous venons de faire pour la médiane, nous pouvons le répéter pour
estimer la variance d'autres statistiques. La suite des opérations est toujours la même :
1
2.
3.
4.
Faire un tirage au hasard avec remise de taille 15, dans l'échantillon y.
Calculer la statistique pour le nouvel échantillon bootstrap obtenu en 1.
Stocker la statistique ainsi calculée dans un vecteur nommé boot.
Retourner en 1.
Répéter ces étapes B=1000 fois.
C. Huber
93
Cependant, nous pouvons faire beaucoup d'autres choses grçace au bootstrap. Par exemple,
nous pouvons obtenir un intervalle de confiance pour le paramètre d'intérêt sans faire aucune
supposition sur la loi de son estimateur. Tout ce dont nous avons besoin, c'est d'ordonner le
vecteur boot par ordre croissant. Alors, un intervalle de confiance de coefficent de confiance
(1-a)*100% , pour le paramètre estmé par la statistique considérée, qui dans notre cas est la
médiane est donné par l'intervalle :
( boot [ B*(a/2)] , boot[B*(1-a/2)] )
Tout cela est obtenu par la fonction bootstrap en SPLUS.
Exemple 1 :
boot.obj1 <- bootstrap(y, statistic = median, B=1000,
seed = 0, trace = F)
summary(boot.obj1)
# Affichage des résultats :
Call:
bootstrap(data = y, statistic = median, B = 1000, seed = 0, trace = F)
Number of Replications: 1000
Summary Statistics:
median
Observed Bias
4
-0.133
Mean SE
3.867 0.9199
Empirical Percentiles:
2.5% 5% 95% 97.5%
median 2 3 5
6
BCa Percentiles:
2.5% 5% 95% 97.5%
median 2
2 5
5
On déduit de ce résumé que :
La médiane observée valait 4. La moyenne du vecteur boot, qui devrait être un bon estimateur
de la médiane de l'échantillon vaut 3.867. Donc, un estimateur du biais de la médiane de
l'échantillon comme estimateur de la médiane de la population est :
moyenne(médianes bootstrap) -médiane(y)
c'est à dire ici -.133. D'autre part, l'estimateur bootstrap de l'écart-type de la médiane vaut
.9199. On remarquera que ce n'est pas cet estimateur qui est utilisé pour obtenir l'intervalle de
confiance pour la médiane.
La première méthode, appelée celle des percentiles, et décrite ci-dessus, donne pour intervalle
de confiance bootstrap à 95% pour la médiane de la population [2 ; 6].
C. Huber
94
La deuxième méthode, appelée BCa (corrected percentile method, l'intervalle de confiance
bootstrap à 95% vaut [2 ; 5].
Le vecteur boot de médianes bootstrap est donné par SPLUS et peut être utilisé pour tracer un
histogramme bootstrap de la médiane pour la population :
Plot(boot.obj1)
Exemple 2 :
Cherchons maintenant un estimateur bootstrap de l'écart-type de l'aire sous la courbe ROC, et
un intervalle de confiance bootstrap à 95% pour cette aire.
Voici le programme Splus correspondant :
# rappelons que Fit<-fitted.values(coron.glm.AWT) (cf regression logistique)
mmin <- min(Fit)
mmax <- max(Fit)
delta <- .01
# nn = nombre des points en lesquels
# la sensibilité et la spécificité seront calculées.
nn <- floor((mmax - mmin) / delta)
boot.frame <- data.frame(coron, Fit)
attach(boot.frame)
ROCarea <- function(coron, Fit,nn)
{
rocc<-matrix(0,nn,2)
for(ii in (1:nn))
{
rocc[ii,1]<-sum(coron*
(Fit >ii*.02))/sum(coron)
# sensitivite
rocc[ii,2]<-1-sum
((1-coron)*(Fit<=ii*.02))/sum(1-coron)
#1-specificite
}
ord <- order(rocc[,2], rocc[,1])
rocc1 <- rocc[ord,]
# coron et Fit ordonnes
roca<-0
for(ii in 2:nn)
{
delta <- rocc1[ii,2] - rocc1[ii-1,2]
roca <- roca + delta * ((rocc1[ii,1]-rocc1[ii1])/2 + rocc1[ii-1,1])
}
c(roca)
# roca = area under the ROC curve is
# returned by the function ROCarea
}
# fin de la fonction ROCarea
boot.obj1 <- bootstrap(data = boot.frame, statistic =
C. Huber
95
ROCarea(coron,Fit,nn), B=1000, seed=0)
summary(boot.obj1)
# Affichage des résultats :
Call:
bootstrap(data = boot.frame, statistic =
ROCarea(coron, Fit, nn), B = 1000, seed = 0)
Number of Replications: 1000
Summary Statistics:
Observed Bias Mean
SE
Param 0.7391 -0.00477 0.7343 0.04811
Empirical Percentiles:
2.5% 5% 95% 97.5%
Param 0.634 0.6532 0.8101 0.826
BCa Percentiles:
2.5% 5% 95% 97.5%
Param 0.6364 0.6572 0.8134 0.8267
plot(boot.obj1)
A partir de l'histogramme de la distribution bootstrap de l'aire au-dessous de la courbe ROC,
on constate que cette distribution est remarquablement normale. Pour vérifier cette
impression, on peut faire :
qqnorm(boot.obj1)
qui donne un graphe des quantiles de la loi normale contre les quantiles des données (QQ
plot) pour l'échantillon bootstrapé des aires sous la courbe ROC. La linéarité de la courbe
obtenue suggère bien qu'en effet la loi de l'aire sous la courbe ROC est approximativement
normale.
L'intervalle de confiance à 95%, obtenu grâce aux percentiles corrigé, pour l'aire sous la
courbe ROC, vaut [.6364 ; .8267].
Par ailleurs, l'estimateur bootstrap de l'écart-type de l'estimateur de l'aire vaut .0481.
Remarquons que si on utilisait l'intervalle de confiance à 95% fondé sur l'approximation
normale, on aurait .7391 +/- 1.96*.0481 , soit [.645 ; .833]. Cet intervalle est très proche de
celui donné par les percentiles bootstrap, ce qui était prévisible après le tracé du QQ plot.
C. Huber

Documents pareils