Arbre de décision dans R : arbre de classification avec exemple
⚡ Résumé intelligent
Les arbres de décision en R divisent les données en branches à l'aide de règles binaires (oui/non) jusqu'à ce que chaque feuille contienne une seule classe dominante. Ce tutoriel explique comment construire, visualiser, évaluer et optimiser un arbre de classification rpart sur le jeu de données de survie du Titanic.

Que sont les arbres de décision ?
Arbres de décision Les arbres de décision sont des algorithmes d'apprentissage automatique polyvalents, capables d'effectuer des tâches de classification et de régression. Ce sont des algorithmes très performants, aptes à traiter des ensembles de données complexes. De plus, les arbres de décision sont des composantes fondamentales des forêts aléatoires, qui figurent parmi les algorithmes d'apprentissage automatique les plus puissants actuellement disponibles.
Avant d'en construire un par programmation, il est utile de comprendre comment un arbre décide où se diviser.
Comment fonctionne un arbre de décision ?
Un arbre de décision transforme un ensemble de données en un organigramme de questions par oui ou par non, construit à partir de trois types de nœuds : racine contient chaque observation de formation, un nœud interne pose une question sur un prédicteur et divise les données en deux, et un feuille arrête la division et renvoie la classe majoritaire.
La croissance suit une procédure gourmande appelée partitionnement binaire récursif:
- Évaluer chaque segment de candidat. Pour chaque prédicteur et point de coupure, mesurez le degré d'impureté des deux groupes résultants.
- Gardez le meilleur. La division qui réduit le plus l'impureté devient la question posée à ce nœud.
- Répétez l'opération pour chaque enfant. jusqu'à ce qu'une règle de contrôle l'arrête : minsplit, minbucket, maxdepth ou cp.
- Élaguer. cp supprime ensuite les branches qui ne sont pas rentables, ce qui empêche l'arbre de mémoriser l'ensemble d'entraînement.
Comme chaque question compare une variable à un seuil, l'algorithme n'a jamais besoin de mise à l'échelle ni de codage fictif.
Indice de Gini vs Entropie dans les arbres de décision
Cette impureté peut être mesurée de deux manières, et rpart vous permet de choisir.
| Critères | Index de Gini | Entropie (Gain d'information) |
|---|---|---|
| Laits en poudre | 1 – somme des carrés des proportions de classes | -somme de p fois log2(p) |
| Gamme (deux classes) | 0 à 0.5 ans, qui | 0 à 1 ans, qui |
| Calcul | Plus rapide, sans logarithme | Plus lent, utilise des logarithmes |
| Paramètres rpart | Réglage par défaut | parms = liste(split = "information") |
fit_entropy <- rpart(survived~., data = data_train, method = 'class', parms = list(split = "information"))
En pratique, les deux critères aboutissent la plupart du temps à la même répartition, l'indice de Gini par défaut est donc un choix sûr.
Avantages et inconvénients des arbres de décision
Ces compromis vous indiquent quand un seul arbre suffit et quand il faut passer à un ensemble.
Avantages
- Entièrement interprétable : Le modèle ajusté est un diagramme que toute partie prenante peut lire.
- Prétraitement minimal : Aucune mise à l'échelle ni normalisation n'est requise, et les facteurs fonctionnent nativement.
- Gère les deux tâches : La méthode 'class' ajuste un classificateur et la méthode 'anova' ajuste un arbre de régression.
- Rapide à former : Les grands ensembles de données sont traités en quelques secondes, les arbres constituent donc une première base de référence utile.
Désavantages
- Variabilité élevée : Une petite modification des données d'entraînement peut produire un arbre complètement différent.
- Tendance au surapprentissage : Un arbre sans contrainte pousse jusqu'à ce que chaque feuille soit pure, sauf si les paramètres cp et maxdepth le contraignent.
- Divisions uniquement parallèles aux axes : Les limites diagonales nécessitent de nombreuses découpes en forme d'escalier.
Le remède aux deux premières faiblesses consiste à moyenner de nombreux arbres, ce qui est ce que fait un forêt aléatoire t.
Comment entraîner et visualiser un arbre de décision en R
Pour construire votre premier arbre de décision en R, vous suivrez sept étapes :
- Étape 1 : Importer les données
- Étape 2 : Nettoyer l'ensemble de données
- Étape 3 : Créer un ensemble d'entraînement/de test
- Étape 4 : Construire le modèle
- Étape 5 : Faire une prédiction
- Étape 6 : Mesurer les performances
- Étape 7 : Ajustez les hyper-paramètres
Étape 1) Importez les données
Si vous êtes curieux de connaître le sort du Titanic, vous pouvez regarder cette vidéo sur Youtube. Le but de cet ensemble de données est de prédire quelles personnes ont le plus de chances de survivre après une collision avec un iceberg. L'ensemble de données contient 13 variables et 1309 observations. L'ensemble de données est ordonné par la variable X.
set.seed(678) path <- 'https://raw.githubusercontent.com/guru99-edu/R-Programming/master/titanic_data.csv' titanic <-read.csv(path) head(titanic)
Sortie :
## X pclass survived name sex ## 1 1 1 1 Allen, Miss. Elisabeth Walton female ## 2 2 1 1 Allison, Master. Hudson Trevor male ## 3 3 1 0 Allison, Miss. Helen Loraine female ## 4 4 1 0 Allison, Mr. Hudson Joshua Creighton male ## 5 5 1 0 Allison, Mrs. Hudson J C (Bessie Waldo Daniels) female ## 6 6 1 1 Anderson, Mr. Harry male ## age sibsp parch ticket fare cabin embarked ## 1 29.0000 0 0 24160 211.3375 B5 S ## 2 0.9167 1 2 113781 151.5500 C22 C26 S ## 3 2.0000 1 2 113781 151.5500 C22 C26 S ## 4 30.0000 1 2 113781 151.5500 C22 C26 S ## 5 25.0000 1 2 113781 151.5500 C22 C26 S ## 6 48.0000 0 0 19952 26.5500 E12 S ## home.dest ## 1 St Louis, MO ## 2 Montreal, PQ / Chesterville, ON ## 3 Montreal, PQ / Chesterville, ON ## 4 Montreal, PQ / Chesterville, ON ## 5 Montreal, PQ / Chesterville, ON ## 6 New York, NY
tail(titanic)
Sortie :
## X pclass survived name sex age sibsp ## 1304 1304 3 0 Yousseff, Mr. Gerious male NA 0 ## 1305 1305 3 0 Zabour, Miss. Hileni female 14.5 1 ## 1306 1306 3 0 Zabour, Miss. Thamine female NA 1 ## 1307 1307 3 0 Zakarian, Mr. Mapriededer male 26.5 0 ## 1308 1308 3 0 Zakarian, Mr. Ortin male 27.0 0 ## 1309 1309 3 0 Zimmerman, Mr. Leo male 29.0 0 ## parch ticket fare cabin embarked home.dest ## 1304 0 2627 14.4583 C ## 1305 0 2665 14.4542 C ## 1306 0 2665 14.4542 C ## 1307 0 2656 7.2250 C ## 1308 0 2670 7.2250 C ## 1309 0 315082 7.8750 S
À partir des sorties head et tail, vous pouvez remarquer que les données ne sont pas mélangées. C'est un gros problème! Lorsque vous diviserez vos données entre une rame et une rame de test, vous sélectionnerez uniquement le passager des classes 1 et 2 (aucun passager de la classe 3 ne figure dans les 80 % des observations), ce qui signifie que l'algorithme ne verra jamais les caractéristiques du passager de la classe 3. Cette erreur entraînera une mauvaise prédiction.
Pour résoudre ce problème, vous pouvez utiliser la fonction sample().
shuffle_index <- sample(1:nrow(titanic)) head(shuffle_index)
Arbre de décision Code R Explication
- sample(1:nrow(titanic)) : génère une liste aléatoire d'index de 1 à 1309 (c'est-à-dire le nombre maximum de lignes).
Sortie :
## [1] 288 874 1078 633 887 992
Vous utiliserez cet index pour mélanger l'ensemble de données titanesque.
titanic <- titanic[shuffle_index, ]
head(titanic)
Sortie :
## X pclass survived ## 288 288 1 0 ## 874 874 3 0 ## 1078 1078 3 1 ## 633 633 3 0 ## 887 887 3 1 ## 992 992 3 1 ## name sex age ## 288 Sutton, Mr. Frederick male 61 ## 874 Humblen, Mr. Adolf Mathias Nicolai Olsen male 42 ## 1078 O'Driscoll, Miss. Bridget female NA ## 633 Andersson, Mrs. Anders Johan (Alfrida Konstantia Brogren) female 39 ## 887 Jermyn, Miss. Annie female NA ## 992 Mamee, Mr. Hanna male NA ## sibsp parch ticket fare cabin embarked home.dest## 288 0 0 36963 32.3208 D50 S Haddenfield, NJ ## 874 0 0 348121 7.6500 F G63 S ## 1078 0 0 14311 7.7500 Q ## 633 1 5 347082 31.2750 S Sweden Winnipeg, MN ## 887 0 0 14313 7.7500 Q ## 992 0 0 2677 7.2292 C
Étape 2) Nettoyer l'ensemble de données
Plusieurs variables contiennent des valeurs NA. Le nettoyage se déroule en trois parties :
- Supprimez les variables home.dest, cabin, name, X et ticket
- Créer des variables factorielles pour pclass et survécu
- Abandonnez le NA
library(dplyr) # Drop variables clean_titanic <- titanic %>% select(-c(home.dest, cabin, name, X, ticket)) %>% #Convert to factor level mutate(pclass = factor(pclass, levels = c(1, 2, 3), labels = c('Upper', 'Middle', 'Lower')), survived = factor(survived, levels = c(0, 1), labels = c('No', 'Yes'))) %>% na.omit() glimpse(clean_titanic)
Code Explication
- select(-c(home.dest, cabin, name, X, ticket)) : supprimez les variables inutiles
- pclass = factor(pclass, levels = c(1,2,3), labels= c('Supérieur', 'Moyen', 'Inférieur')): Ajoute une étiquette à la variable pclass. 1 devient Supérieur, 2 devient Moyen et 3 devient Inférieur
- factor(survived, levels = c(0,1), labels = c('Non', 'Oui')): Ajoute des étiquettes à la variable survived. 0 devient Non et 1 devient Oui
- na.omit() : Supprime les observations NA
Sortie :
## Observations: 1,045 ## Variables: 8 ## $ pclass <fctr> Upper, Lower, Lower, Upper, Middle, Upper, Middle, U... ## $ survived <fctr> No, No, No, Yes, No, Yes, Yes, No, No, No, No, No, Y... ## $ sex <fctr> male, male, female, female, male, male, female, male... ## $ age <dbl> 61.0, 42.0, 39.0, 49.0, 29.0, 37.0, 20.0, 54.0, 2.0, ... ## $ sibsp <int> 0, 0, 1, 0, 0, 1, 0, 0, 4, 0, 0, 1, 1, 0, 0, 0, 1, 1,... ## $ parch <int> 0, 0, 5, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 2, 0, 4, 0,... ## $ fare <dbl> 32.3208, 7.6500, 31.2750, 25.9292, 10.5000, 52.5542, ... ## $ embarked <fctr> S, S, S, S, S, S, S, S, S, C, S, S, S, Q, C, S, S, C...
Étape 3) Créer un ensemble d'entraînement/test
Avant d'entraîner votre modèle, vous devez effectuer deux étapes :
- Créez un ensemble de train et de test : vous entraînez le modèle sur la rame et testez la prédiction sur l'ensemble de test (c'est-à-dire des données invisibles)
- Installez rpart.plot depuis la console
La pratique courante consiste à diviser les données à 80/20, 80 % des données servant à entraîner le modèle et 20 % à faire des prédictions. Vous devez créer deux blocs de données distincts. Vous ne voulez pas toucher à l'ensemble de test tant que vous n'avez pas fini de créer votre modèle. Vous pouvez créer un nom de fonction create_train_test() qui prend trois arguments.
create_train_test(df, size = 0.8, train = TRUE) arguments: -df: Dataset used to train the model. -size: Size of the split. By default, 0.8. Numerical value -train: If set to `TRUE`, the function creates the train set, otherwise the test set. Default value sets to `TRUE`. Boolean value.You need to add a Boolean parameter because R does not allow to return two data frames simultaneously.
create_train_test <- function(data, size = 0.8, train = TRUE) { n_row = nrow(data) total_row = size * n_row train_sample <- 1: total_row if (train == TRUE) { return (data[train_sample, ]) } else { return (data[-train_sample, ]) } }
Code Explication
- function(data, size=0.8, train = TRUE) : Ajoutez les arguments dans la fonction
- n_row = nrow(data) : compte le nombre de lignes dans l'ensemble de données
- total_row = size*n_row : renvoie la nième ligne pour construire la rame
- train_sample <- 1:total_row : sélectionnez la première ligne jusqu'à la nième ligne
- if (train ==TRUE){ } else { } : si la condition est définie sur true, renvoie la rame, sinon l'ensemble de test.
Vous pouvez tester votre fonction et vérifier la dimension.
data_train <- create_train_test(clean_titanic, 0.8, train = TRUE) data_test <- create_train_test(clean_titanic, 0.8, train = FALSE) dim(data_train)
Sortie :
## [1] 836 8
dim(data_test)
Sortie :
## [1] 209 8
L'ensemble de données d'entraînement comporte 836 lignes et 8 colonnes, tandis que l'ensemble de données de test comporte 209 lignes et les mêmes 8 colonnes.
Vous utilisez la fonction prop.table() combinée avec table() pour vérifier si le processus de randomisation est correct.
prop.table(table(data_train$survived))
Sortie :
## ## No Yes ## 0.5944976 0.4055024
prop.table(table(data_test$survived))
Sortie :
## ## No Yes ## 0.5789474 0.4210526
Dans les deux ensembles de données, le nombre de survivants est le même, environ 40 pour cent.
Installer rpart.plot
rpart.plot n'est pas disponible dans les bibliothèques conda. Vous pouvez l'installer depuis la console :
install.packages("rpart.plot")
Étape 4) Construire le modèle
Vous êtes prêt à construire le modèle. La syntaxe de la fonction d'arbre de décision rpart() est la suivante :
rpart(formula, data=, method='') arguments: - formula: The function to predict - data: Specifies the data frame - method: - "class" for a classification tree - "anova" for a regression tree
Vous utilisez la méthode de classe parce que vous prédisez une classe.
library(rpart) library(rpart.plot) fit <- rpart(survived~., data = data_train, method = 'class') rpart.plot(fit, extra = 106)
Code Explication
- rpart() : Fonction pour ajuster le modèle. Les arguments sont :
- survécu ~.: Formule des arbres de décision
- data = data_train : ensemble de données
- method = 'class' : Ajuster un modèle binaire
- rpart.plot(fit, extra= 106) : Affiche l’arbre. L’argument extra est fixé à 106, ce qui affiche la probabilité de la seconde classe ainsi que le pourcentage d’observations dans chaque nœud. Vous pouvez vous référer à… vignette pour plus d’informations sur les autres choix.
Sortie :
Vous commencez au nœud racine, en haut du graphe et à la profondeur 0 sur 3 :
- Au sommet, c’est la probabilité globale de survie. Il montre la proportion de passagers qui ont survécu à l'accident. 41 pour cent des passagers ont survécu.
- Ce nœud vérifie si le passager est un homme. Si oui, on descend au niveau 1, c'est-à-dire au fils gauche de la racine. 63 % des passagers sont des hommes, avec une probabilité de survie de 21 %.
- Dans le deuxième nœud, vous demandez si le passager masculin a plus de 3.5 ans. Si oui, les chances de survie sont de 19 pour cent.
- Vous continuez ainsi pour comprendre quelles caractéristiques ont un impact sur les chances de survie.
Notez que l’une des nombreuses qualités des arbres de décision est qu’ils nécessitent très peu de préparation des données. En particulier, ils ne nécessitent pas de mise à l’échelle ou de centrage des fonctionnalités.
Par défaut, la fonction rpart() utilise Gini La mesure d'impureté sert à déterminer chaque division. Plus la valeur de Gini est élevée, plus les classes au sein de ce nœud sont mélangées ; l'algorithme choisit donc toujours la division qui la diminue le plus.
Étape 5) Faites une prédiction
Vous pouvez prédire votre ensemble de données de test. Pour faire une prédiction, vous pouvez utiliser la fonction prédire(). La syntaxe de base de la prévision pour l'arbre de décision R est :
predict(fitted_model, df, type = 'class') arguments: - fitted_model: This is the object stored after model estimation. - df: Data frame used to make the prediction - type: Type of prediction - 'class': for classification - 'prob': to compute the probability of each class - 'vector': Predict the mean response at the node level
Vous devez maintenant prédire, pour chacun des 209 passagers de l'ensemble de test, si le modèle s'attend à ce qu'ils survivent à la collision.
predict_unseen <-predict(fit, data_test, type = 'class')
Code Explication
- predict(fit, data_test, type = 'class') : prédire la classe (0/1) de l'ensemble de test
Comparez maintenant les classes prédites avec les résultats réels.
table_mat <- table(data_test$survived, predict_unseen)
table_mat
Code Explication
- table(data_test$survived, predict_unseen): Construire un tableau de contingence des classes prédites par rapport au résultat réel
Sortie :
## predict_unseen ## No Yes ## No 106 15 ## Yes 30 58
Les lignes représentent les valeurs réelles, les colonnes les prédictions. Le modèle a correctement identifié 106 non-survivants et 58 survivants, mais a classé par erreur 15 non-survivants comme survivants et 30 survivants comme décédés.
Étape 6) Mesurer les performances
Vous pouvez calculer une mesure de précision pour une tâche de classification avec l'outil matrice de confusion:
Le matrice de confusion est un meilleur choix pour évaluer les performances de classification. L'idée générale est de compter le nombre de fois où les instances vraies sont classées comme étant fausses.
Chaque ligne d'une matrice de confusion représente une cible réelle, tandis que chaque colonne représente une cible prédite. La première ligne de cette matrice considère les passagers décédés (la classe négative) : 106 ont été correctement classés comme décédés (Vrai négatif), tandis que 15 ont été classés à tort comme survivants (Faux positifLa deuxième ligne concerne les survivants : 58 ont été correctement identifiés (Vrai positif), tandis que 1 074 ont été manqués (Faux négatif).
Vous pouvez calculer le test de précision de la matrice de confusion :
C'est la proportion de vrais positifs et de vrais négatifs sur la somme de la matrice. Avec R, vous pouvez coder comme suit :
accuracy_Test <- sum(diag(table_mat)) / sum(table_mat)
Code Explication
- sum(diag(table_mat)) : Somme de la diagonale
- sum(table_mat) : Somme de la matrice.
Vous pouvez imprimer la précision de l'ensemble de test :
print(paste('Accuracy for test', accuracy_Test))
Sortie :
## [1] "Accuracy for test 0.784688995215311"
La précision sur l'ensemble de test est de 0.7847, soit 78.47 %. Répétez l'exercice sur l'ensemble d'entraînement pour observer le degré de surapprentissage du modèle.
Étape 7) Ajustez les hyper-paramètres
L'arbre de décision dans R comporte divers paramètres qui contrôlent les aspects de l'ajustement. Dans la bibliothèque d'arbres de décision rpart, vous pouvez contrôler les paramètres à l'aide de la fonction rpart.control(). Dans le code suivant, vous introduisez les paramètres que vous allez régler. Vous pouvez vous référer au vignette pour les autres paramètres.
rpart.control(minsplit = 20, minbucket = round(minsplit/3), maxdepth = 30) Arguments: -minsplit: Set the minimum number of observations in the node before the algorithm perform a split -minbucket: Set the minimum number of observations in a terminal node, i.e. the leaf -maxdepth: Set the maximum depth of any node of the final tree. The root node is treated as depth 0
Nous procéderons de la manière suivante :
- Construire une fonction pour renvoyer la précision
- Ajustez la profondeur maximale
- Ajustez le nombre minimum d'échantillons qu'un nœud doit avoir avant de pouvoir se diviser
- Ajustez le nombre minimum d’échantillons qu’un nœud feuille doit avoir
Vous pouvez écrire une fonction pour afficher la précision. Vous enveloppez simplement le code que vous avez utilisé auparavant :
- prédire : prédire_unseen <- prédire (fit, data_test, type = 'class')
- Produire la table : table_mat <- table(data_test$survived, prédict_unseen)
- Précision du calcul : précision_Test <- sum(diag(table_mat))/sum(table_mat)
accuracy_tune <- function(fit) { predict_unseen <- predict(fit, data_test, type = 'class') table_mat <- table(data_test$survived, predict_unseen) accuracy_Test <- sum(diag(table_mat)) / sum(table_mat) accuracy_Test }
Ajustez maintenant les paramètres et voyez si vous pouvez améliorer le modèle par défaut. Pour rappel, vous devez atteindre une précision supérieure à 0.7847.
control <- rpart.control(minsplit = 4,
minbucket = round(5 / 3),
maxdepth = 3,
cp = 0)
tune_fit <- rpart(survived~., data = data_train, method = 'class', control = control)
accuracy_tune(tune_fit)
Sortie :
## [1] 0.7990431
Avec le paramètre suivant :
minsplit = 4
minbucket = round(5/3)
maxdepth = 3
cp = 0
La précision passe de 0.7847 à 0.7990, ce qui signifie que l'arbre optimisé surpasse la configuration par défaut d'environ 1.4 point de pourcentage.
Arbres de décision en R : Guide de référence rapide des fonctions
Le tableau ci-dessous répertorie toutes les fonctions utilisées dans les sept étapes ci-dessus, ainsi que le package qui les fournit et les paramètres qu'elles attendent. R.
| Bibliothèque | Objectif | Fonction | Classe | Paramètres | DÉTAILS |
|---|---|---|---|---|---|
| partie | Arbre de classification des trains dans R | rpart() | classe | formule, df, méthode | |
| partie | Arbre de régression de train | rpart() | anova | formule, df, méthode | |
| partie | Tracer les arbres | rpart.plot() | modèle ajusté | ||
| base | prévoir | prédire() | classe | modèle ajusté, type | |
| base | prévoir | prédire() | problèmes | modèle ajusté, type | |
| base | prévoir | prédire() | vecteur | modèle ajusté, type | |
| partie | Paramètres de contrôle | rpart.control() | minsplit | Définissez le nombre minimum d'observations dans le nœud avant que l'algorithme effectue une division | |
| minbucket | Définir le nombre minimal d'observations dans un nœud terminal, c'est-à-dire la feuille | ||||
| profondeur max | Définissez la profondeur maximale de chaque nœud de l'arbre final. Le nœud racine est considéré comme ayant une profondeur de 0. | ||||
| partie | Modèle de train avec paramètre de contrôle | rpart() | formule, df, méthode, contrôle |
Remarque : entraînez le modèle sur des données d'entraînement et testez les performances sur un ensemble de données invisible, c'est-à-dire un ensemble de test.



