Régressions linéaires avec R
Régressions simples, multiples et validation des modèles
L'essentiel de cette page !
Faire une régression linéaire entre x et y, une fonction affine, c'est facile ! Mais quand Y dépend de plusieurs variables X1, X2, X3... Cela demande un peu plus de méthodologie. Voici une démarche à appliquer point à point.
L'objectif est toujours d'établir la relation qu'il existe entre Y et les autres variables.
Mais attention, n'oublions pas qu'il est impossible de connaître la vérité ou d'être certain de la reconnaître.
Si la régression linéaire trouve ses limites, on peut passer aux modèles généralisés avec glm().
Étape 0 - Partons d'un exemple ! Imaginons un échantillon d'une population, dont on a la taille, le poids et l'IMC (Indice de Masse Corporelle) !
Essayons de voir si on peut retrouver la relation entre taille, poids et IMC sachant qu'on connait pour une fois la réponse IMC = poids/(taille/100)²
Simulons ce jeu de données :
nb = 180 ; poids <- round(rnorm(nb,65,11),0) ; imc <- rnorm(nb,22,5) ; # IMC
taille <- round(sqrt(poids/imc)*100,0) ; taille_bis <- round(rnorm(nb,170,11),0)
taille_biss <- round(rnorm(nb,170,11),0) ; taille <- round((taille+taille_bis+taille_biss)/3,0)
imc <- round(poids/(taille/100)^2,1)
compil <- data.frame(poids,taille,imc) # Compilation des valeurs
Etape 1 - Décrire le jeu de données afin de comprendre les relations entre Y (l'IMC) et les variables X1 et X2 (poids et taille)
plot(compil) # Permet d'établir directement une relation entre toutes les variables d'un data.frame.
On voit ainsi que poids et IMC sont bien liés et que l'IMC a tendance à baisser si la taille augmente.
1.1- Établir un lissage de la relation poids // IMC.
Un lissage permet de se donner une idée précise de la relation existante entre deux variables et d'estimer la linéarité de cette relation !
On peut utiliser la commande par défaut scatterplot() de la librairie car :
library(car)
scatterplot(imc~poids, data=compil, regLine=list(method=lm, lty=1, lwd=2, col="red"))
On y voit ainsi figurer la courbe de lissage moyenne, les courbes de lissage des intervalles de confiance et une régression obtenue par les moindres carrés (en rouge ici)
Le lissage peut aussi se faire manuellement avec la fonction lowess() :
plot(imc~poids,pch=16,data=compil) # équivalent de plot(compil$poids, compil$imc, pch=16)
# Je réalise un lissage de mes valeurs
P <- lowess(data$poids, data$imc,f=0.33,iter=1) # Lissage local sur 33% des valeurs
P2 <- lowess(data$poids, data$imc,f=0.66,iter=1) # Lissage plus global sur 66% des valeurs
Attention, superposer un polygone translucide qui figure un lissage des intervalles de confiance n'est pas une moindre affaire ! On ne le représentera pas ici car il faut créer sa fonction : fonction qui va faire glisser une fenêtre et calculer à chaque fois un intervalle de confiance. L'ensemble sera affiché avec la fonction polygone().
Dans tous les cas : scatterplot() ou plot() + lowess() permettent de voir qu'il existe une relation entre poids et IMC qui pourrait être étudiée !
1.2 - Décrire les relations statistiques entre les variables X1, X2... et Y avec les fonctions cor(), cor.test() et le R²
Afficher un tableau de variance, covariance avec cor()
round(cor(compil),2)
Le tableau renvoyé permet d'un seul coup d'œil de voir le niveau de corrélation entre les variables.
Mais cela ne suffit pas, une faible corrélation peut être très significative. Un paramètre peut jouer un peu mais de façon certaine.
poids taille imc
poids 1.00 0.48 0.78
taille 0.48 1.00 -0.17
imc 0.78 -0.17 1.00
On peut aussi afficher un diagramme de corrélation avec la librairie corrplot
install.packages(corrplot)
library(corrplot)
corrplot(cor(compil))
En savoir plus sur les corrélogramme en tenant compte aussi de la significativité des corrélation.
Établir la significativité de l'influence de chaque paramètre avec cor.test()
# Je teste une relation linéaire entre poids et imc
cor.test(compil$imc,compil$poids)
cor.test(compil$imc,compil$taille)
La p-value renvoyé à chaque fois montre ici un lien significatif entre poids et imc et légèrement significatif entre taille et IMC.
Remarque : la méthode par défaut ici est la méthode de Pearson. Il en existe d'autres.
Analyser la forme de la relation avec le R² (coefficient de détermination)
Si R² = 1, le nuage est très allongé, X et Y tracent une droite.
Si R² = 0, le nuage se disperse dans tous les sens et une régression linéaire ne permettra pas de faire des prédictions.
# Je calcule mon R²
cor(compil$imc,compil$poids)^2
cor(compil$imc,compil$taille)^2
A ce stade, la relation poids - IMC est bien établie. On voit en revanche que la relation taille - IMC a un R² proche de 0.
Faut-il renoncer à la taille dans notre régression ? On sait bien que non ! Cela nous invite à rester méfiant et à ne pas exclure une variable si le R² seul ou la p-value nous pousse à renoncer.
Étape 2 - Réaliser le modèle de régression linéaire
Réaliser la régression linéaire.
Je tente un premier modèle de régression :
reg1 <- lm(imc~poids, data=compil)
J'affiche le résultat :
plot(imc~poids,pch=16,data=compil)
abline(reg1,col="red",lwd=2)
Je décris ma régression avec les commandes :
summary(reg1) # décris la régression
coef(reg1) # affiche les coefficients de la droite
residuals(reg1) # donne les résidus
Extraire les p-values de chaque coefficient :
summary(reg1)[[4]][,4]
Extraire la p-value finale de la régression
str(summary(reg1))
# Extraction de la p-value
pf(summary(reg1)$fstatistic[1],summary(reg1)$fstatistic[2],summary(reg1)$fstatistic[3],lower.tail=FALSE)
Étape 3 - Contrôler le modèle de régression linéaire
En paraphrasant statistique-et-logiciel-r.com, on sait qu'on peut valider une relation linéaire entre X et Y si les résidus :
Sont indépendants
Sont distribués selon une loi Normale de moyenne 0
Sont distribués de façon homogènes, c’est à dire, avec une variance constante.
C'est quoi un résidu ? C'est la différence entre valeur réelle et sa projection sur la droite de régression : entre pratique et théorie.
Tous les graphiques nécessaires à ce qui va suivre s'affichent en une seule fois avec la commande plot(reg1).
La fonction valreg() pour valider un modèle de régression
On peut s'éviter toutes les étapes qui suivent en utilisant la fonction valreg() qui compile cet ensemble :
Installer valreg() (package {KefiR}
install.packages("devtools") ; require(devtools) # Risque d'erreur si RTools non installé.
devtools::install_github("Antoine-Masse/KefiR")
Exécuter le package {KefiR}
library("KefiR")
Exemple d'utilsation de valreg()
valreg(reg, plot=T, analyse=T)
plot : permet d'activer le paramètre boot (boostrap) de validation du modèle de régression avec affichage d'un bilan graphique.
analyse : permet d'afficher les commentaires et les raisons de l'invalidation du modèle de régression.
3.0- On va vérifier d'abord que le modèle de régression semble adapté.
Le test dit de "validité de spécification" permet de vérifier que la moyenne des résidus est de 0. On vérifie donc la linéarité du modèle qui peut être mise à mal (courbure ?) si on a oublié une variable explicative importante [Desgraupes].
Dans ce but, le test rainbow compare le modèle initial à un modèle construit sur un sous-échantillon constitué des valeurs centrales de l’échantillon. S’il n’y a pas adéquation des deux modèles, le test conclut à une mauvaise spécification.
On peut d'abord commencer par un contrôle visuel :
plot(reg1, which = 1)
Sur ce graphique, la ligne rouge doit être approximativement horizontale s'il y a bien une relation linéaire en X et Y.
Ici, cette relation se vérifie mais on voit qu'elle n'est pas parfaite !
Vérification l'adéquation par le test de Rainbow (adéquation : p-value > 0,05)
install.packages("lmtest") ; library("lmtest") ;
raintest(reg1) # test de rainbow
Il y a adéquation du modèle de régression si la p-value est supérieur à 0,05.
3.1- Indépendance des résidus
Les résidus ne doivent pas être liés les uns aux autres. En gros : on ne doit pas pouvoir anticiper en résidu par la connaissance d'un autre résidu.
On veut éviter les phénomènes d'auto-corrélation : les résidus augmenteraient ensemble dans une zone donnée et seraient liés les uns aux résidus précédents ou suivants (cas typiques lorsque X est un enregistrement du temps).
Vérification par le test de Durbin-Watson (indépendance : p-value > 0,05)
Cela se vérifie avec le test de Durbin-Watson : il faut une p-value supérieure à 0,05 pour avoir indépendance.
durbinWatsonTest(reg1) # nécessite la librairie car
dwtest(reg1) # nécessite la librairie lmtest
Attention : 0,05 n'est qu'une limite, il ne faut pas renoncer si on a 0,03 !
Autre remarque : ce test n'évalue que l'autocorrélation entre résidus et le résidus qui suit directement (n+1) et non ceux séparés d'un intervalle (résidus+2) qui pourraient être autocorrélés dans un phénomène cyclique.
On peut aussi visualiser l'indépendance avec ce graphique suivant :
library(stats)
acf(residuals(reg1), main="Regression IMC = f(poids)")
L'interprétation de ce graphique se fait de la manière suivante :
Le premier bâtonnet est très élevé, c'est l'auto-corrélation des résidus avec eux-même !
Le deuxième bâtonnet indique l'auto-corrélation entre les résidus et les résidus n+1 : il y a auto-corrélation dès que le bâtonnet (lag) dépasse les pointillés.
Le troisième bâtonnet entre les résidus n et les résidus n+2... etc.
3.2- Distribution normale des résidus
On peut vérifier que les résidus suivent la loi normal avec un histogramme classique (cela devrait dessiner une courbe de gauss approximativement).
hist(residuals(reg1),col="yellow",freq=F)
densite <- density(residuals(reg1)) # estimer la densité que représente ces différentes valeurs
lines(densite, col = "red",lwd=3) # Superposer une ligne de densité à l'histogramme
Plus pertinent encore est de faire appel au diagramme Quantile-Quantile ou QQ plot.
plot(reg1, which = 2) # plot(reg1, 2)
Le principe de ce diagramme est de découper le jeu de données en boîte contenant le même nombre de données (ex : les quartiles sont 3 quantiles qui permettent de découper un jeu de données en 4 boîtes qui contiennent chacune 25% des données).
Si la distribution suit une loi normale, les quantiles de résidus devraient s'organiser de la même façon que les quantiles théoriques de la fonction de régression linéaire. On devrait donc avoir un alignement entre quantiles des résidus et quantiles théoriques.
Attention : Si on n'observe pas d'alignement sur le QQ-plot, la régression risque de ne pas être pertinente.
Vérification par le test de Shapiro-Wilk (normalité : p-value > 0,05)
Un test permet de trancher sur la normalité de la distribution des résidus : c'est le test de normalité de Shapiro-Wilk
shapiro.test(residuals(reg1))
On peut considérer qu'il y a normalité tant que la p-value reste supérieure à 0,05 (mais on le rappelle que c'est une frontière floue : on peut tolérer 0,03).
3.3- Vérifier que la variance des résidus est constante (distribution homogène)
Pour cela on trace le graphique suivant qui met en relation les racines carrées des résidus (résidus standardisés) en fonction des valeurs théoriques (fitted-values) de Y prédites par l'équation de la régression.
plot(reg1, which = 3)
On cherche ici une courbe rouge plane. L'homogénéité est à rejeter si celle-ci n'est pas horizontale.
De plus, ce graphique est l'occasion de vérifier si certains points se regroupent, ce qui indiquerait un défaut d'indépendance.
Vérification par le test de Breush-Pagan (homogénéité : p-value > 0,05)
Le test de Breush-Pagan permet de vérifier objectivement l'homogénéité des résidus (et donc l'hétéroscédasticité vs homoscédasticité).
install.packages("lmtest") ; library("lmtest") ;
bptest(reg1) # librairie lmtest
ncvTest(reg1) # librairie car
L'homogénéité est rejeté si la p-value est inférieure à 0,05. Dans cet exemple, la relation poids/imc serait acceptée.
Vérification par le test de Goldfeld-Quandt (homogénéité : p-value > 0,05)
D'autres sources suggèrent de faire le test de Goldfeld-Quandt.
library("lmtest")
gqtest(reg1)
D'autres proposent un 3ème test, celui de White supposé plus capable sur les modèles complexes à détecter l'hétéroscédasticité mais moins puissant que Breusch-Pagan sur les modèles simples.
3.4- Évaluer les points qui ont une grande influence sur la régression afin de les écarter s'il s'agit de points potentiellement aberrants.
La distance de Cook permet d'évaluer les points qui auront une (trop) grande influence sur le modèle de régression.
Le graphique suivant permet de voir les points normaux et trop influents.
plot(reg1, which = 5)
Les point trop influents sortent de la ligne pointillé rouge (distance de Cook).
Ici, les points sont tellement normaux qu'on n'aperçoit même pas cette ligne !!!
Aucun problème donc. Pour voir des exemple où la distance de Cook est traversée : voir le lien suivant.
Étape 4 - Estimer la pertinence du modèle
Voici quelques indices qui permettent de prouver qu'un modèle de régression est bon.
Certains sont pertinents pour les régressions multiples et comparer plusieurs modèles. C'est le cas du RSS, de l'AIC, du BIC, du MSE.
En revanche, biais, variance et R² ajusté peuvent déjà s'utiliser sur une simple régression à une seule variable.
Le RSS (Residual Sum of Squares), la somme des résidus au carré, illustre la pertinence d'un modèle de régression.
Plus le RSS est petit, mieux c'est !
Le risque du RSS est qu'il peut être bon si notre régression est absurde est modélisant le bruit. Autre inconvénient, le RSS varie avec le nombre d'individus.
Le R² ajusté, R squared adjusted. Ce R² doit tendre vers 1. Il se calcule à partir du RSS. Mais attention ! On peut avoir un R² proche de 1 si on a compilé un grand nombre de variable et fait le modèle à partir d'un seul échantillon sans jamais le tester.
Plus le R² ajusté est proche de 1, mieux c'est !
L'AIC (Critère d'Information d'Akaike) est plus pertinent que le RSS car elle va simuler un RSS si on avait eu des données inconnues. Elle va aussi pénaliser l'augmentation du nombre de variables.
Plus l'AIC est petit, mieux c'est !
Le BIC (Critère d'Information Bayésien) pénalise encore plus que l'AIC le nombre de variables. On doit donc trouver un compromis entre le nombre de variables et le BIC le plus bas.
Plus le BIC est petit, mieux c'est !
Le MSE (Mean Sqare Error). Équivalent au RSS, le MSE est calculé sur un échantillon test que l'on va appliquer au modèle établi à partir d'un échantillon d'apprentissage. La MSE tient compte du nombre d'individus. Critère pertinent !
Plus le MSE est petit, mieux c'est !
Le biais, distance entre le modèle et la réalité. C'est plus souvent d'ailleurs la différence entre le modèle et l'échantillon.
Plus le biais est petit, mieux c'est !
La variance, potentialité des coefficients du modèle à bouger d'un échantillon à l'autre.
Moins ils bougent, mieux c'est !
Nous reparlerons plus tard du RSS, de l'AIC, du BIC et du MSE...
On ne parlera dans l'immédiat que du R² ajusté et de la variance.
4.1- Valider la pertinence des coefficients par les p-values et R² ajusté
On peut afficher de nombreux éléments via la fonction summary() - résultats ci-contre.
summary(reg1) # A regarder : Adjusted R-squared:
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 7.34412 0.93488 7.856 3.62e-13 ***
poids 0.22290 0.01395 15.979 < 2e-16 ***
Residual standard error: 2.264 on 178 degrees of freedom
Multiple R-squared: 0.5892, Adjusted R-squared: 0.5869
F-statistic: 255.3 on 1 and 178 DF, p-value: < 2.2e-16
Si le coefficient intercept (la valeur b dans une équation y = ax + b), et les autres coefficients directeurs sont associés à des valeurs Pr petites (et plusieurs étoiles), alors ils ont un impact significatif et sont pertinents.
Le R² ajusté vaut ici de 0,5869 ce qui n'est pas énorme. On pourrait améliorer le modèle de régression pour approcher 1.
La p-value très basse (2.2e-16) montre aussi que le modèle établit une relation pertinente entre poids et IMC. Ce lien ne peut être nié mais il peut probablement être ajusté.
4.2- La variance du modèle de régression
La fonction confint() permet d'accéder facilement aux intervalles de confiance sur les coefficients.
coef(reg1) # affiche les coefficients du modèle
confint(reg1, level = 0.95) # affiche les coefficients min et max correspondant aux intervalles de confiance à 95%
2.5 % 97.5 %
(Intercept) 5.4992456 9.1889919
poids 0.1953758 0.2504326
Il est possible de visualiser la variance graphiquement.
La fonction predict() en paramètrage "prediction" va renvoyer le domaine où se situeraient toutes les droites possibles. matplot() pourra afficher le tout.
# Où se trouvent les droites possibles du fait de la fluctuation des coefficients ?
new <- data.frame(poids = seq(30, 50, 0.5),taille<-rep(mean(taille),41))
yy <- predict(reg2, new, interval="prediction")
matplot(new$poids,yy,lty=c(1,3,3), type="l",ylab="predicted y" ,col=c('black','green','green'))
Étape 5 - Régression multiple
Lorsque l'on a plusieurs variables, la fonction lm() reste pertinente. Il faut juste en maîtriser la syntaxe.
lm(y~x) : régression classique y = f(x) - affine - y = ax+b
lm(y~x+0) : régression linéaire y = f(x) sans intersection - y = ax
lm(y~x1+x2) : régression telle que y = B + A1*x1 + A2*x2
lm(y~x1:x2) : régression telle que y = B + A1*x1*x2
lm(y~x1*x2) : régression telle que y = B + A1*x1 + A2*x2 + A3*x1*x2
A cette syntaxe, on peut ajouter d'autres fonctions pour permettre des régression polynomiales avec poly() ou d'autres transformations avec I().
Exemples :
lm(y~poly(x,3)) : régression polynomiale telle que y = B + A1*x + A2*x² + A3*x^3
lm(y~I(1/x) : permet d'imposer des transformations à x comme ici y = B + A1*1/x
Créons donc des modèles de régressions pour essayer de retrouver la relation entre IMC et poids / taille
reg2 <- lm(imc~poids+taille,data=compil)
reg3 <- lm(imc~poids*taille,data=compil)
reg4 <- lm(imc~poids*poly(taille,2),data=compil)
reg5 <- lm(imc~poids:taille,data=compil)
reg6 <- lm(imc~poids*I(taille^2),data=compil)
reg7 <- lm(imc~poids*I(1/(taille/100)^2)+0,data=compil)
reg8 <- lm(imc~poids:I(1/(taille/100)^2)+0,data=compil)# C'est le vrai modèle où IMC = poids/taille_en_cm² - pas de valeur B
La fonction coef() permet d'afficher l'ensemble des coefficients de la régression.
coef(reg4)
La librairie Zelig vaut la peine de se détourner : elle permet de faire des régression multiples et d'estimer la répartition probable de mes valeurs en tenant compte des autres variables (ce qui est très difficile lorsqu'on a plusieurs variables).
library("Zelig")
# Régression simple IMC en fonction du poids
out <- zelig(imc~poids, data=compil, model='ls')
summary(out)
setx(out,poids=c(50:80))->out
sim(out) -> out
plot(out)
summary(out) # description de chaque point
# Régression multiple IMC en fonction du poids et de la taille
out2 <- zelig(imc~poids*taille, data=compil, model='ls')
summary(out2)
setx(out2,poids=c(50:80))->out2 # simulation pour des poids de 50 à 80 kg
sim(out2) -> out2 # simulation des poids
plot(out2) # affichage
summary(out)
out2$coefficient
On voit ici que le domaine de prévision est beaucoup plus fiable si on fait une régression multiple qui intègre la taille.
Étape 6 - Sélectionner le meilleur modèle
6.1- Comparer les R² ajusté.
On y accède avec summary() sinon on peut regarder les R² non ajustés (très proches, moins fiables).
summary(reg1) # 0.5892249
summary(reg2) # 0.9868673
summary(reg3) # 0.9958284
summary(reg4) # 0.9998804
summary(reg5) # 0.354403
summary(reg6) # 0.992617
summary(reg7) # 1
summary(reg8) # 1
On voit ici que le modèle reg1 qui ne dépend que du poids n'est pas pertinent à côté des autres, tout comme reg5.
Il faudrait maintenant comparer les modèles reg2, reg3, reg4, reg6, reg7 et reg8.
Il faut surtout ne pas prendre celui qui a le meilleur R² ajusté ! Car on pourrait avoir un modèle trop complexe derrière. Mieux vaut un modèle un peu moins bon mais simple !
6.2- Comparer les modèles de régression avec la fonction anova().
La fonction anova() permet de faire des comparaisons entre modèles.
anova(reg2,reg3,reg4,reg6,reg7)
Il suffit de regarder le RSS (en gras) pour savoir quelle est le meilleur modèle. Le 3ème (reg4) et le cinquième (reg7).
On peut comparer ces deux modèles reg4 et reg7.
anova(reg4,reg7) # L'absence d'étoiles indique bien que ces modèles peuvent être considérés comme équivalent en termes de relations linéaires.
Attention : cette fonction d'un point de vue mathématique semble adaptée uniquement pour des modèles imbriqués.
Si on remonte à l'étape 5, on voit bien que reg2 est imbriqué dans reg3. C'est donc possible de les comparer. En revanche, on ne peut comparer reg2 à reg5 (aucun ne contient l'autre modèle). Ajoutons que reg2 et reg5 peuvent toutefois être comparés à reg4 qui contient ces deux régressions.
Enfin, on voit qu'il n'est pas particulièrement pertinent de comparer reg4 et reg7 par anova !
Model 1: imc ~ poids + taille
Model 2: imc ~ poids * taille
Model 3: imc ~ poids * poly(taille, 2)
Model 4: imc ~ poids * I(taille^2)
Model 5: imc ~ poids * I(1/(taille/100)^2)
Res.Df RSS Df Sum of Sq F Pr(>F)
1 177 29.1601
2 176 9.2627 1 19.8973 13036.4 < 2.2e-16 ***
3 174 0.2656 2 8.9972 2947.4 < 2.2e-16 ***
4 176 16.3934 -2 -16.1278 5283.3 < 2.2e-16 ***
5 176 0.1515 0 16.2419
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
6.3- Comparer les modèles de régression par l'intermédiaire des RSS.
Calculer les RSS des modèles : ce calcul peut se faire de la façon suivante - sans passer par la fonction anova().
sum(reg7$residuals^2) # sommes des résidus mis au carré pour le modèle reg7
6.4- Comparer les modèles de régression par l'intermédiaire des AIC.
Pour comparer l'AIC, on peut utiliser les fonctions extractAIC() et AIC(). Bien que ces deux fonctions ne renvoient pas la même valeurs d'AIC. Pas d'inquiétude, c'est juste qu'elle n'additionnent pas à la base de leurs calculs la même constante.
L'essentiel est de toujours utiliser la même fonction.
extractAIC() renvoie deux valeurs : la première, le nombre de variables et l'AIC qui, rappelons-le, doit être aussi petit que possible.
extractAIC(reg1) # 2.0000 296.1014
extractAIC(reg2) # 3.0000 -321.6283
extractAIC(reg3) # 4.0000 -526.0523
extractAIC(reg4) # 6.000 -1161.387
extractAIC(reg5) # 2.0000 377.4847
extractAIC(reg6) # 4.0000 -423.2939
extractAIC(reg7) # 3.000 -1266.462
extractAIC(reg8) # 1.000 -1269.127
On voit que le modèle reg4 et reg7 jugés aussi bon que la réalité (reg8) ont pourtant 4 à 6 variables contre une seule ! (en réalité 2 car la formule poids/taille^2 est interprétée comme une seule variable dans reg8)
6.5- Comparer les modèles de régression par l'intermédiaire des BIC.
On peut comparer les BIC de chaque modèle avec la fonction BIC.
On rappelle que le BIC pénalise encore plus le nombre de variables que l'AIC.
BIC(reg1) # 878.7482
BIC(reg2) # 186.8721
BIC(reg3) # 128.4066
BIC(reg4) # -516.1518
BIC(reg5) # 936.2825
BIC(reg6) # 231.072
BIC(reg7) # -742.7517
BIC(reg8) # -753.0865
Il apparait très clairement que les seuls modèles à même de rivaliser avec la vérité (reg8) sont reg4 et reg7 avec une nette préférence pour reg7 qui est un modèle plus simple que reg4.
6.6- Comparer la colinéarité des variables
La fonction vif() de la librairie car permet de comparer la colinéarité des variables. Les valeurs renvoyées par vif doivent être basses sinon les variables ont tendance à évoluer dans la même direction.
library(car)
vig(reg2) # pas de colinéarité
vif(reg4) # colinéarité
vif(reg7) # colinéarité
Le mieux est d'en calculer directement la racine carré avec sqrt() : cela permet de savoir dans quelle mesure l'écart-type d'une variable est influencé par sa colinéarité avec une autre. On peut, par exemple, se fixer un seuil de 10 et éliminer les variables qui dépassent ce seuil.
Ainsi : Mes analyses précédentes (RSS, AIC, BIC) montrent que reg7 serait un bon modèle.
Les valeurs renvoyées par vif() suggèrent de se débarrasser de "poids:I(1/(taille/100)^2)" et on retrouverait ainsi la vérité, reg8 !
6.7- Ajouter ou supprimer des variables avec les fonctions add1(), drop1() et update()
La fonction update permet de modifier un modèle existant en y ajoutant ou en y supprimant des variables.
Un exemple ou je vais supprimer la variable espèce d'une régression :
data(iris)
lm(Sepal.Length~.,data=iris) -> reg
regressor<-update(reg,~.-Species)
summary(regressor)
La fonction drop1() ou son pendant (add1()) permettent de voir ce qui se passe si on ajoute ou supprime une variable. Il ne change pas réellement le modèle mais nous donnes des infos (AIC, BIC, p-values...).
Son grand avantage est lorsqu'on a fait une régression en fonction d'une variable catégoriel, lm() nous fera une régression en fonction de chaque catégorie : drop1() va regrouper tout cela pour clarifier le problème...
Exemple : j'ai fait une variable sur le jeu de données iris en fonction notamment de la variable Species. Il y a 3 espèces (versicolor, virginica et setosa) : lm()) va faire une régression pour chaque espèce selon la logique "pas versicolor" = 0 et "versicolor" = 1.
De ce fait, Species apparait plusieurs fois dans le modèle. drop1() évite ce problème.
data(iris)
lm(Sepal.Length~.,data=iris) -> reg
summary(reg)
drop1(reg)
Étape 7 - Optimiser son modèle de régression
Partons volontairement d'une régression sale ! J'ai fait une régression croisée polynomiale où Y dépend de X1, X1^2, X2, X2^2 et des produits des ces éléments mais aussi des inverses...
reg9 <- lm(imc~poly(poids,2)*poly(taille,2)*poly(I(1/taille),2)*poly(I(1/poids),2))
La fonction step() est capable de nettoyer ces modèles. step() va avancer pas à pas pour supprimer des coefficients et réoptimiser les autres sans que la régression ne perte de sa qualité.
On peut paramétrer strep() de 3 façons :
forward : on part d'un premier coefficient et on en ajoute peu en voyant jusqu'où s'arrêter
backward : on part de l'ensemble des coefficients et on en retire peu à peu
both : un mixe entre forward et backward
summary(reg9)
reg9_prim <- step(reg9,direction="both")
summary(reg9_prim)
anova(reg9,reg9_prim) # l'anova ne montre pas d'amélioration de reg9_prim par rapport à reg9
extractAIC(reg9)
extractAIC(reg9_prim)
On voit ici que l'AIC a baissé un peu et que j'ai réussi de passer de 25 variables à 9 variables ! J'en ai éliminé 16 !
La fonction update() permet d'ajouter ou de retirer manuellement des variables.
summary(reg9_prim)
reg9_update <- update(reg9_prim,.~.-1) # je retire l'intercept
summary(reg9_update)
reg9_update <- update(reg9_update,.~.-poly(poids, 2)) # je retire les polynomes du poids
reg9_update <- update(reg9_update,.~.-poly(I(1/taille), 2)) # je retire les polynomes de l'inverse de la taille
reg9_update <- update(reg9_update,.~.+I(poids/(taille/100)^2)) # j'introduis la formule réelle du modèle
extractAIC(reg9_prim) ; extractAIC(reg9_update) # j'ai bien baissé mon AIC
reg9_update <- step(reg9_update) # je peux encore utiliser step pour éliminer ce qui peut l'être
summary(reg9_update) # Je suis ainsi arrivé à retrouver mon modèle
Étape 8 - Validation du modèle
Pour valider un modèle, il existe plusieurs approches :
Validation avec échantillon d'apprentissage et échantillon test - on s'assure alors que le MSE soit bas. Et que le modèle marche pour l'échantillon test.
Validation croisée : un bootstrap permet de refaire plusieurs fois la méthode ci-dessus.
Une fois le modèle validé :
Un bootstrap va permettre de remixer l'échantillon qui a servi pour l'obtention du modèle (en retirant certains points et en dupliquant d'autres).
On va ainsi se faire une idée précise de la variance et du biais.
Comment faire : il suffit de demander à être formé
Voici aussi une petite illustration du potentielle du machine learning : le bootstrap
Étape 9 - Faire des prédictions
La commande predict est excellente pour pouvoir prédire les valeurs possibles à partir d'un modèle de régression.
Elle permet aussi d'anticiper la fiabilité de nos valeurs.
# Pour que predict() fonctionne, les données de prédictions doivent être sous forme de data.frame.
new <- data.frame(poids = seq(30, 50, 0.5),taille<-rep(mean(taille),41))
pred_droites <- predict(reg2, new, interval="confidence",level=0.95)
En paramétrage confidence (+ level=0.95), predict() prédit des valeurs à partir d'un modèle de régression et donne les intervalles de confiance des estimations.
# Trace les trois courbes
matplot(new$poids,pred_droites,lty=c(1,2,2), type="l",xlab ='x',ylab="predicted y",col=c('black','blue','blue'))
Étape 10 - Afficher des régressions multiples en modélisant des nappes
10.1- Régression en 2D
On peut très facilement afficher une régression linéaire si on a (sans compter l'intercept) 1 seul ou 2 coefficients.
Si j'ai une seule variable et une relation du type y = ax + b, je peux utiliser la fonction abline().
D'autres fonctions ci-dessus sont très utiles telles matplot() ou encore les affichages de régressions obtenues avec Zelig (cf. ci-dessus).
10.2- Régression simple en 3D : 2 coefficients = 1 plan.
Si mon modèle de régression implique deux coefficients directeurs appliqués à deux variables, la régression donnera un plan dans un graphique 3d.
C'est le cas du modèle de régression 2 illustré ci-dessus que l'on peut comparer à reg1 qui ne faisait varier l'IMC qu'en fonction du poids.
reg1 <- lm(imc~poids,data=compil)
reg2 <- lm(imc~poids+taille,data=compil)
On peut maintenant afficher le graphique avec en rouge (reg1) et en bleu (reg2)
library("rgl")
plot3d(imc~poids+taille,radius=2,expand=1.5,box=F,size=4)
# reg1
planes3d(a=reg1$coefficients[2], b=0, c=-1, d=reg1$coefficients[1], col='red',alpha=0.5)
# reg2
planes3d(a=reg2$coefficients[2], b=reg2$coefficients[3], c=-1, d=reg2$coefficients[1], col='blue',alpha=0.5)
10.3- Régression simple en 3D : n coefficients dérivés de 2 variables.
Imaginons que je veux illustrer la relation entre Y et de nombreuses transformations de deux variables, je peux alors faire une transformation.
Je définis l'étendue des valeurs de X1 et de X2 que je compte tester.
my_seq_poids <- seq(min(poids), max(poids), length = 40)
my_seq_taille <- seq(min(taille), max(taille), length = 40)
Je combine les deux étendues de X1 et X2 pour créer une Grille de valeurs possibles
Grille<-expand.grid(poids=my_seq_poids,taille=my_seq_taille)
J'utilise la comme predict() pour simuler toutes les valeurs d'IMC possible pour les combinaisons de X1 et X2
Nappe<- predict(reg2,Grille)
Nappe<- matrix(Nappe,nrow=40, ncol=40,byrow=F)
Je trace le graphique 3D et j'ajoute la nappe
library("plot3Drgl")
scatter3Drgl(poids, taille, imc, pch=16 ,cex=1,col="black")
colfunc<-colorRampPalette(c("black","blue")) # couleurs utilisées
colors <- (colfunc(40))
# Nappe de la régression 2 en noir & bleu
persp3Drgl(x=my_seq_poids,y=my_seq_taille,z=Nappe,col=colors,alpha=0.5 ,add=T)
Je peux ajouter d'autres régressions
Nappe<- predict(reg8,Grille)
Nappe<- matrix(Nappe,nrow=40, ncol=40,byrow=F)
colfunc<-colorRampPalette(c("blue","cyan","yellow","red","#990000"))
colors <- (colfunc(40))
# Nappe de la régression 8 en arc en ciel, le vrai modèle
persp3Drgl(x=my_seq_poids,y=my_seq_taille,z=Nappe,col=colors,alpha=0.5 ,add=T)
10.3- Et si j'ai plus de 2 variables ?
Impossible de les voir !!! Mais ce problème reste le même sans régression. Il faudra alors penser à l'ACP ou sinon, modéliser pour deux variables et fixer les valeurs des autres variables.
Exemple : si j'avais une variable supplémentaire X3
Grille<-expand.grid(poids=my_seq_poids,taille=my_seq_taille)
Grille <- data.frame(Grille,X3=rep(12,40^2)) # Permet de fixer la variable X3 à 12.
Il ne reste qu'à suivre le code ci-dessus avec la fonction predict()...
Mais attention : si les autres variables ont un rôle à jouer dans les prédictions : cela ne donnera rien.
Étape 11 - Gérer les variables colinéaires
Certains variables sont colinéaires. Parfois aussi, on a plusieurs Y (Y1, Y2, Y3) à mettre en relation avec plusieurs X (X1, X2, X3)...