Analisis de Dataset Diabetes

Author

Jaime Llanos Bardales

Predecir la aparición de diabetes basándose en medidas diagnósticas.

Este conjunto de datos procede originalmente del Instituto Nacional de Diabetes y Enfermedades Digestivas y Renales. El objetivo del conjunto de datos es predecir de forma diagnóstica si un paciente tiene diabetes o no, basándose en determinadas mediciones diagnósticas incluidas en el conjunto de datos. Se impusieron varias restricciones a la selección de estos casos de una base de datos más amplia. En concreto, todos los pacientes aquí incluidos son mujeres de al menos 21 años de edad y ascendencia indígena pima.

El conjunto de datos consta de varias variables predictivas médicas y una variable objetivo, el resultado. Las variables predictivas incluyen el número de embarazos que ha tenido la paciente, su IMC, su nivel de insulina, su edad, etc.

Paso 0. Instalar y cargar librerías necesarias

install.packages(c(“readxl”,“dplyr”,“ggplot2”,“caret”,“e1071”,“randomForest”))

library(readxl)
library(dplyr)

Adjuntando el paquete: 'dplyr'
The following objects are masked from 'package:stats':

    filter, lag
The following objects are masked from 'package:base':

    intersect, setdiff, setequal, union
library(ggplot2)
library(caret)
Cargando paquete requerido: lattice
library(e1071) # Para SVM
library(randomForest)
randomForest 4.7-1.2
Type rfNews() to see new features/changes/bug fixes.

Adjuntando el paquete: 'randomForest'
The following object is masked from 'package:ggplot2':

    margin
The following object is masked from 'package:dplyr':

    combine

Paso 1. Cargar dataset

setwd("~/RStudio")
df <- read_excel("diabetes.xlsx")

Revisar estructura

cat("\nPrimeras filas del dataset:\n")

Primeras filas del dataset:
print(head(df))
# A tibble: 6 × 9
  Pregnancies Glucose BloodPressure SkinThickness Insulin   BMI
        <dbl>   <dbl>         <dbl>         <dbl>   <dbl> <dbl>
1           6     148            72            35       0  33.6
2           1      85            66            29       0  26.6
3           8     183            64             0       0  23.3
4           1      89            66            23      94  28.1
5           0     137            40            35     168  43.1
6           5     116            74             0       0  25.6
# ℹ 3 more variables: DiabetesPedigreeFunction <dbl>, Age <dbl>, Outcome <dbl>
cat("\nInformación general:\n")

Información general:
print(str(df))
tibble [768 × 9] (S3: tbl_df/tbl/data.frame)
 $ Pregnancies             : num [1:768] 6 1 8 1 0 5 3 10 2 8 ...
 $ Glucose                 : num [1:768] 148 85 183 89 137 116 78 115 197 125 ...
 $ BloodPressure           : num [1:768] 72 66 64 66 40 74 50 0 70 96 ...
 $ SkinThickness           : num [1:768] 35 29 0 23 35 0 32 0 45 0 ...
 $ Insulin                 : num [1:768] 0 0 0 94 168 0 88 0 543 0 ...
 $ BMI                     : num [1:768] 33.6 26.6 23.3 28.1 43.1 25.6 31 35.3 30.5 0 ...
 $ DiabetesPedigreeFunction: num [1:768] 0.627 0.351 0.672 0.167 2.288 ...
 $ Age                     : num [1:768] 50 31 32 21 33 30 26 29 53 54 ...
 $ Outcome                 : num [1:768] 1 0 1 0 1 0 1 0 1 1 ...
NULL

Paso 2. Definir variables X (inputs) y Y (target)

target_col <- "Outcome" 
X <- df %>% select(-Outcome)
Y <- df[[target_col]]
feature_names <- colnames(X)

cat("\nVariables de estudio:\n")

Variables de estudio:
print(feature_names)
[1] "Pregnancies"              "Glucose"                 
[3] "BloodPressure"            "SkinThickness"           
[5] "Insulin"                  "BMI"                     
[7] "DiabetesPedigreeFunction" "Age"                     

Paso 3. Estadísticos descriptivos

cat("\nResumen estadístico:\n")

Resumen estadístico:
print(summary(df))
  Pregnancies        Glucose      BloodPressure    SkinThickness  
 Min.   : 0.000   Min.   :  0.0   Min.   :  0.00   Min.   : 0.00  
 1st Qu.: 1.000   1st Qu.: 99.0   1st Qu.: 62.00   1st Qu.: 0.00  
 Median : 3.000   Median :117.0   Median : 72.00   Median :23.00  
 Mean   : 3.845   Mean   :120.9   Mean   : 69.11   Mean   :20.54  
 3rd Qu.: 6.000   3rd Qu.:140.2   3rd Qu.: 80.00   3rd Qu.:32.00  
 Max.   :17.000   Max.   :199.0   Max.   :122.00   Max.   :99.00  
    Insulin           BMI        DiabetesPedigreeFunction      Age       
 Min.   :  0.0   Min.   : 0.00   Min.   :0.0780           Min.   :21.00  
 1st Qu.:  0.0   1st Qu.:27.30   1st Qu.:0.2437           1st Qu.:24.00  
 Median : 30.5   Median :32.00   Median :0.3725           Median :29.00  
 Mean   : 79.8   Mean   :31.99   Mean   :0.4719           Mean   :33.24  
 3rd Qu.:127.2   3rd Qu.:36.60   3rd Qu.:0.6262           3rd Qu.:41.00  
 Max.   :846.0   Max.   :67.10   Max.   :2.4200           Max.   :81.00  
    Outcome     
 Min.   :0.000  
 1st Qu.:0.000  
 Median :0.000  
 Mean   :0.349  
 3rd Qu.:1.000  
 Max.   :1.000  

Paso 4. Revisar nulos

cat("\nValores nulos por columna:\n")

Valores nulos por columna:
print(sapply(df, function(x)
sum(is.na(x))))
             Pregnancies                  Glucose            BloodPressure 
                       0                        0                        0 
           SkinThickness                  Insulin                      BMI 
                       0                        0                        0 
DiabetesPedigreeFunction                      Age                  Outcome 
                       0                        0                        0 

Paso 5. Conteo por clase

cat("\nConteo por clase:\n")

Conteo por clase:
print(table(Y))
Y
  0   1 
500 268 
ggplot(df, aes(x = factor(Outcome))) + geom_bar(fill = "steelblue") + labs(title = "Conteo por clase", x = "Clase", y = "Cantidad")

Paso 6. Boxplots de variables numéricas

df_long <- df %>% 
  tidyr::pivot_longer(cols = -Outcome, names_to = "Variable", values_to = "Valor")

ggplot(df_long, aes(x = Variable, y = Valor)) + geom_boxplot(fill = "lightblue") + theme(axis.text.x = element_text(angle = 45, hjust = 1)) + labs(title = "Boxplots de variables numéricas")

Paso 7. Importancia de variables

set.seed(42)
rf_model <- randomForest(as.factor(Outcome) ~ ., data = df, importance = TRUE, ntree = 200)

varImpPlot(rf_model)

importancias <- importance(rf_model)
print(importancias)
                                 0          1 MeanDecreaseAccuracy
Pregnancies              10.045376  0.1796077             8.942198
Glucose                  22.122296 21.7098092            28.514781
BloodPressure             2.719151 -0.7970322             1.552620
SkinThickness             2.304140  0.6010348             2.370711
Insulin                   6.497576 -0.2431017             4.883847
BMI                       8.775309 12.3548253            15.185508
DiabetesPedigreeFunction  5.552919  0.7169622             4.574080
Age                      10.127655  6.3659783            12.370677
                         MeanDecreaseGini
Pregnancies                      28.82191
Glucose                          89.53865
BloodPressure                    31.72135
SkinThickness                    22.61302
Insulin                          25.11629
BMI                              56.68582
DiabetesPedigreeFunction         43.09255
Age                              47.04410

Remover las menos importantes (percentil 20)

scores <- importancias[,1] # MeanDecreaseAccuracy 
threshold <- quantile(scores, 0.20)
to_remove <- names(scores[scores <= threshold])

cat("\nVariables removidas:", to_remove, "\n")

Variables removidas: BloodPressure SkinThickness 
df_reduced <- df %>% select(-all_of(to_remove))

Paso 8. Dividir en entrenamiento y prueba

set.seed(42)
trainIndex <- createDataPartition(df_reduced$Outcome, p = 0.8, list = FALSE)
trainData <- df_reduced[trainIndex,]
testData <- df_reduced[-trainIndex,]

Paso 9. Entrenar Logistic Regression

model_lr <- glm(Outcome ~ ., data = trainData, family = "binomial")
pred_lr <- predict(model_lr, newdata = testData, type = "response")
pred_lr_class <- ifelse(pred_lr > 0.5, 1, 0)

Paso 10. Entrenar SVM

model_svm <- svm(as.factor(Outcome) ~ ., data = trainData, kernel = "radial", probability = TRUE) 

pred_svm <- predict(model_svm, newdata = testData)

Paso 11. Evaluación

cat("\nResultados Logistic Regression:\n")

Resultados Logistic Regression:
conf_lr <- confusionMatrix(as.factor(pred_lr_class), as.factor(testData$Outcome))
print(conf_lr)
Confusion Matrix and Statistics

          Reference
Prediction  0  1
         0 86 23
         1 13 31
                                          
               Accuracy : 0.7647          
                 95% CI : (0.6894, 0.8294)
    No Information Rate : 0.6471          
    P-Value [Acc > NIR] : 0.001164        
                                          
                  Kappa : 0.4622          
                                          
 Mcnemar's Test P-Value : 0.133614        
                                          
            Sensitivity : 0.8687          
            Specificity : 0.5741          
         Pos Pred Value : 0.7890          
         Neg Pred Value : 0.7045          
             Prevalence : 0.6471          
         Detection Rate : 0.5621          
   Detection Prevalence : 0.7124          
      Balanced Accuracy : 0.7214          
                                          
       'Positive' Class : 0               
                                          
cat("\nResultados SVM:\n")

Resultados SVM:
conf_svm <- confusionMatrix(as.factor(pred_svm), as.factor(testData$Outcome))
print(conf_svm)
Confusion Matrix and Statistics

          Reference
Prediction  0  1
         0 88 24
         1 11 30
                                          
               Accuracy : 0.7712          
                 95% CI : (0.6965, 0.8352)
    No Information Rate : 0.6471          
    P-Value [Acc > NIR] : 0.0006263       
                                          
                  Kappa : 0.4702          
                                          
 Mcnemar's Test P-Value : 0.0425225       
                                          
            Sensitivity : 0.8889          
            Specificity : 0.5556          
         Pos Pred Value : 0.7857          
         Neg Pred Value : 0.7317          
             Prevalence : 0.6471          
         Detection Rate : 0.5752          
   Detection Prevalence : 0.7320          
      Balanced Accuracy : 0.7222          
                                          
       'Positive' Class : 0               
                                          

Graficar matrices de confusión

plot_confusion_matrix <- function(cm, title = "Matriz de Confusión") { cm_table <- as.data.frame(cm$table)

ggplot(cm_table, aes(x = Reference, y = Prediction, fill = Freq)) + geom_tile(color = "white") + geom_text(aes(label = Freq), color = "black", size = 6) + scale_fill_gradient(low = "lightblue", high = "steelblue") + labs(title = title, x = "Clase Real", y = "Clase Predicha") + theme_minimal()
}

Gráfico para Logistic Regression

plot_confusion_matrix(conf_lr, "Matriz de Confusión - Logistic Regression")

Gráfico para SVM

plot_confusion_matrix(conf_svm, "Matriz de Confusión - SVM")

Comparación rápida

acc_lr <- conf_lr$overall["Accuracy"]
acc_svm <- conf_svm$overall["Accuracy"]

cat("\nAccuracy LR:", acc_lr, "\n")

Accuracy LR: 0.7647059 
cat("Accuracy SVM:", acc_svm, "\n") 
Accuracy SVM: 0.7712418 
if(acc_lr > acc_svm){
    cat("El mejor modelo es Logistic Regression\n") 
} else {
    cat("El mejor modelo es SVM\n")
  }
El mejor modelo es SVM

Interpretación

  • Capacidad para manejar relaciones no lineales

La Regresión Logística asume una relación lineal entre las variables independientes y la probabilidad de pertenecer a una clase.

En cambio, el SVM con kernel radial (RBF) proyecta los datos a un espacio de mayor dimensión y puede separar clases que no son linealmente separables.

En un dataset como el de diabetes, donde las variables biomédicas (glucosa, presión, insulina, etc.) no siempre se relacionan linealmente, SVM logra capturar mejor estas complejidades.

  • Margen de decisión más robusto

El principio de SVM es encontrar el hiperplano con el mayor margen de separación entre clases.

Esto significa que busca el límite que maximiza la distancia entre los puntos de frontera, reduciendo el riesgo de clasificar mal en datos nuevos (mejor generalización).

  • Mayor desempeño empírico en el dataset

En tu proyecto, al comparar los modelos, el accuracy de SVM es superior al de Logistic Regression.

Además de la precisión global, SVM suele tener mejor recall en la clase minoritaria (personas con diabetes), lo cual es fundamental en aplicaciones médicas, ya que es más grave un falso negativo (no detectar diabetes) que un falso positivo.

  • Menor sensibilidad a outliers en algunos casos

La regresión logística puede verse más afectada por valores extremos en las variables.

El SVM, al enfocarse en los vectores soporte (los puntos más cercanos a la frontera), ignora gran parte de los datos alejados, siendo más robusto frente a ruido.

Conclusión

El SVM es el mejor modelo en este caso porque:

  • Se adapta mejor a relaciones no lineales.

  • Proporciona una frontera de decisión más robusta.

  • Presenta un mejor desempeño empírico en el dataset de diabetes (mayor accuracy y mejores métricas de clasificación).

  • Tiene mayor valor práctico en contextos médicos, ya que reduce los falsos negativos.