Vous êtes sur la page 1sur 12

Master 2 MMS 2018-2019

Apprentissage automatique

TP5 : CART avec la fonction rpart

Exercice 1. Les objectifs :


— Appliquer CART sur un exemple simple.
— Découvrir la fonction rpart.
Reprendre les jeux de données synth_train.txt et synth_test.txt. On a Y ∈ {1, 2} et X ∈ R2 . On dispose
de 100 données d’apprentissage et 200 données test.
1. Charger le jeu de données d’apprentissage.
train <- read.table(file="synth_train.txt", header=TRUE)
X <- train[,-1]
Y <- train$y
plot(X, pch=Y, col=Y, main="trainins set")
legend("topleft", legend=c("classe1", "classe2"), pch=1:2, col=1:2)

trainins set

classe1
classe2
3.0
2.5
2.0
x2

1.5
1.0
0.5
0.0

−2 −1 0 1

x1

dim(train)
## [1] 100 3
train$y <- as.factor(train$y)
table(train$y)
##
## 1 2
## 22 78

2. Charger le package rpart (rpart = recursive partitioning) et consulter l’aide des fonctions rpart
et rpart.control (ainsi que la vignette du fichier logintro.pdf ) :
— quel algorithme est appliqué lorsque la variable à expliquer est qualitative ?
— en laissant les paramètres par défaut, quelle version de cet algorithme est utilisée (priors, coûts,
fonction d’impureté, critère d’arrêt, paramètre de complexité) ?

1
— quelle est la différence entre un competitor et un surrogate ?
— comment sont gérées les données manquantes dans cet algorithme ?
library(rpart)
help(rpart)
help(rpart.control)

3. Construire un objet tree à l’aide de la fonction rpart en laissant tous les paramètres par défaut.
Afficher tree, avec la fonction print et expliquez toutes les sorties.

tree <- rpart(y~., data=train)


#tree <- rpart(y~., data=train,method="class") # si y pas type factor
class(tree)
## [1] "rpart"
print(tree)
## n= 100
##
## node), split, n, loss, yval, (yprob)
## * denotes terminal node
##
## 1) root 100 22 2 (0.22000000 0.78000000)
## 2) x1< -1.002376 16 1 1 (0.93750000 0.06250000) *
## 3) x1>=-1.002376 84 7 2 (0.08333333 0.91666667) *

4. Pour avoir plus de détails sur le déroulement de l’algorithme, afficher l’arbre avec la fonction
summary. Expliquez toutes les sorties. Pourquoi l’algorithme s’arrête ici après une seule division ?

summary(tree)
## Call:
## rpart(formula = y ~ ., data = train)
## n= 100
##
## CP nsplit rel error xerror xstd
## 1 0.6363636 0 1.0000000 1.0000000 0.1882938
## 2 0.0100000 1 0.3636364 0.5909091 0.1528652
##
## Variable importance
## x1
## 100
##
## Node number 1: 100 observations, complexity param=0.6363636
## predicted class=2 expected loss=0.22 P(node) =1
## class counts: 22 78
## probabilities: 0.220 0.780
## left son=2 (16 obs) right son=3 (84 obs)
## Primary splits:
## x1 < -1.002376 to the left, improve=19.611670, (0 missing)
## x2 < 1.513305 to the right, improve= 8.952017, (0 missing)
##

2
## Node number 2: 16 observations
## predicted class=1 expected loss=0.0625 P(node) =0.16
## class counts: 15 1
## probabilities: 0.938 0.062
##
## Node number 3: 84 observations
## predicted class=2 expected loss=0.08333333 P(node) =0.84
## class counts: 7 77
## probabilities: 0.083 0.917
# réduction de l'impureté première division (avec Gini)
# 1-0.22^2-0.78^2-0.16*(1-0.938^2-0.062^2)-0.84*(1-0.083^2-0.917^2)
# arrêt des divisions car minsplit=20 (n=16 dans node2) et
# cp <- 0.01 (dans node3 probablement)

5. Tracer enfin l’arbre à l’aide des méthodes plot puis text.


plot(tree)
text(tree)

x1< −1.002
|

1 2

6. Charger le jeu de données test puis representer la coupure x1 < −1.002 sur le plot des données
d’apprentissage puis des données test.
test <- read.table(file="synth_test.txt", header=TRUE)

par(mfrow=c(1:2))
plot(X, pch=Y, col=Y, main="trainins set")
legend("topleft", legend=c("classe1", "classe2"), pch=1:2, col=1:2)
abline(v=-1.002,lty=2,col=4)
Xtest <- test[,-1]
Ytest <- test$y
plot(Xtest, pch=Ytest, col=Ytest, main="test set")
legend("topleft", legend=c("classe1", "classe2"), pch=1:2, col=1:2)
abline(v=-1.002,lty=2,col=4)

3
trainins set test set

4
classe1 classe1
3.0 classe2 classe2

3
2.5
2.0

2
x2

x2
1.5
1.0

1
0.5

0
0.0

−2 −1 0 1 −2.5 −2.0 −1.5 −1.0 −0.5 0.0 0.5 1.0

x1 x1

7. Calculer le taux d’erreur d’apprentissage et le taux d’erreur test.


?predict.rpart
pred.test <- predict(tree, newdata=test, type="class")
sum(pred.test!=test$y)/length(pred.test) # taux erreur test
## [1] 0.19

8. Calculer à la main les probabilités à posteriori de la première et de la 10ème donnée test. Calculer
ensuite avec la méthode predict les probabilités à posteriori de toutes les données test.

prob <- predict(tree, newdata=test, type="prob")


head(prob)
## 1 2
## 1 0.08333333 0.9166667
## 2 0.08333333 0.9166667
## 3 0.08333333 0.9166667
## 4 0.08333333 0.9166667
## 5 0.08333333 0.9166667
## 6 0.08333333 0.9166667

9. Modifier les paramètres de la fonction rpart pour construire l’arbre de longeur maximal Tmax
avec comme seul critère d’arrêt le nombre minimum d’observation dans le noeud nmin = 2. Tester
ensuite différentes options des méthodes plot et text données dans la vignette logintro.pdf.

tree <- rpart(y~., data=train, minsplit=2,cp=0)

plot(tree)
text(tree,use.n = TRUE,all=TRUE)

4
x1< −1.002
2|
22/78

x2>=0.7755 x2>=1.945
1 x1< −1.484 2
1 15/1 1 x1>=−0.7079 7/77 x1< −0.6228
14/0 1 1/1 2 x1< −0.008248 1 x2>=1.318 2
1/0 0/1 1 x1>=0.26174/2 2 2 3/75 2
1 4/1 1 0/1 3/7 0/68
2/0 1 2/1 2
2/0 0/1 1 2
3/0 0/7

10. Afficher cet arbre avec la fonction printcp. Expliquez les sorties.

#erreur relative=erreur de l'arbre/erreur du noeud racine


printcp(tree)
##
## Classification tree:
## rpart(formula = y ~ ., data = train, minsplit = 2, cp = 0)
##
## Variables actually used in tree construction:
## [1] x1 x2
##
## Root node error: 22/100 = 0.22
##
## n= 100
##
## CP nsplit rel error xerror xstd
## 1 0.636364 0 1.000000 1.00000 0.18829
## 2 0.090909 1 0.363636 0.45455 0.13636
## 3 0.068182 2 0.272727 0.50000 0.14222
## 4 0.045455 4 0.136364 0.45455 0.13636
## 5 0.022727 5 0.090909 0.31818 0.11598
## 6 0.000000 9 0.000000 0.31818 0.11598
#summary(tree)
#print(tree)

11. Afficher cet arbre avec la fonction plotcp. Expliquez le graphique.


?plotcp
plotcp(tree)

5
size of tree

1 2 3 5 6 10

1.2
1.0
X−val Relative Error

0.8
0.6
0.4
0.2

Inf 0.24 0.079 0.056 0.032 0

cp

#en abscisse les beta_j=sqrt(cp_j*cp_{j-1})


cp <- tree$cptable[,1]
#beta1=inf
#beta2=sqrt(cp2*cp1)
sqrt(cp[2]*cp[1])
## 2
## 0.2405228
#beta4
sqrt(cp[4]*cp[3])
## 4
## 0.05567022
#en haut size of tree=nb noeux terminaux
#pour cp=beta2=0.24 i.e. cp dans [alpha2,alpha3[=[0.09,0.63[ deux noeux terminaux i.e. 1 spl
#pour cp=beta4=0.056 i.e. cp dans [alpha4,alpha5[=[0.045,0.02[ cinq noeux terminaux i.e. 4 s
#La ligne pointillée: min(err-rel-CV)+sd(min-err-rel-CV)
xerror <- tree$cptable[,4] #erreur relative en CV
xstd <- tree$cptable[,5] #ecart-type de l'erreur relative de CV
min(xerror)
## [1] 0.3181818
min(xerror)+xstd[which.min(xerror)] #ligne pointillée
## 5
## 0.4341577

6
12. A partir de ce graphique, choisir la valeur du paramètre de complexité cp avec la règle dite du
"1-SE" et élaguer l’arbre Tmax avec la fonction prune.
#règle du 1-SE: choisir beta_j (le plus petit possible pour avoir l'arbre le plus simple pos
#dont la xerror est à moins d'un écart-type (CV) de la ligne pointillée.
#Ici on choisit cp=0.24 et donc l'abre à deux feuilles.
tree2 <- prune(tree, cp=0.24)
tree2
## n= 100
##
## node), split, n, loss, yval, (yprob)
## * denotes terminal node
##
## 1) root 100 22 2 (0.22000000 0.78000000)
## 2) x1< -1.002376 16 1 1 (0.93750000 0.06250000) *
## 3) x1>=-1.002376 84 7 2 (0.08333333 0.91666667) *

13. Vérifiez que vous comprenez le code suivant et ses sorties qui permettent d’introduire une matrice
de coûts. En particulier, comparer les coûts de mauvaise classification (LOSS) obtenus avec et sans
matrice de coût. Vous semble-il logique que les questions binaires soient différentes avec et sans
matrice de coût ? Cherchez des éléments de réponse dans le fichier logintro.pdf.
parms=list(loss=matrix(c(0,2,1,0), byrow=TRUE, nrow=2))
tree <- rpart(y~., data=train, method="class",parms=parms)
print(tree)
## n= 100
##
## node), split, n, loss, yval, (yprob)
## * denotes terminal node
##
## 1) root 100 44 2 (0.22000000 0.78000000)
## 2) x1< -0.6228014 28 9 1 (0.67857143 0.32142857) *
## 3) x1>=-0.6228014 72 6 2 (0.04166667 0.95833333)
## 6) x2>=1.780615 7 4 1 (0.42857143 0.57142857) *
## 7) x2< 1.780615 65 0 2 (0.00000000 1.00000000) *
#summary(tree)
#cp(t1)= (44-9-6)/44

Exercice 2. On reprend les données concernant n = 1260 exploitations agricoles. Les variables expli-
catives sont p = 30 critères économiques et financiers et la variable qualitative à expliquer est la variable
difficulté de paiement (0=saine t 1=défaillant).
1. Charger le jeu de données Desbois_complet.rda dans R.
load("Desbois_complet.rda")
table(data$DIFF)
##
## 0 1
## 653 607

7
2. Créez un découpage aléatoire des données en 945 observations d’apprentissage et 315 observations
test.
set.seed(10)
tr <- sample(1:nrow(data),945)
train <- data[tr,]
test <- data[-tr,]

3. Constuire l’arbre de classification CART avec les données d’apprentissage en laissant les options
par défaut. Visualisez l’arbre en utilisant le package rpart.plot pour améliorer le graphique.
4. Visualiser ensuite la décroissante de l’erreur relative de validation croisée avec la fonction plotcp.
Recommencez plusieurs fois. Que constatez-vous ?
tree <- rpart(DIFF~., data=train,method="class")
plotcp(tree)
size of tree

1 2 4 6
1.2
1.0
X−val Relative Error

0.8
0.6
0.4
0.2

Inf 0.16 0.026 0.013

cp

printcp(tree)
##
## Classification tree:
## rpart(formula = DIFF ~ ., data = train, method = "class")
##
## Variables actually used in tree construction:
## [1] R14 R21 R22 R3 R6
##
## Root node error: 461/945 = 0.48783
##
## n= 945
##
## CP nsplit rel error xerror xstd
## 1 0.668113 0 1.00000 1.06074 0.033321
## 2 0.037961 1 0.33189 0.33623 0.024692
## 3 0.017354 3 0.25597 0.29935 0.023548
## 4 0.010000 5 0.22126 0.27766 0.022819

8
5. Quel valeur du paramètre de complexité choisiriez-vous pour minimiser l’erreur relative de valida-
tion croisée ? Même question en utilisant la règle dite du "1-SE". Elaguez alors l’arbre en fonction
de cette valeur.

tree2 <- prune(tree, cp=0.026)


tree2
## n= 945
##
## node), split, n, loss, yval, (yprob)
## * denotes terminal node
##
## 1) root 945 461 0 (0.51216931 0.48783069)
## 2) R14< 0.57935 539 104 0 (0.80705009 0.19294991)
## 4) R6< 0.9205 381 24 0 (0.93700787 0.06299213) *
## 5) R6>=0.9205 158 78 1 (0.49367089 0.50632911)
## 10) R22< 0.5057 69 18 0 (0.73913043 0.26086957) *
## 11) R22>=0.5057 89 27 1 (0.30337079 0.69662921) *
## 3) R14>=0.57935 406 49 1 (0.12068966 0.87931034) *

6. Diminuez maintenant la valeur de l’argument cp dans la fonction rpart pour constuire l’arbre
maximal et élaguer cet arbre en choisissant (automatiquement) la valeur du paramètre de com-
plexité qui minimise l’erreur de validation croisée.

tree <- rpart(DIFF~., data=train,cp=0,method="class")


plotcp(tree)
size of tree

1 2 4 6 10 13 15 18 21
1.0
0.8
X−val Relative Error

0.6
0.4
0.2

Inf 0.16 0.026 0.011 0.0053 0.0038 0.0022 0.001 0

cp

cpopt <- tree$cptable[which.min(tree$cptable[,"xerror"]),"CP"]


tree2 <- prune(tree, cp=cpopt)
plot(tree2)
text(tree2)

9
R14< 0.5794
|

R6< 0.9205 R21< 0.12


R22< 0.5057 R3< 0.5953
0 1
0 1
R21< 0.351
0 R37< 0.5325
R3>=0.1617 1
R6< 1.453 1
0 1 1

7. Elaguer maintenant l’arbre maximal en choisissant (automatiquement) la valeur du paramètre de


complexité avec la règle dite du "1-SE".

#La ligne pointillée: min(err-rel-CV)+sd(min-err-rel-CV)


#ligne pointillée
ligne <- min(tree$cptable[,"xerror"])+tree$cptable[,"xstd"][which.min(tree$cptable[,"xerror"
min(which(tree$cptable[,"xerror"]<=ligne))
## [1] 4
cp1SE <- tree$cptable[min(which(tree$cptable[,"xerror"]<=ligne)),"CP"]
tree3 <- prune(tree, cp=cp1SE)
plot(tree3)
text(tree3)

R14< 0.5794
|

R6< 0.9205 R21< 0.12

R22< 0.5057 R3< 0.5953


0 1
0 1
0 1

8. Sur les données test calculer le taux d’erreur, tracer la courbe ROC et calculer le AUC.

10
pred.test <- predict(tree3, newdata=test[,-1],type="class")
sum(pred.test!=test$DIFF)/length(pred.test) # taux erreur test
## [1] 0.1238095

9. Comparer alors pour plusieurs découpages les performances de la LDA, la régression logistique, de
CART avec le paramétrage par défaut de rpart, CART avec l’élaguage selon la valeur du cp qui
minimise l’erreur relative de validation croisée, CART avec l’élaguage selon la valeur du cp choisie
avec la règle du 1-SE.
B <- 100
err_lda <- rep(NA,B)
err_glm <- rep(NA,B)
err_rpart <- rep(NA,B)
err_elag <- rep(NA,B)
err_elag2 <- rep(NA,B)

for (b in 1:B)
{
tr <- sample(1:nrow(data),945)
train <- data[tr,]
test <- data[-tr,]

#Estimation des modèles


library(MASS)
g1 <- lda(DIFF~.,data=train) #LDA
g2 <- glm(DIFF~.,data=train,family=binomial) #logistique
g3 <- rpart(DIFF~., data=train,method="class")
tree <- rpart(DIFF~., data=train,cp=0,method="class")
cpopt <- tree$cptable[which.min(tree$cptable[,"xerror"]),"CP"]
g4 <- prune(tree, cp=cpopt)
ligne <- min(tree$cptable[,"xerror"])+
tree$cptable[,"xstd"][which.min(tree$cptable[,"xerror"])]
min(which(tree$cptable[,"xerror"]<=ligne))
cp1SE <- tree$cptable[min(which(tree$cptable[,"xerror"]<=ligne)),"CP"]
g5 <- prune(tree, cp=cp1SE)

#Predictions et erreur test


pred1<- predict(g1,test[,-1])$class
err_lda[b] <- sum(pred1!=test$DIFF)/length(test$DIFF)
prob <- predict(g2,test[,-1],type="response")
pred2 <- 1*I(prob>0.5)+0*I(prob<=0.5)
err_glm[b] <- sum(pred2!=test$DIFF)/length(test$DIFF)
pred3 <- predict(g3,test[,-1],type="class")
err_rpart[b] <- sum(pred3!=test$DIFF)/length(test$DIFF)
pred4 <- predict(g4,test[,-1],type="class")
err_elag[b] <- sum(pred4!=test$DIFF)/length(test$DIFF)
pred5 <- predict(g5,test[,-1],type="class")
err_elag2[b] <- sum(pred5!=test$DIFF)/length(test$DIFF)
}

11
err <- data.frame(lda=err_lda,glm=err_glm,rpart=err_rpart,
rpart_elag=err_elag,rpart_1SE=err_elag2)
save(err,file="err_Dubois.RData")

Erreurs test pour 100 decoupages

0.18
0.16
0.14
0.12
0.10
0.08
0.06

lda glm rpart rpart_elag rpart_1SE

12

Vous aimerez peut-être aussi