data <- read.csv("heart_failure_clinical_records_dataset.csv")

library(tidyverse)
library(caret)
library(kknn)
library(scales)
library(class)
library(dplyr)
library(forcats)

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

sum(is.na(data))
## [1] 0
data$DEATH_EVENT<- as.factor(data$DEATH_EVENT)
levels(data$DEATH_EVENT) <- c("sobrevivio", "fallecio")


table(data$DEATH_EVENT)
## 
## sobrevivio   fallecio 
##        203         96
round(prop.table(table(data$DEATH_EVENT)),2)
## 
## sobrevivio   fallecio 
##       0.68       0.32

Como las variables manejan magnitudes distintas, pasamos a normalizar los datos

# Normalización Min-Max con rescale
data_norm <- data %>% select(-DEATH_EVENT) %>%
  mutate(across(everything(), rescale))

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

Dividimos la muestra en dos conjuntos, uno para entrenamiento y otro para prueba.

set.seed(2025)
folds         <- createFolds(data$DEATH_EVENT, k = 6)
entrenamiento <- data_norm[-folds[[6]],]
prueba        <- data_norm[folds[[6]],]

guardamos las etiquetas del diagnóstico de todas las observaciones en dos vectores por separado.

# Etiquetas
entrenamiento_labels <- data$DEATH_EVENT[-folds[[6]]]
prueba_labels        <- data$DEATH_EVENT[folds[[6]]]

PASO 3: Aplicar el método K-NN

train.kknn(entrenamiento_labels ~ ., data = entrenamiento, kmax = 50)
## 
## Call:
## train.kknn(formula = entrenamiento_labels ~ ., data = entrenamiento,     kmax = 50)
## 
## Type of response variable: nominal
## Minimal misclassification: 0.2088353
## Best kernel: optimal
## Best k: 19

Encontramos que el valor optimo de k=19 Entonces la predicción, sería:

pred <- knn(entrenamiento,prueba, cl = entrenamiento_labels, k = 19)
confusionMatrix(data = pred, reference = prueba_labels)
## Confusion Matrix and Statistics
## 
##             Reference
## Prediction   sobrevivio fallecio
##   sobrevivio         31       15
##   fallecio            3        1
##                                           
##                Accuracy : 0.64            
##                  95% CI : (0.4919, 0.7708)
##     No Information Rate : 0.68            
##     P-Value [Acc > NIR] : 0.777970        
##                                           
##                   Kappa : -0.0321         
##                                           
##  Mcnemar's Test P-Value : 0.009522        
##                                           
##             Sensitivity : 0.9118          
##             Specificity : 0.0625          
##          Pos Pred Value : 0.6739          
##          Neg Pred Value : 0.2500          
##              Prevalence : 0.6800          
##          Detection Rate : 0.6200          
##    Detection Prevalence : 0.9200          
##       Balanced Accuracy : 0.4871          
##                                           
##        'Positive' Class : sobrevivio      
## 

Con este modelo hemos obtenido una exactitud del 68% Probemos ahora reescalando los valores con la función scale() , la cual convierte las variables en normales estándar, y quizás nos pueda ayudar a mejorar la clasificación.

data_z           <- as.data.frame(scale(data[,-13]))
entrenamiento_z <- data_z[-folds[[6]],]
prueba_z        <- data_z[folds[[6]],]

pred_z           <- knn(entrenamiento_z, prueba_z, cl = entrenamiento_labels, k = 19)
confusionMatrix(data = pred_z, reference = prueba_labels)
## Confusion Matrix and Statistics
## 
##             Reference
## Prediction   sobrevivio fallecio
##   sobrevivio         31       15
##   fallecio            3        1
##                                           
##                Accuracy : 0.64            
##                  95% CI : (0.4919, 0.7708)
##     No Information Rate : 0.68            
##     P-Value [Acc > NIR] : 0.777970        
##                                           
##                   Kappa : -0.0321         
##                                           
##  Mcnemar's Test P-Value : 0.009522        
##                                           
##             Sensitivity : 0.9118          
##             Specificity : 0.0625          
##          Pos Pred Value : 0.6739          
##          Neg Pred Value : 0.2500          
##              Prevalence : 0.6800          
##          Detection Rate : 0.6200          
##    Detection Prevalence : 0.9200          
##       Balanced Accuracy : 0.4871          
##                                           
##        'Positive' Class : sobrevivio      
## 

En este caso podemos ver que nuestra exactitud bajo a 64% por lo que no pudimos logarar mejorarla

PASO 4: Validar la estabilidad del modelo

Apliquemos validación cruzada para validar la estabilidad del modelo

# Guardar la exactitud de cada fold
exactitud <- numeric(length = 6)

for(i in 1:6){
  # Definir conjuntos entrenamiento y prueba según el fold actual
  prueba        <- data_norm[folds[[i]],]
  entrenamiento <- data_norm[-folds[[i]],]
  
  # Etiquetas
  entrenamiento_labels <- data$DEATH_EVENT[-folds[[i]]]
  prueba_labels        <- data$DEATH_EVENT[folds[[i]]]
  
  pred_knn <- knn(entrenamiento,prueba, cl= entrenamiento_labels, k = 19)
  
  # Evaluar exactitud del modelo en cada fold
  cm <- confusionMatrix(pred_knn, prueba_labels)
  exactitud[i] <- cm$overall["Accuracy"]
  
  # Mostrar resultado del fold
  cat("Fold", i, "- Exactitud:", exactitud[i], "\n")
}
## Fold 1 - Exactitud: 0.78 
## Fold 2 - Exactitud: 0.7142857 
## Fold 3 - Exactitud: 0.7 
## Fold 4 - Exactitud: 0.7 
## Fold 5 - Exactitud: 0.72 
## Fold 6 - Exactitud: 0.64
# Exactitud promedio de validación cruzada
Exactitud_promedio <- round(mean(exactitud),4)*100
 paste("Exactitud_promedio: ",Exactitud_promedio,"%",sep="")
## [1] "Exactitud_promedio: 70.9%"

La exactitud promedio es 70.9%

PASO 5: Interpretación de los resultados finales

set.seed(2025)
train_control <- trainControl(method="cv",number=10,savePredictions = TRUE)

knn_cv <- train(DEATH_EVENT ~ ., data=cbind(prueba, DEATH_EVENT=prueba_labels), 
                method = "knn", trControl = train_control, tuneGrid = data.frame(k=19))

# Resultados de validación cruzada
knn_cv
## k-Nearest Neighbors 
## 
## 50 samples
## 12 predictors
##  2 classes: 'sobrevivio', 'fallecio' 
## 
## No pre-processing
## Resampling: Cross-Validated (10 fold) 
## Summary of sample sizes: 45, 45, 44, 46, 45, 45, ... 
## Resampling results:
## 
##   Accuracy   Kappa
##   0.6816667  0    
## 
## Tuning parameter 'k' was held constant at a value of 19
# Matriz de confusión usando predicciones guardadas por caret
confusionMatrix(knn_cv$pred$pred, knn_cv$pred$obs)
## Confusion Matrix and Statistics
## 
##             Reference
## Prediction   sobrevivio fallecio
##   sobrevivio         34       16
##   fallecio            0        0
##                                          
##                Accuracy : 0.68           
##                  95% CI : (0.533, 0.8048)
##     No Information Rate : 0.68           
##     P-Value [Acc > NIR] : 0.5671960      
##                                          
##                   Kappa : 0              
##                                          
##  Mcnemar's Test P-Value : 0.0001768      
##                                          
##             Sensitivity : 1.00           
##             Specificity : 0.00           
##          Pos Pred Value : 0.68           
##          Neg Pred Value :  NaN           
##              Prevalence : 0.68           
##          Detection Rate : 0.68           
##    Detection Prevalence : 1.00           
##       Balanced Accuracy : 0.50           
##                                          
##        'Positive' Class : sobrevivio     
## 

Podemos concluir que la tasa de aciertos es aproximadamente 68.17%