Problema 1: Historiales clínicos de insuficiencia cardíaca.

Paso 1 : Exploración y limpieza de datos

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

Paso 2 : Entrenamiento y predicción del modelo utilizando árboles de desiciones

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]]]

Paso 3 : Visualización del árbol de desición

tree <- rpart(DEATH_EVENT ~ ., data = entrenamiento)
rpart.plot(tree)

Paso 4 : Validación cruzada realizada múltiples veces

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      
## 

Paso 5: Comparación de métodos para árboles de desición

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.

Problema 2: Calidad del Vino

Paso 1 : Preparación inicial y limpieza de los datos:

colnames(df_completo) <- make.names(colnames(df_completo))
df_completo$quality = as.factor(df_completo$quality)

Paso 2 : Dividir los datos en conjunto de entrenamiento y prueba

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.

Paso 3 : Construcción del árbol de decisión

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.

Paso 4 : Validar la estabilidad del modelo CART

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.

Paso 5 : Validar la estabilidad del modelo Árbol C5.0

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.

Paso 6 : Comparar desempeño de CART vs. C5.0

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.