Beslisboom in R: Classificatieboom met voorbeeld
⚡ Slimme samenvatting
Beslissingsbomen in R splitsen gegevens op in takken met behulp van eenvoudige ja/nee-regels totdat elk blad een enkele dominante klasse bevat. Deze handleiding bouwt, plot, evalueert en optimaliseert een rpart-classificatieboom op de Titanic-overlevingsdataset.

Wat zijn beslisbomen?
Beslissingsbomen Beslissingsbomen zijn veelzijdige machine learning-algoritmen die zowel classificatie- als regressietaken kunnen uitvoeren. Het zijn zeer krachtige algoritmen die complexe datasets aankunnen. Bovendien vormen beslissingsbomen een fundamenteel onderdeel van random forests, die behoren tot de krachtigste machine learning-algoritmen die momenteel beschikbaar zijn.
Voordat je er een in code bouwt, is het handig om te weten hoe een boom bepaalt waar hij zich splitst.
Hoe werkt een beslisboom?
Een beslissingsboom zet een dataset om in een stroomdiagram van ja/nee-vragen, opgebouwd uit drie soorten knooppunten: de wortel bewaart elke trainingsobservatie, een interne knooppunt stelt een vraag over één voorspellende variabele en splitst de gegevens in tweeën, en een blad Het splitsen wordt gestopt en de meerderheidsklasse wordt geretourneerd.
Groei volgt een hebzuchtige procedure genaamd recursieve binaire partitionering:
- Evalueer elke kandidaat-splitsing. Meet voor elke voorspellende variabele en grenswaarde hoe onzuiver de twee resulterende groepen zouden zijn.
- Bewaar de beste. De splitsing die de onzuiverheden het meest reduceert, wordt de vraag die op dat knooppunt gesteld wordt.
- Herhaal dit voor elk kind. totdat een besturingsregel dit stopt: minsplit, minbucket, maxdepth of cp.
- Snoeien. cp snoeit vervolgens takken die zichzelf niet terugverdienen, waardoor de boom de trainingsset niet meer onthoudt.
Omdat elke vraag één variabele vergelijkt met een drempelwaarde, heeft het algoritme nooit schaling of dummycodering nodig.
Gini-index versus entropie in beslissingsbomen
Die onzuiverheid kan op twee manieren worden gemeten, en rpart laat je kiezen.
| criteria | Gini-index | Entropie (Informatiewinst) |
|---|---|---|
| Formule | 1 – som van de kwadraten van de klassenproporties | -som van p maal log2(p) |
| Bereik (twee klassen) | 0 tot 0.5 | 0 tot 1 |
| Computation | Sneller, geen logaritme | Langzamer, maakt gebruik van logaritmen |
| rpart-instelling | Standaard | parms = list(split = “information”) |
fit_entropy <- rpart(survived~., data = data_train, method = 'class', parms = list(split = "information"))
In de praktijk kiezen beide criteria meestal dezelfde splitsing, dus de standaard Gini-index is een veilige keuze.
Voordelen en nadelen van beslissingsbomen
De afwegingen laten zien wanneer één boom voldoende is en wanneer je moet overstappen op een ensemble.
Voordelen
- Volledig interpreteerbaar: Het aangepaste model is een diagram dat door alle belanghebbenden kan worden gelezen.
- Minimale voorbewerking: Schaling of normalisatie is niet nodig en factoren werken direct.
- Voert beide taken uit: `method = 'class'` past een classificatiemodel toe en `method = 'anova'` past een regressieboom toe.
- Snel te trainen: Grote datasets passen binnen enkele seconden, dus beslissingsbomen vormen een nuttig eerste uitgangspunt.
Nadelen
- Hoge variantie: Een kleine wijziging in de trainingsgegevens kan een compleet andere boom opleveren.
- Gevoelig voor overfitting: Een onbeperkte boom groeit totdat elk blad puur is, tenzij cp en maxdepth dit beperken.
- Alleen splitsingen parallel aan de as: Diagonale grenzen vereisen veel trapvormige sneden.
De oplossing voor de eerste twee zwakheden is het middelen van veel bomen, wat een willekeurig bos doet.
Hoe train en visualiseer je een beslissingsboom in R?
Om je eerste beslissingsboom in R te bouwen, doorloop je zeven stappen:
- Stap 1: Importeer de gegevens
- Stap 2: Maak de gegevensset schoon
- Stap 3: Maak een trein-/testset
- Stap 4: Bouw het model
- Stap 5: Maak een voorspelling
- Stap 6: Prestaties meten
- Stap 7: Stem de hyperparameters af
Stap 1) Importeer de gegevens
Als je nieuwsgierig bent naar het lot van de Titanic, kun je deze video bekijken YouTube. Het doel van deze dataset is om te voorspellen welke mensen een grotere kans hebben om te overleven na de botsing met de ijsberg. De dataset bevat 13 variabelen en 1309 observaties. De dataset wordt geordend op basis van de variabele X.
set.seed(678) path <- 'https://raw.githubusercontent.com/guru99-edu/R-Programming/master/titanic_data.csv' titanic <-read.csv(path) head(titanic)
Output:
## 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)
Output:
## 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
Aan de kop- en staartuitvoer kunt u zien dat de gegevens niet in willekeurige volgorde zijn weergegeven. Dit is een groot probleem! Wanneer u uw gegevens splitst tussen een treinset en een testset, selecteert u Slechts de passagier uit klasse 1 en 2 (geen enkele passagier uit klasse 3 bevindt zich in de top 80 procent van de waarnemingen), wat betekent dat het algoritme nooit de kenmerken van de passagier uit klasse 3 zal zien. Deze fout zal leiden tot slechte voorspellingen.
Om dit probleem op te lossen, kunt u de functie sample() gebruiken.
shuffle_index <- sample(1:nrow(titanic)) head(shuffle_index)
Beslisboom R-code Uitleg
- sample(1:nrow(titanic)): Genereer een willekeurige indexlijst van 1 tot 1309 (dwz het maximale aantal rijen).
Output:
## [1] 288 874 1078 633 887 992
U gebruikt deze index om de Titanic-dataset in willekeurige volgorde af te spelen.
titanic <- titanic[shuffle_index, ]
head(titanic)
Output:
## 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
Stap 2) Maak de dataset schoon
Verschillende variabelen bevatten NA-waarden. De opschoning bestaat uit drie delen:
- Verwijder de variabelen home.dest, cabin, name, X en ticket.
- Creëer factorvariabelen voor pclass en overleefde
- Laat de NA vallen
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 Uitleg
- select(-c(home.dest, hut, naam, X, ticket)): Verwijder onnodige variabelen
- pclass = factor(pclass, levels = c(1,2,3), labels= c('Upper', 'Middle', 'Lower')): Voeg een label toe aan de variabele pclass. 1 wordt Upper, 2 wordt Middle en 3 wordt Lower.
- factor(survived, levels = c(0,1), labels = c('No', 'Yes')): Voeg labels toe aan de variabele survivor. 0 wordt Nee en 1 wordt Ja
- na.omit(): Verwijder de NA-waarnemingen
Output:
## 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...
Stap 3) Maak een trein-/testset aan
Voordat u uw model traint, moet u twee stappen uitvoeren:
- Maak een trein en testset: Je traint het model op de treinset en test de voorspelling op de testset (dat wil zeggen ongeziene gegevens)
- Installeer rpart.plot vanaf de console
De gebruikelijke praktijk is om de gegevens 80/20 te splitsen; 80 procent van de gegevens dient om het model te trainen en 20 procent om voorspellingen te doen. U moet twee afzonderlijke gegevensframes maken. U wilt de testset pas aanraken als u klaar bent met het bouwen van uw model. U kunt een functienaam create_train_test() maken waaraan drie argumenten moeten doorgegeven worden.
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 Uitleg
- function(data, size=0.8, train = TRUE): Voeg de argumenten in de functie toe
- n_row = nrow(data): Tel het aantal rijen in de dataset
- total_row = size*n_row: Retourneer de zoveelste rij om de treinset samen te stellen
- train_sample <- 1:total_row: Selecteer de eerste rij tot en met de zoveelste rij
- if (train ==TRUE){ } else { }: Als de voorwaarde op true staat, retourneert u de treinset, anders de testset.
U kunt uw functie testen en de afmeting controleren.
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)
Output:
## [1] 836 8
dim(data_test)
Output:
## [1] 209 8
De trainingsdataset bevat 836 rijen en 8 kolommen, terwijl de testdataset 209 rijen en eveneens 8 kolommen bevat.
U gebruikt de functie prop.table() gecombineerd met table() om te verifiëren of het randomisatieproces correct is.
prop.table(table(data_train$survived))
Output:
## ## No Yes ## 0.5944976 0.4055024
prop.table(table(data_test$survived))
Output:
## ## No Yes ## 0.5789474 0.4210526
In beide datasets is het aantal overlevenden hetzelfde, ongeveer 40 procent.
Installeer rpart.plot
rpart.plot is niet beschikbaar in conda-bibliotheken. Je kunt het vanaf de console installeren:
install.packages("rpart.plot")
Stap 4) Bouw het model
Je bent klaar om het model te bouwen. De syntax voor de rpart() beslissingsboomfunctie is:
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
Je gebruikt de klassenmethode omdat je een klasse voorspelt.
library(rpart) library(rpart.plot) fit <- rpart(survived~., data = data_train, method = 'class') rpart.plot(fit, extra = 106)
Code Uitleg
- rpart(): Functie die in het model past. De argumenten zijn:
- overleefde ~.: Formule van de beslissingsbomen
- data = data_train: gegevensset
- method = 'class': Pas een binair model aan
- rpart.plot(fit, extra=106): Plot de boom. Het argument extra is ingesteld op 106, wat de waarschijnlijkheid van de tweede klasse plus het percentage waarnemingen in elk knooppunt weergeeft. U kunt de vignet voor meer informatie over de andere keuzes.
Output:
Je begint bij het wortelknooppunt, bovenaan de grafiek en op diepte 0 van 3:
- Bovenaan staat de algemene overlevingskans. Het laat zien hoeveel passagiers de crash hebben overleefd. 41 procent van de passagiers overleefde.
- Deze knoop vraagt of het geslacht van de passagier mannelijk is. Zo ja, dan ga je naar het linker kindknooppunt van de wortel (diepte 1). 63 procent is mannelijk met een overlevingskans van 21 procent.
- In het tweede knooppunt vraagt u of de mannelijke passagier ouder is dan 3.5 jaar. Zo ja, dan is de overlevingskans 19 procent.
- Je blijft zo doorgaan om te begrijpen welke kenmerken van invloed zijn op de overlevingskans.
Merk op dat een van de vele kwaliteiten van beslissingsbomen is dat ze heel weinig gegevensvoorbereiding vereisen. Ze vereisen met name geen schaal- of centrering van functies.
Standaard gebruikt de functie rpart() de Gini De Gini-coëfficiënt wordt gebruikt om elke splitsing te bepalen. Hoe hoger de Gini-waarde, hoe meer de klassen binnen dat knooppunt gemengd zijn. Het algoritme kiest daarom altijd de splitsing die de Gini-coëfficiënt het laagst maakt.
Stap 5) Doe een voorspelling
U kunt uw testgegevensset voorspellen. Om een voorspelling te doen, kunt u de functie voorspellen() gebruiken. De basissyntaxis van voorspellen voor de R-beslissingsboom is:
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
Je voorspelt nu voor elk van de 209 passagiers in de testset of het model verwacht dat ze de botsing zullen overleven.
predict_unseen <-predict(fit, data_test, type = 'class')
Code Uitleg
- voorspellen(fit, data_test, type = 'class'): Voorspel de klasse (0/1) van de testset
Vergelijk nu de voorspelde klassen met de werkelijke uitkomsten.
table_mat <- table(data_test$survived, predict_unseen)
table_mat
Code Uitleg
- tabel(data_test$survived, predict_unseen): Bouw een kruistabel van voorspelde klassen ten opzichte van de werkelijke uitkomst
Output:
## predict_unseen ## No Yes ## No 106 15 ## Yes 30 58
De rijen geven de werkelijke waarden weer, de kolommen de voorspellingen. Het model identificeerde correct 106 niet-overlevenden en 58 overlevenden, maar bestempelde 15 niet-overlevenden als overlevenden en 30 overlevenden als overleden.
Stap 6) Meet de prestaties
U kunt een nauwkeurigheidsmaatstaf voor de classificatietaak berekenen met de verwarring matrix:
De verwarring matrix is een betere keuze om de classificatieprestaties te evalueren. Het algemene idee is om het aantal keren te tellen dat True-instanties zijn geclassificeerd als False.
Elke rij in een verwarringsmatrix vertegenwoordigt een daadwerkelijk doelwit, terwijl elke kolom een voorspeld doelwit vertegenwoordigt. De eerste rij van deze matrix betreft de passagiers die zijn overleden (de negatieve klasse): 106 werden correct geclassificeerd als overleden (Echt negatief), terwijl 15 ten onrechte als overlevenden werden geclassificeerd (Vals positief). De tweede rij betreft de overlevenden: 58 werden correct geïdentificeerd (Echt positief), terwijl er 30 werden gemist (Fout negatief).
Je kunt de nauwkeurigheidstest uit de verwarringsmatrix:
Het is de verhouding tussen echt positief en echt negatief over de som van de matrix. Met R kun je als volgt coderen:
accuracy_Test <- sum(diag(table_mat)) / sum(table_mat)
Code Uitleg
- sum(diag(table_mat)): Som van de diagonaal
- sum(table_mat): Som van de matrix.
U kunt de nauwkeurigheid van de testset afdrukken:
print(paste('Accuracy for test', accuracy_Test))
Output:
## [1] "Accuracy for test 0.784688995215311"
De nauwkeurigheid op de testset is 0.7847, oftewel 78.47 procent. Herhaal de oefening op de trainingsset om te zien in hoeverre het model overfit.
Stap 7) Stem de hyperparameters af
Beslissingsboom in R heeft verschillende parameters die aspecten van de fit regelen. In de rpart decision tree-bibliotheek kunt u de parameters regelen met de rpart.control()-functie. In de volgende code introduceert u de parameters die u wilt afstemmen. U kunt verwijzen naar de vignet voor andere parameters.
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
Wij gaan als volgt te werk:
- Construeer een functie om de nauwkeurigheid terug te geven
- Stem de maximale diepte af
- Stem het minimale aantal monsters af dat een knooppunt moet hebben voordat het kan splitsen
- Stem het minimale aantal monsters af dat een leaf-knooppunt moet hebben
U kunt een functie schrijven om de nauwkeurigheid weer te geven. U verpakt eenvoudigweg de code die u eerder hebt gebruikt:
- voorspellen: voorspellen_unseen <- voorspellen (fit, data_test, type = 'klasse')
- Maak een tabel: table_mat <- table(data_test$survived, voorspellen_unseen)
- Bereken nauwkeurigheid: nauwkeurigheid_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 }
Stel nu de parameters in en kijk of je het standaardmodel kunt verbeteren. Ter herinnering: je moet een nauwkeurigheid van 0.7847 overtreffen.
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)
Output:
## [1] 0.7990431
Met de volgende parameter:
minsplit = 4
minbucket = round(5/3)
maxdepth = 3
cp = 0
De nauwkeurigheid stijgt van 0.7847 naar 0.7990, dus de geoptimaliseerde boom presteert ongeveer 1.4 procentpunt beter dan de standaardconfiguratie.
Beslissingsbomen in R: Snel overzicht van functies
De onderstaande tabel geeft een overzicht van alle functies die in de zeven bovenstaande stappen zijn gebruikt, samen met het pakket dat deze functie levert en de parameters die deze verwacht. R.
| Bibliotheek | Objectief | Functie | Klasse | Kenmerken | Details |
|---|---|---|---|---|---|
| deel | Treinclassificatieboom in R | rpart() | klasse | formule, df, methode | |
| deel | Train regressieboom | rpart() | anova | formule, df, methode | |
| deel | Teken de bomen | rpart.plot() | getailleerd model | ||
| baseren | voorspellen | voorspellen() | klasse | inbouwmodel, type | |
| baseren | voorspellen | voorspellen() | waarschijnlijk | inbouwmodel, type | |
| baseren | voorspellen | voorspellen() | vector | inbouwmodel, type | |
| deel | Controleparameters | rpart.control() | minsplit | Stel het minimumaantal waarnemingen in het knooppunt in voordat het algoritme een splitsing uitvoert | |
| minemmer | Stel het minimum aantal waarnemingen in een eindknooppunt in, oftewel het blad. | ||||
| maximale diepte | Stel de maximale diepte in voor elk knooppunt in de uiteindelijke boom. Het wortelknooppunt wordt beschouwd als een knooppunt met diepte 0. | ||||
| deel | Treinmodel met controleparameter | rpart() | formule, df, methode, controle |
Opmerking: Train het model op basis van trainingsgegevens en test de prestaties op een onzichtbare dataset, dat wil zeggen een testset.



