Introducción
Como se explicó anteriormente los árboles de decisión son un método usado en diversas disciplinas usado como un modelo de predicción; estos tienen ciertas similitudes con los diagramas de flujo, en donde en ciertos puntos se basan en la toma de decisiones mediante unas reglas en específico.
Para realizar un ejercicio de ejemplo se utilizara un conjunto de datos sobre la producción del vino en el Machine Learning Repository, fuente: * https://archive.ics.uci.edu/ml/datasets/Wine.
Producción de Vino
Paquetes Utilizados
library(pacman)
p_load("tidyverse", "rpart", "rpart.plot", "caret")
Importando los Datos
Se necesitan descargar dos archivos. El primero contiene todos los datos que seran utilizados y en el segundo contiene una descripción que mas adelante tendrá una utilidad importante.
# Datos
download.file("https://archive.ics.uci.edu/ml/machine-learning-databases/wine/wine.data", "wine.data")
# Información
download.file("https://archive.ics.uci.edu/ml/machine-learning-databases/wine/wine.names", "wine.names")
vino <- readLines("wine.data", n = 10)
Podemos utilizar la función read.table para leer el archivo, ya que es una función diseñada para leer tablas de datos con su propia estructura.
vino <- read.table("wine.data", sep = ",", header = FALSE)
Comprobamos si el archivo wine.names tiene respuestas.
readLines("wine.names", n = 10)
## [1] "1. Title of Database: Wine recognition data"
## [2] "\tUpdated Sept 21, 1998 by C.Blake : Added attribute information"
## [3] ""
## [4] "2. Sources:"
## [5] " (a) Forina, M. et al, PARVUS - An Extendible Package for Data"
## [6] " Exploration, Classification and Correlation. Institute of Pharmaceutical"
## [7] " and Food Analysis and Technologies, Via Brigata Salerno, "
## [8] " 16147 Genoa, Italy."
## [9] ""
## [10] " (b) Stefan Aeberhard, email: stefan@coral.cs.jcu.edu.au"
Se puede crear una copia de los archivos a manera de respaldo con la extensión .txt para convertirlo en un bloc de notas.
file.copy(from = "wine.names", to = "wine_names.txt")
## [1] FALSE
file.show("wine_names.txt")
summary(vino)
## V1 V2 V3 V4
## Min. :1.000 Min. :11.03 Min. :0.740 Min. :1.360
## 1st Qu.:1.000 1st Qu.:12.36 1st Qu.:1.603 1st Qu.:2.210
## Median :2.000 Median :13.05 Median :1.865 Median :2.360
## Mean :1.938 Mean :13.00 Mean :2.336 Mean :2.367
## 3rd Qu.:3.000 3rd Qu.:13.68 3rd Qu.:3.083 3rd Qu.:2.558
## Max. :3.000 Max. :14.83 Max. :5.800 Max. :3.230
## V5 V6 V7 V8
## Min. :10.60 Min. : 70.00 Min. :0.980 Min. :0.340
## 1st Qu.:17.20 1st Qu.: 88.00 1st Qu.:1.742 1st Qu.:1.205
## Median :19.50 Median : 98.00 Median :2.355 Median :2.135
## Mean :19.49 Mean : 99.74 Mean :2.295 Mean :2.029
## 3rd Qu.:21.50 3rd Qu.:107.00 3rd Qu.:2.800 3rd Qu.:2.875
## Max. :30.00 Max. :162.00 Max. :3.880 Max. :5.080
## V9 V10 V11 V12
## Min. :0.1300 Min. :0.410 Min. : 1.280 Min. :0.4800
## 1st Qu.:0.2700 1st Qu.:1.250 1st Qu.: 3.220 1st Qu.:0.7825
## Median :0.3400 Median :1.555 Median : 4.690 Median :0.9650
## Mean :0.3619 Mean :1.591 Mean : 5.058 Mean :0.9574
## 3rd Qu.:0.4375 3rd Qu.:1.950 3rd Qu.: 6.200 3rd Qu.:1.1200
## Max. :0.6600 Max. :3.580 Max. :13.000 Max. :1.7100
## V13 V14
## Min. :1.270 Min. : 278.0
## 1st Qu.:1.938 1st Qu.: 500.5
## Median :2.780 Median : 673.5
## Mean :2.612 Mean : 746.9
## 3rd Qu.:3.170 3rd Qu.: 985.0
## Max. :4.000 Max. :1680.0
nombres <-
readLines("wine_names.txt")[58:70] %>%
gsub("[[:cntrl:]].*\\)", "", .) %>%
trimws() %>%
tolower() %>%
gsub(" |/", "_", .) %>%
# Agregamos el nombre "tipo", para nuestra primera columna con los tipos de vino
c("tipo", .)
names(vino) <- nombres
vino <- vino %>%
mutate_at("tipo", factor)
Creando los sets de prueba
Se necesita un set de entrenamiento para poder generar un modelo predictivo al igual que un set de prueba, con el fin de hacer mas eficaz el modelo a la hora de hacer predicciones. Usamos la función sample_frac() de dplyr para obtener un subconjunto de nuestros datos, que consiste en 70% del total de ellos. Usamos también set.seed() para que este ejemplo sea reproducible.
set.seed(1649)
vino_entrenamiento <- sample_frac(vino, .7)
Con setdiff() de dplyr, obtenemos el subconjunto de datos complementario al de entrenamiento para nuestro set de prueba, esto es, el 30% restante.
vino_prueba <- setdiff(vino, vino_entrenamiento)
Utilizando el Modelo
Usamos la librería rpart para entrenar nuestro modelo. Esta función nos pide una formula para especificar la variable objetivo de la clasificación. La formula que usaremos es tipo ~ ., la cual expresa que intentaremos clasificar tipo usando a todas las demás variables como predictoras.
arbol_1 <- rpart(formula = tipo ~ ., data = vino_entrenamiento)
Evaluación y Analisis de los Datos
arbol_1
## n= 125
##
## node), split, n, loss, yval, (yprob)
## * denotes terminal node
##
## 1) root 125 75 2 (0.35200000 0.40000000 0.24800000)
## 2) proline>=755 48 6 1 (0.87500000 0.04166667 0.08333333)
## 4) flavanoids>=2.35 41 1 1 (0.97560976 0.02439024 0.00000000) *
## 5) flavanoids< 2.35 7 3 3 (0.28571429 0.14285714 0.57142857) *
## 3) proline< 755 77 29 2 (0.02597403 0.62337662 0.35064935)
## 6) flavanoids>=1.265 51 4 2 (0.03921569 0.92156863 0.03921569) *
## 7) flavanoids< 1.265 26 1 3 (0.00000000 0.03846154 0.96153846) *
rpart.plot(arbol_1)
Como podemos observar en el gráfico vemos una serie de rectángulos, estos representan los nodos del árbol con su regla de clasificación. Los colores de cada nodo dependen de la categoría maroyitaria de los datos que agrupa.
Dentro del rectángulo de cada nodo se nos muestra qué proporción de casos pertenecen a cada categoría y la proporción del total de datos que han sido agrupados allí. Por ejemplo, el rectángulo en el extremo inferior izquierdo de la gráfica tiene 98% de casos en el tipo 1, y 2% en los tipo 2 y 0% en el tipo 3, que representan 33% de todos los datos.
Usamos la función precict() con nuestro set de prueba para generar un vector con los valores predichos por el modelo que hemos entrenado, especificamos el parámetro type = “class”.
prediccion_1 <- predict(arbol_1, newdata = vino_prueba, type = "class")
Cruzamos la predicción con los datos reales de nuestro set de prueba para generar una matriz de confusión, usando confusionMatrix() de caret.
confusionMatrix(prediccion_1, vino_prueba[["tipo"]])
## Confusion Matrix and Statistics
##
## Reference
## Prediction 1 2 3
## 1 15 0 0
## 2 0 15 3
## 3 0 6 14
##
## Overall Statistics
##
## Accuracy : 0.8302
## 95% CI : (0.702, 0.9193)
## No Information Rate : 0.3962
## P-Value [Acc > NIR] : 1.106e-10
##
## Kappa : 0.7444
##
## Mcnemar's Test P-Value : NA
##
## Statistics by Class:
##
## Class: 1 Class: 2 Class: 3
## Sensitivity 1.000 0.7143 0.8235
## Specificity 1.000 0.9062 0.8333
## Pos Pred Value 1.000 0.8333 0.7000
## Neg Pred Value 1.000 0.8286 0.9091
## Prevalence 0.283 0.3962 0.3208
## Detection Rate 0.283 0.2830 0.2642
## Detection Prevalence 0.283 0.3396 0.3774
## Balanced Accuracy 1.000 0.8103 0.8284
Segundo Árbol
Ahora se realizará un segundo árbol con sets de entrenamineto y pruebas totalmente distitnas al primero.
set.seed(7439)
vino_entrenamiento_2 <- sample_frac(vino, .7)
vino_prueba_2 <- setdiff(vino, vino_entrenamiento)
arbol_2 <- rpart(formula = tipo ~ ., data = vino_entrenamiento_2)
prediccion_2 <- predict(arbol_2, newdata = vino_prueba_2, type = "class")
rpart.plot(arbol_2)
confusionMatrix(prediccion_2, vino_prueba_2[["tipo"]])
## Confusion Matrix and Statistics
##
## Reference
## Prediction 1 2 3
## 1 14 0 0
## 2 1 21 0
## 3 0 0 17
##
## Overall Statistics
##
## Accuracy : 0.9811
## 95% CI : (0.8993, 0.9995)
## No Information Rate : 0.3962
## P-Value [Acc > NIR] : < 2.2e-16
##
## Kappa : 0.9713
##
## Mcnemar's Test P-Value : NA
##
## Statistics by Class:
##
## Class: 1 Class: 2 Class: 3
## Sensitivity 0.9333 1.0000 1.0000
## Specificity 1.0000 0.9688 1.0000
## Pos Pred Value 1.0000 0.9545 1.0000
## Neg Pred Value 0.9744 1.0000 1.0000
## Prevalence 0.2830 0.3962 0.3208
## Detection Rate 0.2642 0.3962 0.3208
## Detection Prevalence 0.2642 0.4151 0.3208
## Balanced Accuracy 0.9667 0.9844 1.0000
Ahora los resultados han tenido una mejora notable a la hora de predecir en comparación al primero. Sin embargo, es importante notar que este segundo modelo es diferente con respecto al anterior en el orden que fueron hechas las particiones, pero es idéntico en cuanto a las variables usadas para separar grupos.
Podemos cambiar los datos usados en los sets de entrenamiento y prueba y obtener resultados distintos.
set.seed(8476)
vino_entrenamiento_3 <- sample_frac(vino, .7)
vino_prueba_3 <- setdiff(vino, vino_entrenamiento)
arbol_3 <- rpart(formula = tipo ~ ., data = vino_entrenamiento_3)
prediccion_3 <- predict(arbol_3, newdata = vino_prueba_3, type = "class")
rpart.plot(arbol_3)
confusionMatrix(prediccion_3, vino_prueba_3[["tipo"]])
## Confusion Matrix and Statistics
##
## Reference
## Prediction 1 2 3
## 1 15 4 0
## 2 0 17 0
## 3 0 0 17
##
## Overall Statistics
##
## Accuracy : 0.9245
## 95% CI : (0.8179, 0.9791)
## No Information Rate : 0.3962
## P-Value [Acc > NIR] : 8.174e-16
##
## Kappa : 0.8871
##
## Mcnemar's Test P-Value : NA
##
## Statistics by Class:
##
## Class: 1 Class: 2 Class: 3
## Sensitivity 1.0000 0.8095 1.0000
## Specificity 0.8947 1.0000 1.0000
## Pos Pred Value 0.7895 1.0000 1.0000
## Neg Pred Value 1.0000 0.8889 1.0000
## Prevalence 0.2830 0.3962 0.3208
## Detection Rate 0.2830 0.3208 0.3208
## Detection Prevalence 0.3585 0.3208 0.3208
## Balanced Accuracy 0.9474 0.9048 1.0000
Esta ocasión hemos obtenido una precisión en nuestras predicciones similar al primer modelo que generamos, pero ahora una de las variables usadas en la partición es diferente.
En todos estos ejemplos, lo único que hemos cambiado son nuestros sets de prueba y entrenamiento, ningún otro parámetro ha cambiado.
Conclusión
Para concluir tomando en cuenta la definición, ventajas y desventajas de los árboles de decisión podemos implementarlos gracias al paquete rpart que ofrece R y así este nos ayuda a crear una gráfica que nos ayude a realizar predicciones. Generalmente en ocasiones se necesitan realizar algunos sets de entrenamiento más para llegar a una buena precisión de los datos.