Para este ejercicio utilizamos la misma base de datos utilizada en el ejercici anterior de KNN, luego de descargar la base de datos factorizamos las variables categoricas utilizando as.factor.
## Rows: 299 Columns: 13
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## dbl (13): age, anaemia, creatinine_phosphokinase, diabetes, ejection_fractio...
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
En este paso vamos a dividir en dos subconjutnos de entrenamiento y prueba. Creamoslas 5 grupos balanceados “K = 5”.Luego, tomé “fold” 5 como conjunto de prueba y 5 folds como conjunto de entrenamiento. Luego los guardamos por separado porque esto nos va a ayudar a cosntruir y evaluar el modelo. Y último, vamos a utilizar “dim” para verificar las observaciones que tenemos ahora.
folds <- createFolds(heart$DEATH_EVENT, k = 5)
entrenamiento <- heart[-folds[[5]],]
prueba <- heart[folds[[5]],]
dim(entrenamiento)[1]## [1] 239
## [1] 60
## n= 239
##
## node), split, n, loss, yval, (yprob)
## * denotes terminal node
##
## 1) root 239 77 muerto (0.32217573 0.67782427)
## 2) time< 73.5 60 12 vivo (0.80000000 0.20000000)
## 4) serum_sodium< 136.5 31 2 vivo (0.93548387 0.06451613) *
## 5) serum_sodium>=136.5 29 10 vivo (0.65517241 0.34482759)
## 10) time< 48.5 19 3 vivo (0.84210526 0.15789474) *
## 11) time>=48.5 10 3 muerto (0.30000000 0.70000000) *
## 3) time>=73.5 179 29 muerto (0.16201117 0.83798883)
## 6) serum_creatinine>=1.45 36 16 muerto (0.44444444 0.55555556)
## 12) platelets< 12.37158 17 6 vivo (0.64705882 0.35294118) *
## 13) platelets>=12.37158 19 5 muerto (0.26315789 0.73684211) *
## 7) serum_creatinine< 1.45 143 13 muerto (0.09090909 0.90909091) *
##
## Call:
## C5.0.formula(formula = DEATH_EVENT ~ ., data = entrenamiento)
##
## Classification Tree
## Number of samples: 239
## Number of predictors: 12
##
## Tree size: 19
##
## Non-standard options: attempt to group attributes
train_control <- trainControl(method = "cv", number = 10, savePredictions = TRUE)
arbol_cv <- train(DEATH_EVENT ~ .,
data = prueba,
method = "C5.0",
trControl = train_control,
tuneLength = 3)
confusionMatrix(arbol_cv$pred$pred, arbol_cv$pred$obs)## Confusion Matrix and Statistics
##
## Reference
## Prediction vivo muerto
## vivo 176 40
## muerto 52 452
##
## Accuracy : 0.8722
## 95% CI : (0.8456, 0.8957)
## No Information Rate : 0.6833
## P-Value [Acc > NIR] : <2e-16
##
## Kappa : 0.7005
##
## Mcnemar's Test P-Value : 0.2515
##
## Sensitivity : 0.7719
## Specificity : 0.9187
## Pos Pred Value : 0.8148
## Neg Pred Value : 0.8968
## Prevalence : 0.3167
## Detection Rate : 0.2444
## Detection Prevalence : 0.3000
## Balanced Accuracy : 0.8453
##
## 'Positive' Class : vivo
##
Observaciones
Al observar el gráfico del árbol CART, se ve que el nodo raíz clasifica inicialmente la mayoría de los casos como muerto, con una proporción de 0.68, y después las ramas van refinando esa clasificación. Por ejemplo, cuando time < 74, aparecen subgrupos donde predominan pacientes vivos, mientras que en la rama de time ≥ 74 predominan los casos muertos, especialmente cuando serum_creatinine ≥ 1.5. Esto muestra que el modelo está encontrando reglas clínicas sencillas e interpretables para separar los pacientes según su desenlace.
Por otro lado, el modelo C5.0 también se ajustó con los mismos datos, pero creó un árbol más grande, con más divisiones. Eso sugiere que C5.0 busca una clasificación más detallada, mientras que CART produce un árbol más simple y más fácil de interpretar visualmente. En general, ambos modelos sirven para clasificar la variable DEATH_EVENT, pero CART se entiende más fácil al mirar el árbol.
arbol_cart <- train(DEATH_EVENT ~ .,
data = prueba,
method = "rpart",
trControl = train_control,
tuneLength = 3)
arbol_c50 <- train(DEATH_EVENT ~ .,
data = prueba,
method = "C5.0",
trControl = train_control,
tuneLength = 3)
comparacion <- resamples(list(CART = arbol_cart, C50 = arbol_c50))
summary(comparacion)##
## Call:
## summary.resamples(object = comparacion)
##
## Models: CART, C50
## Number of resamples: 10
##
## Accuracy
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## CART 0.6666667 0.8750000 1 0.9214286 1 1 0
## C50 0.6666667 0.8333333 1 0.9166667 1 1 0
##
## Kappa
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## CART 0.00 0.6785714 1 0.7821429 1 1 0
## C50 0.25 0.5952381 1 0.8059524 1 1 0
En conclusión, los árboles de decisión lograron un buen desempeño en esta base de datos para predecir DEATH_EVENT. Cuando vemos la matriz de confusión observamos una exactitud de 87.22%, lo que indica que el modelo clasificó correctamente la mayoría de los casos. Además, el valor de Kappa = 0.7005 muestra un nivel de concordancia bueno entre lo predicho y lo observado.
También se puede notar que el modelo identificó bastante bien ambas clases, con una sensibilidad de 0.7719 y una especificidad de 0.9187. Así que, se puede decir que el modelo de árbol de decisión resulta útil para este problema y que las variables clínicas incluidas sí contienen información importante para clasificar si el paciente sobrevive o fallece.
Para este ejercicio estaremos utilizando la misma base de datos utilizada en el ejercicio del método KNN, viendo las diferentes variables que miden la calidad del vino blanco y vino tinto. Utilizando los arboles de decisión, queremos que nos ayude a seleccionar predictores de forma automática y visualizar mejor las relaciones entre predictores.
Al igual que el ejercicio del método KNN preparams nuestra base de datos, dándole nombre especfico a las variables “ID” y luego uniendo ambas bases de datos. A su vez, terminamos asignándole a R que “quality” y “tipo” son variables categóricas usando la función de “as.factor”.
En este paso vamos a dividir en dos subconjutnos de entrenamiento y prueba. Creamoslas 5 grupos balanceados “K = 5”.Luego, tomé “fold” 5 como conjunto de prueba y 5 folds como conjunto de entrenamiento. Luego los guardamos por separado porque esto nos va a ayudar a cosntruir y evaluar el modelo. Y último, vamos a utilizar “dim” para verificar las observaciones que tenemos ahora.
set.seed(2025)
folds <- createFolds(winequality$quality, k = 5)
entrenamiento <- winequality_norm[-folds[[5]], ]
prueba <- winequality_norm[folds[[5]], ]
entrenamiento_labels <- winequality$quality[-folds[[5]]]
prueba_labels <- winequality$quality[folds[[5]]]
dim(entrenamiento)[1]## [1] 5198
## [1] 1299
En este paso estaremos construyendo un modelo ajustado utilizando como variable de respuestar “entrenamiento_labels”, que esta variable contiene las diferentes categorías de calidad del vino. Luego de haber ajustado el modelo, vamos a poder general un arbol binario para observar como el modelo esta seprando las observaciones
library(rpart)
library(rpart.plot)
arbol_1 <- rpart(entrenamiento_labels ~ ., data = (entrenamiento))
arbol_1## n= 5198
##
## node), split, n, loss, yval, (yprob)
## * denotes terminal node
##
## 1) root 5198 2929 6 (0.0046 0.033 0.33 0.44 0.17 0.03 0.00077)
## 2) alcohol< 0.3804348 3073 1627 5 (0.0046 0.04 0.47 0.41 0.066 0.0098 0.00033)
## 4) volatile.acidity>=0.105 2243 995 5 (0.0053 0.048 0.56 0.35 0.035 0.0027 0.00045)
## 8) alcohol< 0.2681159 1486 551 5 (0.004 0.046 0.63 0.3 0.017 0.0013 0) *
## 9) alcohol>=0.2681159 757 418 6 (0.0079 0.053 0.41 0.45 0.071 0.0053 0.0013)
## 18) volatile.acidity>=0.145 499 264 5 (0.008 0.064 0.47 0.4 0.054 0.002 0) *
## 19) volatile.acidity< 0.145 258 119 6 (0.0078 0.031 0.3 0.54 0.1 0.012 0.0039) *
## 5) volatile.acidity< 0.105 830 363 6 (0.0024 0.018 0.24 0.56 0.15 0.029 0) *
## 3) alcohol>=0.3804348 2125 1112 6 (0.0047 0.024 0.12 0.48 0.31 0.058 0.0014) *
## Warning: Bad 'data' argument in model call (expected a data.frame or a matrix).
## To silence this warning:
## Call rpart.plot with roundint=FALSE,
## or rebuild the rpart model with model=TRUE.
Observaciones
Al ver el gráfico, la función del arbol decidió clasificar principalmente los vinos entre 5 y 6, usando como variables principales “alcohol” y “volatile.acidity”. Los nodos azules corresponden a la clase 5 y los verdes a la clase 6. Por el otro lado, También se observa que las clases 3, 4, 7, 8 y 9 salen como “unused”, lo cual indica que el árbol no terminó creando nodos finales dominados por esas clases.
library(caret)
set.seed(2025)
train_control <- trainControl(method = "cv", number = 10, savePredictions = TRUE)
arbol_cv <- train(quality ~ ., data = cbind(prueba, quality = prueba_labels),
method = "rpart", trControl = train_control,
tuneLength = 10)
confusionMatrix(arbol_cv$pred$pred, arbol_cv$pred$obs)## Confusion Matrix and Statistics
##
## Reference
## Prediction 3 4 5 6 7 8 9
## 3 0 0 0 0 0 0 0
## 4 0 1 16 3 0 0 0
## 5 34 216 2198 1354 135 1 0
## 6 26 210 2018 3902 1719 271 10
## 7 0 3 38 406 301 118 0
## 8 0 0 0 5 5 0 0
## 9 0 0 0 0 0 0 0
##
## Overall Statistics
##
## Accuracy : 0.4928
## 95% CI : (0.4842, 0.5015)
## No Information Rate : 0.4365
## P-Value [Acc > NIR] : < 2.2e-16
##
## Kappa : 0.1755
##
## Mcnemar's Test P-Value : NA
##
## Statistics by Class:
##
## Class: 3 Class: 4 Class: 5 Class: 6 Class: 7 Class: 8
## Sensitivity 0.000000 2.326e-03 0.5148 0.6882 0.13935 0.0000000
## Specificity 1.000000 9.985e-01 0.8005 0.4189 0.94783 0.9992063
## Pos Pred Value NaN 5.000e-02 0.5582 0.4784 0.34758 0.0000000
## Neg Pred Value 0.995381 9.669e-01 0.7711 0.6343 0.84667 0.9699538
## Prevalence 0.004619 3.310e-02 0.3287 0.4365 0.16628 0.0300231
## Detection Rate 0.000000 7.698e-05 0.1692 0.3004 0.02317 0.0000000
## Detection Prevalence 0.000000 1.540e-03 0.3032 0.6279 0.06667 0.0007698
## Balanced Accuracy 0.500000 5.004e-01 0.6576 0.5535 0.54359 0.4996032
## Class: 9
## Sensitivity 0.0000000
## Specificity 1.0000000
## Pos Pred Value NaN
## Neg Pred Value 0.9992302
## Prevalence 0.0007698
## Detection Rate 0.0000000
## Detection Prevalence 0.0000000
## Balanced Accuracy 0.5000000
observación
Estos resultados muestran un modelo con Accuracy = 0.4928 y Kappa = 0.1755. Eso significa que el modelo apenas supera el nivel base y que el acuerdo real entre predicción y valores observados es bajo. Por el otro lado, este modelo casi no logra identificar bien las clases menos frecuentes, como 3, 8 y 9, porque su sensibilidad en esas categorías es prácticamente 0. Este modelo se esta concentrando más en las clases más comunes que son 5 y 6.
# Utilizando el metodo de C5.0
library(caret)
set.seed(2025)
train_control <- trainControl(method = "cv", number = 10, savePredictions = TRUE)
arbol_c50 <- train(quality ~ ., data = cbind(prueba, quality = prueba_labels),
method = "C5.0", trControl = train_control,
tuneLength = 10)
confusionMatrix(arbol_c50$pred$pred, arbol_c50$pred$obs)## Confusion Matrix and Statistics
##
## Reference
## Prediction 3 4 5 6 7 8 9
## 3 0 2 87 5 0 0 0
## 4 0 219 291 207 2 2 0
## 5 197 917 10960 6126 708 84 0
## 6 43 515 5090 12146 3847 598 2
## 7 0 63 634 3909 3762 666 18
## 8 0 4 18 285 321 208 20
## 9 0 0 0 2 0 2 0
##
## Overall Statistics
##
## Accuracy : 0.5253
## 95% CI : (0.521, 0.5296)
## No Information Rate : 0.4365
## P-Value [Acc > NIR] : < 2.2e-16
##
## Kappa : 0.2841
##
## Mcnemar's Test P-Value : NA
##
## Statistics by Class:
##
## Class: 3 Class: 4 Class: 5 Class: 6 Class: 7 Class: 8
## Sensitivity 0.000000 0.127326 0.6417 0.5355 0.4354 0.133333
## Specificity 0.998183 0.990008 0.7697 0.6552 0.8779 0.987143
## Pos Pred Value 0.000000 0.303745 0.5771 0.5461 0.4156 0.242991
## Neg Pred Value 0.995373 0.970706 0.8144 0.6455 0.8863 0.973544
## Prevalence 0.004619 0.033102 0.3287 0.4365 0.1663 0.030023
## Detection Rate 0.000000 0.004215 0.2109 0.2338 0.0724 0.004003
## Detection Prevalence 0.001809 0.013876 0.3655 0.4280 0.1742 0.016474
## Balanced Accuracy 0.499091 0.558667 0.7057 0.5954 0.6567 0.560238
## Class: 9
## Sensitivity 0.000e+00
## Specificity 9.999e-01
## Pos Pred Value 0.000e+00
## Neg Pred Value 9.992e-01
## Prevalence 7.698e-04
## Detection Rate 0.000e+00
## Detection Prevalence 7.698e-05
## Balanced Accuracy 5.000e-01
Observación
Al comparar ambos modelos, el segundo presentó mejor desempeño que el primero, ya que obtuvo una exactitud de 52.53% frente a 49.28%, y un Kappa de 0.2841 frente a 0.1755. La exactitud sube un poco y el valor de Kappa también mejora, lo que indica una clasificación más consistente que la del primer modelo. El segundo modelo logra una clasificación más consistente y con mejor acuerdo respecto a los valores reales. Por esta razón, C5.0 se considera el modelo de árbol de decisión más adecuado para esta base de datos.
##
## Call:
## summary.resamples(object = comparacion)
##
## Models: CART, C5.0
## Number of resamples: 10
##
## Accuracy
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## CART 0.4651163 0.4809160 0.5039062 0.5019504 0.5246804 0.5384615 0
## C5.0 0.4846154 0.5133588 0.5325602 0.5383752 0.5678295 0.5937500 0
##
## Kappa
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## CART 0.1140639 0.1394339 0.1787954 0.1792001 0.2039295 0.2505045 0
## C5.0 0.2554919 0.2783630 0.3033057 0.3153326 0.3572629 0.3955685 0
Observación y conclusión:
En la comparación realizada, el modelo C5.0 presentó un mejor desempeño que el modelo CART. En la métrica de accuracy, C5.0 obtuvo un promedio de **0.5384*, mientras que CART alcanzó 0.5020, lo que indica que C5.0 clasificó correctamente una mayor proporción de observaciones. De igual forma, la mediana de accuracy también fue mayor en C5.0, por lo que su rendimiento fue superior en la mayoría de los folds.
Cuando miramos la métrica de KAPPA, el modelo C5.0 también mostró mejores resultados. Su promedio fue 0.3153, en comparación con 0.1792 obtenido por CART. Esto significa que C5.0 no solo tuvo más aciertos, sino que además presentó una mejor concordancia general en la clasificación.