datos <- read.csv("heart_failure_clinical_records_dataset.csv")
glimpse(datos)
## Rows: 299
## Columns: 13
## $ age <dbl> 75, 55, 65, 50, 65, 90, 75, 60, 65, 80, 75, 6…
## $ anaemia <int> 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, …
## $ creatinine_phosphokinase <int> 582, 7861, 146, 111, 160, 47, 246, 315, 157, …
## $ diabetes <int> 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, …
## $ ejection_fraction <int> 20, 38, 20, 20, 20, 40, 15, 60, 65, 35, 38, 2…
## $ high_blood_pressure <int> 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, …
## $ platelets <dbl> 265000, 263358, 162000, 210000, 327000, 20400…
## $ serum_creatinine <dbl> 1.90, 1.10, 1.30, 1.90, 2.70, 2.10, 1.20, 1.1…
## $ serum_sodium <int> 130, 136, 129, 137, 116, 132, 137, 131, 138, …
## $ sex <int> 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, …
## $ smoking <int> 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, …
## $ time <int> 4, 6, 7, 7, 8, 8, 10, 10, 10, 10, 10, 10, 11,…
## $ DEATH_EVENT <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, …
datos <- datos %>% mutate(across(c(anaemia, diabetes, high_blood_pressure, sex, smoking, DEATH_EVENT), as.factor))
datos <- datos %>% mutate(DEATH_EVENT = recode(DEATH_EVENT, "1" = "death", "0" = "alive"))
table(datos$DEATH_EVENT)
##
## alive death
## 203 96
set.seed(8)
folds <- createFolds(datos$DEATH_EVENT, k = 5)
entrenamiento <- datos[-folds[[5]],]
prueba <- datos[folds[[5]],]
entrenamiento_labels <- datos$DEATH_EVENT[-folds[[5]]]
prueba_labels <- datos$DEATH_EVENT[folds[[5]]]
tree <- rpart(DEATH_EVENT ~ ., data = entrenamiento)
rpart.plot(tree)
set.seed(777)
train_control <- trainControl(method="cv",number=13,savePredictions = TRUE)
arbol_cv <- train(DEATH_EVENT ~ ., data=cbind(prueba, tipo=prueba_labels),
method = "C5.0", trControl = train_control,
tuneLength = 13)
confusionMatrix(arbol_cv$pred$pred, arbol_cv$pred$obs)
## Confusion Matrix and Statistics
##
## Reference
## Prediction alive death
## alive 1760 0
## death 0 836
##
## Accuracy : 1
## 95% CI : (0.9986, 1)
## No Information Rate : 0.678
## P-Value [Acc > NIR] : < 2.2e-16
##
## Kappa : 1
##
## Mcnemar's Test P-Value : NA
##
## Sensitivity : 1.000
## Specificity : 1.000
## Pos Pred Value : 1.000
## Neg Pred Value : 1.000
## Prevalence : 0.678
## Detection Rate : 0.678
## Detection Prevalence : 0.678
## Balanced Accuracy : 1.000
##
## 'Positive' Class : alive
##
arbol_cart <- train(DEATH_EVENT ~ ., data=cbind(prueba, tipo=prueba_labels),
method = "rpart", trControl = train_control,
tuneLength = 13)
arbol_c50 <- train(DEATH_EVENT ~ ., data=cbind(prueba, tipo=prueba_labels),
method = "C5.0", trControl = train_control,
tuneLength = 13)
comparacion <- resamples(list(CART = arbol_cart, C5.0 = arbol_c50))
summary(comparacion)
##
## Call:
## summary.resamples(object = comparacion)
##
## Models: CART, C5.0
## Number of resamples: 13
##
## Accuracy
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## CART 1 1 1 1 1 1 0
## C5.0 1 1 1 1 1 1 0
##
## Kappa
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## CART 1 1 1 1 1 1 0
## C5.0 1 1 1 1 1 1 0
bwplot(comparacion, metric = "Accuracy")
bwplot(comparacion, metric = "Kappa")
A partir de la evidencia obtenida en este ejercicio, los modelos de clasificación parecen ofrecer un resultado perfecto, ya que, muestran una clasificación del 100% de los casos. Sin embargo, este resultado debe interpretarse con cuidado. En el árbol de decisiones se observa que, partiendo del 100% de las observaciones, aproximadamente el 32% corresponde a la categoría “alive” y el resto a “death” de la variable “DEATH_EVENT”. La primera división se realiza utilizando la variable “time”, donde valores mayores o iguales a 69 días se asocian con una mayor probabilidad de supervivencia, mientras que valores menores se relacionan con una mayor probabilidad de muerte. Luego, el árbol continúa separando los datos mediante variables como “serum_creatinine” y “ejection_fraction”, lo que sugiere que estos factores influyen significativamente en la clasificación de los pacientes. Aunque varias ramas del árbol clasifican repetidamente como “alive”, los porcentajes mostrados en cada nodo representan probabilidades dentro de grupos específicos, por lo que no implican certeza absoluta en las predicciones.
Por otra parte, la matriz de confusión indica una exactitud de 1, junto con una sensibilidad y especificidad también iguales a 1, lo que significa que el modelo clasificó correctamente el 100% de los casos analizados. Además, al comparar los modelos CART y C5.0, ambos presentan valores de exactitud y kappa iguales a 1 en los 13 remuestreos realizados, lo que sugiere que el modelo podría estar muy sobreajustado. En conclusión, aunque los resultados parecen muy buenos, es posible que no reflejen el desempeño real del modelo con datos nuevos, por lo que sería recomendable validar el modelo con otros métodos o revisar si alguna variable está influyendo de manera directa en el resultado.
colnames(df_completo) <- make.names(colnames(df_completo))
df_completo$quality = as.factor(df_completo$quality)
set.seed(2025)
folds_0 = createFolds(df_completo$quality, k=5)
entrenamiento_0 = df_completo[-folds_0[[5]],]
prueba_0 = df_completo[folds_0[[5]],]
entrenamiento_labels_0 = df_completo$quality[-folds_0[[5]]]
prueba_labels_0 = df_completo$quality[folds_0[[5]]]
prop.table(table(df_completo$quality))*100
##
## 3 4 5 6 7 8
## 0.46189376 3.32563510 32.90223249 43.66435720 16.59738260 2.97151655
## 9
## 0.07698229
apply(entrenamiento_0[,1:11], 2, max)
## fixed.acidity volatile.acidity citric.acid
## 15.9000 1.5800 1.6600
## residual.sugar chlorides free.sulfur.dioxide
## 31.6000 0.6110 289.0000
## total.sulfur.dioxide density pH
## 440.0000 1.0103 4.0100
## sulphates alcohol
## 1.9800 14.9000
apply(entrenamiento_0[,1:11], 2, min)
## fixed.acidity volatile.acidity citric.acid
## 3.80000 0.08000 0.00000
## residual.sugar chlorides free.sulfur.dioxide
## 0.60000 0.00900 1.00000
## total.sulfur.dioxide density pH
## 6.00000 0.98711 2.74000
## sulphates alcohol
## 0.22000 8.00000
Dado que los modelos de árboles de decisión son sensibles al desbalance de clases en los datos de entrenamiento, existe el riesgo de que el modelo desarrolle un sesgo hacia la clase mayoritaria, clasificando con mayor frecuencia las observaciones como pertenecientes a dicha clase. Esto genera una tasa elevada de falsos negativos en las clases minoritarias, es decir, el modelo tiende a no detectar correctamente vinos de calidades poco frecuentes como 3, 4, 8 y 9. La clase mayoritaria corresponde a los vinos de calidad 6, representando aproximadamente el 43.66% de los datos de entrenamiento. Adicionalmente, las predicciones del modelo se realizarán sobre observaciones cuyos valores fisicoquímicos se encuentren dentro del rango observado en cada variable durante el entrenamiento, por lo que predicciones fuera de ese rango podrían no ser confiables.
modCart= rpart(quality ~ ., data = entrenamiento_0)
rpart.plot(modCart)
Las variables más importantes identificadas por el modelo CART son alcohol y volatile.acidity, siendo los únicos criterios de división utilizados. Sin embargo, el modelo presenta una limitación crítica: debido al desbalance de clases, el árbol únicamente predice vinos de calidad 5 y 6, dejando las clases 3, 4, 7, 8 y 9 completamente sin clasificar.
arbol_cart_train <- train(quality ~ .,
data = cbind(entrenamiento_0, quality = entrenamiento_labels_0),
method = "rpart",
trControl = train_control_0,
tuneLength = 10)
confusionMatrix(arbol_cart_train$pred$pred, arbol_cart_train$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 0 2 0 0 0 0
## 5 8 90 1011 523 48 2 0
## 6 16 80 677 1647 642 109 2
## 7 0 3 20 98 170 43 2
## 8 0 0 0 1 2 0 0
## 9 0 0 0 0 0 0 0
##
## Overall Statistics
##
## Accuracy : 0.5443
## 95% CI : (0.5306, 0.5579)
## No Information Rate : 0.4367
## P-Value [Acc > NIR] : < 2.2e-16
##
## Kappa : 0.2602
##
## 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.0000000 0.5912 0.7259 0.19722 0.0000000
## Specificity 1.000000 0.9996018 0.8075 0.4786 0.96170 0.9994050
## Pos Pred Value NaN 0.0000000 0.6011 0.5191 0.50595 0.0000000
## Neg Pred Value 0.995381 0.9666923 0.8011 0.6925 0.85761 0.9703447
## Prevalence 0.004619 0.0332948 0.3291 0.4367 0.16590 0.0296382
## Detection Rate 0.000000 0.0000000 0.1946 0.3170 0.03272 0.0000000
## Detection Prevalence 0.000000 0.0003849 0.3237 0.6107 0.06467 0.0005774
## Balanced Accuracy 0.500000 0.4998009 0.6994 0.6023 0.57946 0.4997025
## 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
arbol_cart_test <- train(quality ~ .,
data = cbind(prueba_0, quality = prueba_labels_0),
method = "rpart",
trControl = train_control_0,
tuneLength = 10)
confusionMatrix(arbol_cart_test$pred$pred, arbol_cart_test$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 0 0 0 0 0 0
## 5 3 25 236 129 8 3 0
## 6 3 16 180 385 121 22 1
## 7 0 2 11 53 87 14 0
## 8 0 0 0 0 0 0 0
## 9 0 0 0 0 0 0 0
##
## Overall Statistics
##
## Accuracy : 0.545
## 95% CI : (0.5175, 0.5724)
## No Information Rate : 0.4365
## P-Value [Acc > NIR] : 2.656e-15
##
## Kappa : 0.2799
##
## 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.0000 0.5527 0.6790 0.40278 0.00000
## Specificity 1.000000 1.0000 0.8073 0.5314 0.92613 1.00000
## Pos Pred Value NaN NaN 0.5842 0.5288 0.52096 NaN
## Neg Pred Value 0.995381 0.9669 0.7866 0.6813 0.88604 0.96998
## Prevalence 0.004619 0.0331 0.3287 0.4365 0.16628 0.03002
## Detection Rate 0.000000 0.0000 0.1817 0.2964 0.06697 0.00000
## Detection Prevalence 0.000000 0.0000 0.3110 0.5604 0.12856 0.00000
## Balanced Accuracy 0.500000 0.5000 0.6800 0.6052 0.66445 0.50000
## 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
En comparación con la exactitud obtenida en los datos de entrenamiento (52.98%), los datos de prueba resultaron en una exactitud marginalmente menor (52.79%), una diferencia no significativa que sugiere que el modelo generaliza de manera estable. Sin embargo, el índice Kappa fue mayor en los datos de prueba (0.2423 > 0.2271). Respecto a la sensibilidad por clase, las clases 5 y 6 presentaron los valores más altos debido a que son las clases mayoritarias, mientras que las clases minoritarias (3, 4, 7, 8, 9) obtuvieron una sensibilidad de 0, confirmando que el modelo es incapaz de clasificarlas correctamente debido al desbalance de clases.
arbol_cv_train <- train(quality ~ ., data=cbind(entrenamiento_0, tipo=entrenamiento_labels_0),
method = "C5.0", trControl = train_control_0,
tuneLength = 10)
confusionMatrix(arbol_cv_train$pred$pred, arbol_cv_train$pred$obs)
## Confusion Matrix and Statistics
##
## Reference
## Prediction 3 4 5 6 7 8 9
## 3 24 0 0 0 0 0 0
## 4 0 173 0 0 0 0 0
## 5 0 0 1710 0 0 0 0
## 6 0 0 0 2269 0 0 0
## 7 0 0 0 0 862 0 0
## 8 0 0 0 0 0 154 0
## 9 0 0 0 0 0 0 4
##
## Overall Statistics
##
## Accuracy : 1
## 95% CI : (0.9993, 1)
## No Information Rate : 0.4367
## P-Value [Acc > NIR] : < 2.2e-16
##
## Kappa : 1
##
## Mcnemar's Test P-Value : NA
##
## Statistics by Class:
##
## Class: 3 Class: 4 Class: 5 Class: 6 Class: 7 Class: 8
## Sensitivity 1.000000 1.00000 1.0000 1.0000 1.0000 1.00000
## Specificity 1.000000 1.00000 1.0000 1.0000 1.0000 1.00000
## Pos Pred Value 1.000000 1.00000 1.0000 1.0000 1.0000 1.00000
## Neg Pred Value 1.000000 1.00000 1.0000 1.0000 1.0000 1.00000
## Prevalence 0.004619 0.03329 0.3291 0.4367 0.1659 0.02964
## Detection Rate 0.004619 0.03329 0.3291 0.4367 0.1659 0.02964
## Detection Prevalence 0.004619 0.03329 0.3291 0.4367 0.1659 0.02964
## Balanced Accuracy 1.000000 1.00000 1.0000 1.0000 1.0000 1.00000
## Class: 9
## Sensitivity 1.0000000
## Specificity 1.0000000
## Pos Pred Value 1.0000000
## Neg Pred Value 1.0000000
## Prevalence 0.0007698
## Detection Rate 0.0007698
## Detection Prevalence 0.0007698
## Balanced Accuracy 1.0000000
arbol_cv_test <- train(quality ~ ., data=cbind(prueba_0, tipo=prueba_labels_0),
method = "C5.0", trControl = train_control_0,
tuneLength = 10)
confusionMatrix(arbol_cv_test$pred$pred, arbol_cv_test$pred$obs)
## Confusion Matrix and Statistics
##
## Reference
## Prediction 3 4 5 6 7 8 9
## 3 6 0 0 0 0 0 1
## 4 0 43 0 0 0 0 0
## 5 0 0 427 0 0 0 0
## 6 0 0 0 567 0 0 0
## 7 0 0 0 0 216 0 0
## 8 0 0 0 0 0 39 0
## 9 0 0 0 0 0 0 0
##
## Overall Statistics
##
## Accuracy : 0.9992
## 95% CI : (0.9957, 1)
## No Information Rate : 0.4365
## P-Value [Acc > NIR] : < 2.2e-16
##
## Kappa : 0.9989
##
## Mcnemar's Test P-Value : NA
##
## Statistics by Class:
##
## Class: 3 Class: 4 Class: 5 Class: 6 Class: 7 Class: 8
## Sensitivity 1.000000 1.0000 1.0000 1.0000 1.0000 1.00000
## Specificity 0.999227 1.0000 1.0000 1.0000 1.0000 1.00000
## Pos Pred Value 0.857143 1.0000 1.0000 1.0000 1.0000 1.00000
## Neg Pred Value 1.000000 1.0000 1.0000 1.0000 1.0000 1.00000
## Prevalence 0.004619 0.0331 0.3287 0.4365 0.1663 0.03002
## Detection Rate 0.004619 0.0331 0.3287 0.4365 0.1663 0.03002
## Detection Prevalence 0.005389 0.0331 0.3287 0.4365 0.1663 0.03002
## Balanced Accuracy 0.999613 1.0000 1.0000 1.0000 1.0000 1.00000
## 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
Utilizando el método C5.0, la tasa de aciertos del modelo no disminuye de manera circunstancial para los datos de prueba (1 > .9992), lo que indica que el modelo generaliza bien a datos no vistos. También hubo una disminución del índice de Kappa en los datos de prueba (1 > .9989); esta caída es prácticamente despreciable, por lo que el poder predictivo del modelo aún es alto en comparación con la predicción al azar. La sensibilidad por clase demuestra que, en los datos de prueba, los vinos con el nivel más alto de calidad (9) son incapaces de ser predichos por el modelo. Esto pudiera deberse al desbalance entre las clases, provocando que las predicciones estén sesgadas hacia las clases mayoritarias.
comparacion <- resamples(list(CART = arbol_cart_test, C5.0 = arbol_cv_test))
summary(comparacion)
##
## 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.4728682 0.5193798 0.5440986 0.5450258 0.5845405 0.610687 0
## C5.0 0.9923077 1.0000000 1.0000000 0.9992308 1.0000000 1.000000 0
##
## Kappa
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## CART 0.1571868 0.2392071 0.2784566 0.2799135 0.3341702 0.3853726 0
## C5.0 0.9886294 1.0000000 1.0000000 0.9988629 1.0000000 1.0000000 0
Ambas métricas de exactitud y el índice de Kappa fueron consistentemente superiores para el modelo C5.0, lo que implica que, para la misma data, la clasificación es más precisa en este modelo. Sin embargo, a pesar del alto rendimiento global, ambos modelos presentan dificultades para clasificar la clase de menor prevalencia — aquellos vinos con un nivel de calidad 9 — según lo indicado por la sensibilidad por clase. Por estas razones, el modelo C5.0 fue seleccionado como el más adecuado; no obstante, debe tomarse en consideración que este modelo no permite la visualización directa del árbol de decisión generado, lo que limita su interpretabilidad en comparación con CART.