Rosalinda Silvera Pastor T1

Librerías

library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ ggplot2   3.5.2     ✔ tibble    3.2.1
## ✔ lubridate 1.9.4     ✔ tidyr     1.3.1
## ✔ purrr     1.0.4     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(gtsummary)
library(MASS)
## 
## Adjuntando el paquete: 'MASS'
## 
## The following object is masked from 'package:gtsummary':
## 
##     select
## 
## The following object is masked from 'package:dplyr':
## 
##     select
library(doParallel)
## Cargando paquete requerido: foreach
## 
## Adjuntando el paquete: 'foreach'
## 
## The following objects are masked from 'package:purrr':
## 
##     accumulate, when
## 
## Cargando paquete requerido: iterators
## Cargando paquete requerido: parallel
library(missForest)
library(VIM)
## Cargando paquete requerido: colorspace
## Cargando paquete requerido: grid
## VIM is ready to use.
## 
## Suggestions and bug-reports can be submitted at: https://github.com/statistikat/VIM/issues
## 
## Adjuntando el paquete: 'VIM'
## 
## The following object is masked from 'package:missForest':
## 
##     nrmse
## 
## The following object is masked from 'package:datasets':
## 
##     sleep
library(ranger)
library(missForest)

Preparacion de los datos

ruta<-"D:/RosalindaSilveraT1/data_exam_ML_T1_2025_2.csv"
df <- read.csv(ruta) %>%
  as_tibble()
print(df)
## # A tibble: 2,000 × 13
##      age anaemia creatinine_phosphokinase diabetes ejection_fraction
##    <int>   <int>                    <int>    <int>             <int>
##  1    61       1                       80        1                38
##  2    70       0                     2695        1                NA
##  3    44       0                       NA        1                40
##  4    NA       0                      198        1                35
##  5    NA       0                      897       NA                45
##  6    65       1                      113        1                60
##  7    45       0                     2442        1                30
##  8    53       0                       NA        0                60
##  9    70       0                       92        0                60
## 10    70      NA                       81       NA                35
## # ℹ 1,990 more rows
## # ℹ 8 more variables: high_blood_pressure <int>, platelets <dbl>,
## #   serum_creatinine <dbl>, serum_sodium <int>, sex <int>, smoking <int>,
## #   time <int>, DEATH_EVENT <int>
set.seed(2025)

# Librería
glimpse(df)
## Rows: 2,000
## Columns: 13
## $ age                      <int> 61, 70, 44, NA, NA, 65, 45, 53, 70, 70, 54, 5…
## $ anaemia                  <int> 1, 0, 0, 0, 0, 1, 0, 0, 0, NA, 0, 0, 0, NA, N…
## $ creatinine_phosphokinase <int> 80, 2695, NA, 198, 897, 113, 2442, NA, 92, 81…
## $ diabetes                 <int> 1, 1, 1, 1, NA, 1, 1, 0, 0, NA, 1, 1, 0, 1, 0…
## $ ejection_fraction        <int> 38, NA, 40, 35, 45, 60, 30, 60, 60, 35, NA, N…
## $ high_blood_pressure      <int> 0, 0, 1, 1, 0, NA, 0, NA, 1, NA, 0, 0, 0, 0, …
## $ platelets                <dbl> 282000, 241000, 235000, 281000, 297000, 20300…
## $ serum_creatinine         <dbl> 1.40, 1.00, 0.70, 0.90, 1.00, 0.90, 1.10, 0.7…
## $ serum_sodium             <int> 137, 137, 139, 137, 133, 140, 139, NA, 140, 1…
## $ sex                      <int> 1, 1, NA, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1,…
## $ smoking                  <int> NA, 0, 0, 1, 0, NA, 0, 1, 1, 0, 0, 0, 0, 0, 0…
## $ time                     <int> 213, 247, 79, 146, 80, 94, 129, NA, 74, 212, …
## $ DEATH_EVENT              <int> 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, …
# Conversión de variables categóricas
df <- df %>% mutate(
  anaemia = factor(anaemia),
  diabetes = factor(diabetes),
  high_blood_pressure = factor(high_blood_pressure),
  sex = factor(sex),
  smoking = factor(smoking),
  DEATH_EVENT = factor(DEATH_EVENT, levels = c(0,1), labels = c("No", "Yes"))
)

# Instalación y carga de librería mice
if(!require(mice)) install.packages("mice")
## Cargando paquete requerido: mice
## 
## Adjuntando el paquete: 'mice'
## The following object is masked from 'package:stats':
## 
##     filter
## The following objects are masked from 'package:base':
## 
##     cbind, rbind
library(mice)

# Resumen de los NA por columna
md.pattern(df)

##     DEATH_EVENT sex creatinine_phosphokinase platelets diabetes
## 623           1   1                        1         1        1
## 45            1   1                        1         1        1
## 90            1   1                        1         1        1
## 23            1   1                        1         1        1
## 81            1   1                        1         1        1
## 55            1   1                        1         1        1
## 16            1   1                        1         1        1
## 32            1   1                        1         1        1
## 6             1   1                        1         1        1
## 10            1   1                        1         1        1
## 7             1   1                        1         1        1
## 24            1   1                        1         1        1
## 9             1   1                        1         1        1
## 98            1   1                        1         1        1
## 9             1   1                        1         1        1
## 3             1   1                        1         1        1
## 7             1   1                        1         1        1
## 13            1   1                        1         1        1
## 4             1   1                        1         1        1
## 85            1   1                        1         1        1
## 8             1   1                        1         1        1
## 6             1   1                        1         1        1
## 9             1   1                        1         1        1
## 17            1   1                        1         1        1
## 9             1   1                        1         1        1
## 74            1   1                        1         1        1
## 28            1   1                        1         1        1
## 5             1   1                        1         1        1
## 10            1   1                        1         1        1
## 39            1   1                        1         1        0
## 8             1   1                        1         1        0
## 8             1   1                        1         1        0
## 30            1   1                        1         1        0
## 9             1   1                        1         1        0
## 5             1   1                        1         1        0
## 3             1   1                        1         1        0
## 7             1   1                        1         1        0
## 6             1   1                        1         1        0
## 11            1   1                        1         1        0
## 7             1   1                        1         1        0
## 14            1   1                        1         1        0
## 3             1   1                        1         1        0
## 7             1   1                        1         1        0
## 5             1   1                        1         1        0
## 53            1   1                        1         0        1
## 19            1   1                        1         0        1
## 20            1   1                        1         0        1
## 7             1   1                        1         0        1
## 3             1   1                        1         0        1
## 10            1   1                        1         0        1
## 4             1   1                        1         0        1
## 16            1   1                        1         0        0
## 11            1   1                        1         0        0
## 25            1   1                        0         1        1
## 5             1   1                        0         1        1
## 9             1   1                        0         1        1
## 38            1   1                        0         1        1
## 9             1   1                        0         1        1
## 9             1   1                        0         1        1
## 5             1   1                        0         1        1
## 5             1   1                        0         1        1
## 5             1   1                        0         1        1
## 6             1   1                        0         1        1
## 11            1   1                        0         1        1
## 8             1   1                        0         1        1
## 6             1   1                        0         0        1
## 5             1   1                        0         0        1
## 11            1   0                        1         1        1
## 20            1   0                        1         1        1
## 14            1   0                        1         1        1
## 11            1   0                        1         1        1
## 6             1   0                        1         1        1
## 11            1   0                        1         1        1
## 6             1   0                        1         1        1
## 5             1   0                        1         1        1
## 11            1   0                        1         1        1
## 7             1   0                        1         1        1
## 6             1   0                        1         1        0
## 6             1   0                        1         1        0
## 9             1   0                        1         0        1
## 6             1   0                        1         0        1
## 6             1   0                        1         0        1
## 4             1   0                        0         1        1
## 4             1   0                        0         1        1
##               0 143                      154       175      201
##     serum_creatinine anaemia ejection_fraction time serum_sodium age smoking
## 623                1       1                 1    1            1   1       1
## 45                 1       1                 1    1            1   1       1
## 90                 1       1                 1    1            1   1       0
## 23                 1       1                 1    1            1   1       0
## 81                 1       1                 1    1            1   0       1
## 55                 1       1                 1    1            0   1       1
## 16                 1       1                 1    1            0   0       0
## 32                 1       1                 1    0            1   1       1
## 6                  1       1                 1    0            1   1       1
## 10                 1       1                 1    0            1   1       0
## 7                  1       1                 1    0            1   0       1
## 24                 1       1                 1    0            0   1       1
## 9                  1       1                 1    0            0   1       0
## 98                 1       1                 0    1            1   1       1
## 9                  1       1                 0    1            1   1       1
## 3                  1       1                 0    1            1   0       1
## 7                  1       1                 0    1            0   1       1
## 13                 1       1                 0    0            1   1       1
## 4                  1       1                 0    0            1   1       1
## 85                 1       0                 1    1            1   1       1
## 8                  1       0                 1    1            1   0       1
## 6                  1       0                 1    0            1   1       1
## 9                  1       0                 1    0            1   1       1
## 17                 1       0                 1    0            1   1       0
## 9                  1       0                 0    1            1   1       1
## 74                 0       1                 1    1            1   1       1
## 28                 0       1                 1    1            1   1       1
## 5                  0       1                 1    1            1   0       1
## 10                 0       1                 0    1            0   1       1
## 39                 1       1                 1    1            1   1       1
## 8                  1       1                 1    1            1   1       1
## 8                  1       1                 1    1            1   1       0
## 30                 1       1                 1    1            1   0       1
## 9                  1       1                 1    1            0   1       1
## 5                  1       1                 1    1            0   1       1
## 3                  1       1                 0    1            1   1       1
## 7                  1       1                 0    1            0   0       1
## 6                  1       1                 0    0            1   1       0
## 11                 1       0                 1    1            1   1       1
## 7                  1       0                 1    1            1   1       1
## 14                 0       1                 1    1            1   1       1
## 3                  0       1                 1    1            0   1       1
## 7                  0       1                 1    0            0   1       1
## 5                  0       1                 0    1            1   1       1
## 53                 1       1                 1    1            1   1       1
## 19                 1       1                 1    1            1   1       1
## 20                 1       1                 1    1            1   1       0
## 7                  1       1                 1    1            1   1       0
## 3                  1       1                 1    1            1   0       1
## 10                 1       1                 1    0            1   1       1
## 4                  0       1                 1    1            1   1       1
## 16                 1       1                 1    1            1   1       1
## 11                 1       0                 1    1            1   1       1
## 25                 1       1                 1    1            1   1       1
## 5                  1       1                 1    1            1   0       1
## 9                  1       1                 1    1            1   0       0
## 38                 1       1                 1    1            0   1       1
## 9                  1       1                 1    0            1   1       1
## 9                  1       1                 0    1            1   1       1
## 5                  1       1                 0    1            0   1       1
## 5                  1       0                 1    1            1   1       1
## 5                  1       0                 1    0            1   0       1
## 6                  0       1                 1    1            1   1       1
## 11                 0       0                 1    1            1   0       1
## 8                  0       0                 1    0            1   1       1
## 6                  1       1                 1    1            1   1       1
## 5                  1       1                 1    0            0   1       1
## 11                 1       1                 1    1            1   1       1
## 20                 1       1                 1    1            1   0       1
## 14                 1       1                 1    0            1   1       1
## 11                 1       1                 1    0            0   1       1
## 6                  1       1                 0    1            0   1       0
## 11                 1       0                 1    1            1   1       1
## 6                  1       0                 1    1            1   0       1
## 5                  0       1                 1    1            1   1       1
## 11                 0       1                 1    1            1   0       1
## 7                  0       1                 0    1            1   1       1
## 6                  1       1                 1    1            1   1       1
## 6                  1       1                 1    1            1   1       0
## 9                  1       1                 1    1            1   1       1
## 6                  1       1                 0    1            0   1       1
## 6                  0       1                 1    1            1   1       1
## 4                  1       1                 1    1            1   1       1
## 4                  0       1                 0    1            1   1       1
##                  208     209               211  212          223 227     227
##     high_blood_pressure     
## 623                   1    0
## 45                    0    1
## 90                    1    1
## 23                    0    2
## 81                    1    1
## 55                    1    1
## 16                    1    3
## 32                    1    1
## 6                     0    2
## 10                    0    3
## 7                     1    2
## 24                    1    2
## 9                     0    4
## 98                    1    1
## 9                     0    2
## 3                     1    2
## 7                     1    2
## 13                    1    2
## 4                     0    3
## 85                    1    1
## 8                     1    2
## 6                     1    2
## 9                     0    3
## 17                    1    3
## 9                     1    2
## 74                    1    1
## 28                    0    2
## 5                     1    2
## 10                    1    3
## 39                    1    1
## 8                     0    2
## 8                     1    2
## 30                    1    2
## 9                     1    2
## 5                     0    3
## 3                     1    2
## 7                     1    4
## 6                     1    4
## 11                    1    2
## 7                     0    3
## 14                    1    2
## 3                     0    4
## 7                     1    4
## 5                     1    3
## 53                    1    1
## 19                    0    2
## 20                    1    2
## 7                     0    3
## 3                     1    2
## 10                    1    2
## 4                     1    2
## 16                    1    2
## 11                    1    3
## 25                    1    1
## 5                     1    2
## 9                     0    4
## 38                    1    2
## 9                     1    2
## 9                     0    3
## 5                     0    4
## 5                     1    2
## 5                     0    5
## 6                     1    2
## 11                    1    4
## 8                     1    4
## 6                     1    2
## 5                     0    5
## 11                    1    1
## 20                    0    3
## 14                    1    2
## 11                    1    3
## 6                     1    4
## 11                    1    2
## 6                     1    3
## 5                     1    2
## 11                    1    3
## 7                     1    3
## 6                     1    2
## 6                     1    3
## 9                     1    2
## 6                     1    4
## 6                     1    3
## 4                     1    2
## 4                     1    4
##                     245 2435
# Ejecutar imputación usando el método por defecto
set.seed(2025)
imputacion <- mice(df, m = 1, maxit = 5, seed = 2025)
## 
##  iter imp variable
##   1   1  age  anaemia  creatinine_phosphokinase  diabetes  ejection_fraction  high_blood_pressure  platelets  serum_creatinine  serum_sodium  sex  smoking  time
##   2   1  age  anaemia  creatinine_phosphokinase  diabetes  ejection_fraction  high_blood_pressure  platelets  serum_creatinine  serum_sodium  sex  smoking  time
##   3   1  age  anaemia  creatinine_phosphokinase  diabetes  ejection_fraction  high_blood_pressure  platelets  serum_creatinine  serum_sodium  sex  smoking  time
##   4   1  age  anaemia  creatinine_phosphokinase  diabetes  ejection_fraction  high_blood_pressure  platelets  serum_creatinine  serum_sodium  sex  smoking  time
##   5   1  age  anaemia  creatinine_phosphokinase  diabetes  ejection_fraction  high_blood_pressure  platelets  serum_creatinine  serum_sodium  sex  smoking  time
# Revisar el resumen de la imputación
summary(imputacion)
## Class: mids
## Number of multiple imputations:  1 
## Imputation methods:
##                      age                  anaemia creatinine_phosphokinase 
##                    "pmm"                 "logreg"                    "pmm" 
##                 diabetes        ejection_fraction      high_blood_pressure 
##                 "logreg"                    "pmm"                 "logreg" 
##                platelets         serum_creatinine             serum_sodium 
##                    "pmm"                    "pmm"                    "pmm" 
##                      sex                  smoking                     time 
##                 "logreg"                 "logreg"                    "pmm" 
##              DEATH_EVENT 
##                       "" 
## PredictorMatrix:
##                          age anaemia creatinine_phosphokinase diabetes
## age                        0       1                        1        1
## anaemia                    1       0                        1        1
## creatinine_phosphokinase   1       1                        0        1
## diabetes                   1       1                        1        0
## ejection_fraction          1       1                        1        1
## high_blood_pressure        1       1                        1        1
##                          ejection_fraction high_blood_pressure platelets
## age                                      1                   1         1
## anaemia                                  1                   1         1
## creatinine_phosphokinase                 1                   1         1
## diabetes                                 1                   1         1
## ejection_fraction                        0                   1         1
## high_blood_pressure                      1                   0         1
##                          serum_creatinine serum_sodium sex smoking time
## age                                     1            1   1       1    1
## anaemia                                 1            1   1       1    1
## creatinine_phosphokinase                1            1   1       1    1
## diabetes                                1            1   1       1    1
## ejection_fraction                       1            1   1       1    1
## high_blood_pressure                     1            1   1       1    1
##                          DEATH_EVENT
## age                                1
## anaemia                            1
## creatinine_phosphokinase           1
## diabetes                           1
## ejection_fraction                  1
## high_blood_pressure                1
# Obtener el dataset imputado completo
df_imputado <- complete(imputacion)

# Verificar que no queden NA
colSums(is.na(df_imputado))
##                      age                  anaemia creatinine_phosphokinase 
##                        0                        0                        0 
##                 diabetes        ejection_fraction      high_blood_pressure 
##                        0                        0                        0 
##                platelets         serum_creatinine             serum_sodium 
##                        0                        0                        0 
##                      sex                  smoking                     time 
##                        0                        0                        0 
##              DEATH_EVENT 
##                        0

Analisis descriptivo Parte 1

library(dplyr)
library(gtsummary)

# Variables continuas y categóricas sin DEATH_EVENT
continuas <- c("age", "creatinine_phosphokinase", "ejection_fraction", "platelets", "serum_creatinine", "serum_sodium", "time")
categoricas <- c("anaemia", "diabetes", "high_blood_pressure", "sex", "smoking")

# Tabla resumen agrupada por DEATH_EVENT
table1 <- tbl_summary(
  df_imputado,
  by = "DEATH_EVENT",
  type = list(
    all_of(continuas) ~ "continuous",
    all_of(categoricas) ~ "categorical"
  ),
  missing = "no"
)

table1
Characteristic No
N = 1,359
1
Yes
N = 641
1
age 60 (50, 67) 60 (51, 75)
anaemia

    0 762 (56%) 375 (59%)
    1 597 (44%) 266 (41%)
creatinine_phosphokinase 257 (109, 618) 280 (128, 582)
diabetes

    0 792 (58%) 359 (56%)
    1 567 (42%) 282 (44%)
ejection_fraction 38 (35, 45) 35 (25, 38)
high_blood_pressure

    0 924 (68%) 399 (62%)
    1 435 (32%) 242 (38%)
platelets 257,000 (219,000, 302,000) 262,000 (200,000, 314,000)
serum_creatinine 1.00 (0.90, 1.20) 1.30 (1.10, 1.90)
serum_sodium 137 (135, 140) 134 (132, 137)
sex

    0 451 (33%) 217 (34%)
    1 908 (67%) 424 (66%)
smoking

    0 881 (65%) 421 (66%)
    1 478 (35%) 220 (34%)
time 147 (95, 212) 45 (20, 111)
1 Median (Q1, Q3); n (%)

Comentarios:

Los pacientes fallecidos tienden a tener una mayor variabilidad hacia edades más avanzadas, esto sugiere que existe un mayor riesgo en edades mayores. La proporción de anemia es ligeramente menor en fallecidos (41%) que en no fallecidos (44%). No existe un diferencia marcada de mortalidad respecto a las personas que tiene diabetes de las que no lo tienen. En el caso de la creatinina serica los fallecidos presentan valores mas elevados indicando grave insuficiencia renal. No existe una diferencia marcada entre el sexo de los fallecidos.

Análisis descriptivo Parte 2

library(gtsummary)

# Variables continuas y categóricas en el contexto de insuficiencia cardíaca
continuas <- c("age", "creatinine_phosphokinase", "ejection_fraction", 
               "platelets", "serum_creatinine", "serum_sodium", "time")

categoricas <- c("anaemia", "diabetes", "high_blood_pressure", 
                 "sex", "smoking")

# Crear tabla resumen con gtsummary
table2 <- tbl_summary(
    df_imputado,
    by = DEATH_EVENT,  # Agrupar por evento de muerte
    type = list(
      all_of(continuas) ~ "continuous",
      all_of(categoricas) ~ "categorical"
    ),
    missing = "no"
  ) %>%
  add_n() %>%  # Número total de observaciones por grupo
  add_p() %>%  # Test estadístico (t-test, chi-cuadrado, etc.)
  modify_header(label = "**Variable**") %>%  # Cambiar encabezado de la columna de variables
  bold_labels()  # Resaltar nombres de las variables

table2
Variable N No
N = 1,359
1
Yes
N = 641
1
p-value2
age 2,000 60 (50, 67) 60 (51, 75) <0.001
anaemia 2,000

0.3
    0
762 (56%) 375 (59%)
    1
597 (44%) 266 (41%)
creatinine_phosphokinase 2,000 257 (109, 618) 280 (128, 582) 0.6
diabetes 2,000

0.3
    0
792 (58%) 359 (56%)
    1
567 (42%) 282 (44%)
ejection_fraction 2,000 38 (35, 45) 35 (25, 38) <0.001
high_blood_pressure 2,000

0.011
    0
924 (68%) 399 (62%)
    1
435 (32%) 242 (38%)
platelets 2,000 257,000 (219,000, 302,000) 262,000 (200,000, 314,000) 0.093
serum_creatinine 2,000 1.00 (0.90, 1.20) 1.30 (1.10, 1.90) <0.001
serum_sodium 2,000 137 (135, 140) 134 (132, 137) <0.001
sex 2,000

0.8
    0
451 (33%) 217 (34%)
    1
908 (67%) 424 (66%)
smoking 2,000

0.7
    0
881 (65%) 421 (66%)
    1
478 (35%) 220 (34%)
time 2,000 147 (95, 212) 45 (20, 111) <0.001
1 Median (Q1, Q3); n (%)
2 Wilcoxon rank sum test; Pearson’s Chi-squared test

Edad (age) La mediana de edad es 60 años en ambos grupos, aunque quienes fallecieron presentan un rango de edad más amplio (51–75 años frente a 50–67 años). Esto indica que, a pesar de que la mediana sea igual, la mortalidad tiende a aumentar con la edad (p < 0.001).

Anemia (anaemia) Las proporciones de anemia son similares entre los grupos (44% frente a 41%) y no se observa una diferencia estadísticamente significativa (p = 0.3), sugiriendo que la anemia no está claramente relacionada con la muerte.

CPK (creatinine_phosphokinase) Los niveles son comparables entre los grupos y no presentan diferencia significativa (p = 0.6), lo que implica que este marcador no tiene una fuerte asociación con la mortalidad en este análisis.

Diabetes Las proporciones son muy parecidas (42% frente a 44%) y no hay diferencias significativas (p = 0.3), por lo que la diabetes no parece estar vinculada directamente a la mortalidad en este conjunto de datos.

Fracción de eyección (ejection_fraction) Los fallecidos tienen una mediana más baja (35 vs. 38), con una diferencia significativa (p < 0.001), lo que indica una función cardíaca deteriorada asociada a un mayor riesgo de muerte.

Presión arterial alta (high_blood_pressure) Esta condición es más frecuente en el grupo de fallecidos (38% vs. 32%) y presenta una diferencia significativa (p = 0.011), sugiriendo que la hipertensión está asociada a un incremento ligero en la mortalidad.

Plaquetas (platelets) Los niveles son ligeramente más elevados en quienes murieron, aunque sin alcanzar significancia estadística (p = 0.093), por lo que no se asocia de forma clara con la muerte.

Creatinina sérica (serum_creatinine) Las concentraciones son mayores en el grupo de fallecidos (1.30 vs. 1.00) y muestran una asociación fuerte y significativa (p < 0.001), reflejando un posible deterioro de la función renal en estos pacientes.

Sodio sérico (serum_sodium) Los niveles son menores en los fallecidos (134 vs. 137), con una diferencia significativa (p < 0.001), indicando que la hiponatremia podría estar vinculada a un mayor riesgo de muerte.

Sexo (sex) La distribución es similar en ambos grupos (66% hombres), sin diferencias significativas (p = 0.8), por lo que el sexo no parece influir en la mortalidad.

Tabaquismo (smoking) Las proporciones son parecidas (35% frente a 34%) y no hay diferencias significativas (p = 0.7), indicando que fumar no se asocia directamente con la muerte en este análisis.

Tiempo de seguimiento (time) El tiempo de seguimiento es notablemente menor en quienes fallecieron (45 días vs. 147 días), con una fuerte asociación estadística (p < 0.001), lo que refleja que la muerte ocurrió en un periodo más corto, aspecto relevante para el análisis de supervivencia.

Selección de variables mediante el método AIC

# Asegurarse de que DEATH_EVENT esté como factor (variable categórica)
df_imputado$DEATH_EVENT <- as.factor(df_imputado$DEATH_EVENT)

# Ajustar modelo completo con todas las variables
mod1 <- glm(DEATH_EVENT ~ ., data = df_imputado, family = binomial(link = "logit"))

# Aplicar el método de selección hacia atrás basado en AIC
mod_aic <- step(mod1)
## Start:  AIC=1589.53
## DEATH_EVENT ~ age + anaemia + creatinine_phosphokinase + diabetes + 
##     ejection_fraction + high_blood_pressure + platelets + serum_creatinine + 
##     serum_sodium + sex + smoking + time
## 
##                            Df Deviance    AIC
## - diabetes                  1   1564.0 1588.0
## - smoking                   1   1564.2 1588.2
## - high_blood_pressure       1   1565.5 1589.5
## <none>                          1563.5 1589.5
## - sex                       1   1566.3 1590.3
## - platelets                 1   1566.8 1590.8
## - age                       1   1571.1 1595.1
## - creatinine_phosphokinase  1   1576.7 1600.7
## - anaemia                   1   1581.7 1605.7
## - serum_sodium              1   1583.4 1607.4
## - serum_creatinine          1   1626.6 1650.6
## - ejection_fraction         1   1696.7 1720.7
## - time                      1   2125.7 2149.7
## 
## Step:  AIC=1587.95
## DEATH_EVENT ~ age + anaemia + creatinine_phosphokinase + ejection_fraction + 
##     high_blood_pressure + platelets + serum_creatinine + serum_sodium + 
##     sex + smoking + time
## 
##                            Df Deviance    AIC
## - smoking                   1   1564.7 1586.7
## - high_blood_pressure       1   1565.8 1587.8
## <none>                          1564.0 1588.0
## - platelets                 1   1566.9 1588.9
## - sex                       1   1567.0 1589.0
## - age                       1   1572.2 1594.2
## - creatinine_phosphokinase  1   1577.2 1599.2
## - anaemia                   1   1582.2 1604.2
## - serum_sodium              1   1584.0 1606.0
## - serum_creatinine          1   1626.8 1648.8
## - ejection_fraction         1   1696.7 1718.7
## - time                      1   2126.4 2148.4
## 
## Step:  AIC=1586.73
## DEATH_EVENT ~ age + anaemia + creatinine_phosphokinase + ejection_fraction + 
##     high_blood_pressure + platelets + serum_creatinine + serum_sodium + 
##     sex + time
## 
##                            Df Deviance    AIC
## - high_blood_pressure       1   1566.6 1586.6
## <none>                          1564.7 1586.7
## - platelets                 1   1567.9 1587.9
## - sex                       1   1570.2 1590.2
## - age                       1   1573.2 1593.2
## - creatinine_phosphokinase  1   1577.7 1597.7
## - anaemia                   1   1582.2 1602.2
## - serum_sodium              1   1584.7 1604.7
## - serum_creatinine          1   1632.4 1652.4
## - ejection_fraction         1   1699.1 1719.1
## - time                      1   2132.3 2152.3
## 
## Step:  AIC=1586.63
## DEATH_EVENT ~ age + anaemia + creatinine_phosphokinase + ejection_fraction + 
##     platelets + serum_creatinine + serum_sodium + sex + time
## 
##                            Df Deviance    AIC
## <none>                          1566.6 1586.6
## - platelets                 1   1569.7 1587.7
## - sex                       1   1573.0 1591.0
## - age                       1   1575.4 1593.4
## - creatinine_phosphokinase  1   1578.4 1596.4
## - anaemia                   1   1584.1 1602.1
## - serum_sodium              1   1586.7 1604.7
## - serum_creatinine          1   1632.4 1650.4
## - ejection_fraction         1   1701.6 1719.6
## - time                      1   2161.0 2179.0
# Mostrar resumen del modelo seleccionado
summary(mod_aic)
## 
## Call:
## glm(formula = DEATH_EVENT ~ age + anaemia + creatinine_phosphokinase + 
##     ejection_fraction + platelets + serum_creatinine + serum_sodium + 
##     sex + time, family = binomial(link = "logit"), data = df_imputado)
## 
## Coefficients:
##                            Estimate Std. Error z value Pr(>|z|)    
## (Intercept)               1.301e+01  2.046e+00   6.362 2.00e-10 ***
## age                       3.036e-05  9.331e-06   3.254  0.00114 ** 
## anaemia1                 -5.683e-01  1.376e-01  -4.130 3.63e-05 ***
## creatinine_phosphokinase  2.171e-04  6.639e-05   3.270  0.00108 ** 
## ejection_fraction        -6.783e-02  6.384e-03 -10.624  < 2e-16 ***
## platelets                -1.187e-06  6.905e-07  -1.719  0.08561 .  
## serum_creatinine          5.249e-01  7.683e-02   6.832 8.38e-12 ***
## serum_sodium             -6.546e-02  1.464e-02  -4.471 7.79e-06 ***
## sex1                     -3.515e-01  1.393e-01  -2.523  0.01165 *  
## time                     -2.263e-02  1.189e-03 -19.029  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 2509.0  on 1999  degrees of freedom
## Residual deviance: 1566.6  on 1990  degrees of freedom
## AIC: 1586.6
## 
## Number of Fisher Scoring iterations: 5
library(gtsummary)

# Mostrar tabla de regresión con odds ratios e IC al 95%
t1 <- tbl_regression(mod_aic, exponentiate = TRUE)
t1
Characteristic OR 95% CI p-value
age 1.00 1.00, 1.00 0.001
anaemia


    0 — —
    1 0.57 0.43, 0.74 <0.001
creatinine_phosphokinase 1.00 1.00, 1.00 0.001
ejection_fraction 0.93 0.92, 0.95 <0.001
platelets 1.00 1.00, 1.00 0.086
serum_creatinine 1.69 1.46, 1.98 <0.001
serum_sodium 0.94 0.91, 0.96 <0.001
sex


    0 — —
    1 0.70 0.54, 0.92 0.012
time 0.98 0.98, 0.98 <0.001
Abbreviations: CI = Confidence Interval, OR = Odds Ratio

En cuanto a la edad, el valor p es significativo, lo que indica que a mayor edad, el riesgo de fallecer también aumenta.

Los pacientes con anemia presentan un 43% menos de probabilidad de morir en comparación con aquellos que no tienen anemia.

La creatinina muestra un valor p significativo, sugiriendo que niveles más elevados se relacionan con un mayor riesgo de muerte.

Respecto a la fracción de eyección, el riesgo de mortalidad disminuye un 7% por cada incremento del 1% en esta medida.

Cada aumento en los niveles de creatinina se asocia con un 69% más de riesgo de fallecer, lo que refleja una función renal deteriorada que puede agravar el pronóstico.

Por cada unidad adicional de sodio en sangre, el riesgo de muerte se reduce en un 6%.

Los hombres tienen un riesgo un 30% menor de morir en comparación con las mujeres.

Los principales factores vinculados con un mayor riesgo de mortalidad son: Niveles elevados de creatinina (indicativo de mala función renal),

Fracción de eyección baja (indicativa de función cardíaca comprometida),

Niveles bajos de sodio (que reflejan desequilibrios electrolíticos).

# Extraer solo las variables seleccionadas por AIC, puedes ajustar esta lista según output de step()
data_model_sel2 <- df_imputado %>%
  dplyr::select(DEATH_EVENT, age, ejection_fraction, serum_creatinine, serum_sodium, time)

# Exportar a CSV
write.csv(data_model_sel2, "data_select.csv", row.names = FALSE, quote = FALSE)
#%% Cargando librerias #
import pandas as pd
import numpy as np
from collections import defaultdict

from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer

from sklearn.preprocessing import OneHotEncoder, LabelEncoder, MinMaxScaler, OrdinalEncoder

from imblearn.over_sampling import RandomOverSampler, SMOTE
from imblearn.under_sampling import RandomUnderSampler

from sklearn.model_selection import train_test_split, GridSearchCV

from sklearn.metrics import accuracy_score, classification_report, confusion_matrix, make_scorer


from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from xgboost import XGBClassifier

import seaborn as sns
import matplotlib.pyplot as plt


import os
import random

from sklearn.base import BaseEstimator, TransformerMixin
import joblib

Funciones propias

def softM(num1,LAMBDA=2):
    PI = np.pi
    AVG = np.mean(num1)
    SD = np.std(num1)
    #LAMBDA = 2
    x1 = num1
    vt = (x1- AVG)/(LAMBDA * (SD/(2 * PI)))
    trans = 1/(1 + np.exp(-vt))
    return(trans)

class softMax(BaseEstimator, TransformerMixin):
    def __init__(self,LAMBDA=2):
        self.LAMBDA = LAMBDA
        #self.parametro2 = parametro2
        pass

    def fit(self, X, y=None):
        return self
    
    def transform(self, X):
        res = X.apply(softM,LAMBDA=self.LAMBDA)
        return res

Lectura de los datos

df2 = pd.read_csv("data_select.csv")
print(df2.head())
##   DEATH_EVENT  age  ejection_fraction  serum_creatinine  serum_sodium  time
## 0          No   61                 38               1.4           137   213
## 1          No   70                 60               1.0           137   247
## 2          No   44                 40               0.7           139    79
## 3          No   72                 35               0.9           137   146
## 4          No   45                 45               1.0           133    80

Transformando las variables y guardando el transformador

# Mapear variable objetivo DEATH_EVENT a 0 y 1 (si está en "No" y "Yes")
df2["DEATH_EVENT"] = df2["DEATH_EVENT"].map({"No": 0, "Yes": 1})

# Si ya está en 0 y 1 o números, asegúrate que sea tipo entero
df2["DEATH_EVENT"] = df2["DEATH_EVENT"].astype(int)

# Definir variables numéricas y variable objetivo
numerical_vars = ['age', 'ejection_fraction', 'serum_creatinine', 'serum_sodium', 'time']
target_var = ['DEATH_EVENT']
# Transformador de la data
full_transform = ColumnTransformer([
    ("num1", MinMaxScaler(), numerical_vars)
])

X = full_transform.fit_transform(df2)

# Guardar el transformador
joblib.dump(full_transform, 'transformador.pkl')
## ['transformador.pkl']

# Cargar y usar el transformador guardado
loaded_trans = joblib.load('transformador.pkl')
X_loaded = loaded_trans.transform(df2)

Transformando la variable target en factor

'''
# Ordinal encoder
label_encoder = OrdinalEncoder(categories=[["Yes","No"]])
y = label_encoder.fit_transform(df1[target]).ravel().astype(int)
'''
## '\n# Ordinal encoder\nlabel_encoder = OrdinalEncoder(categories=[["Yes","No"]])\ny = label_encoder.fit_transform(df1[target]).ravel().astype(int)\n'
label_encoder = LabelEncoder()
ordered_levels = ['No', 'Yes']
label_encoder.classes_ = np.array(ordered_levels)  # Opcional para fijar el orden

y = label_encoder.fit_transform(df2[target_var].values.ravel())

print(label_encoder.classes_)  # Verifica las clases
## [0 1]
print(y[:10])  # Muestra las primeras 10 etiquetas transformadas
## [0 0 0 0 0 0 1 0 0 0]

#Balanceo de datos

# Visualizar distribución original de la variable objetivo
df2[target_var].value_counts().plot.bar()
plt.title("Distribución original de la variable objetivo")
plt.show()

# Balanceo con RandomOverSampler
bal = RandomOverSampler(random_state=42)
X_resampled, y_resampled = bal.fit_resample(X, y)
## C:\Users\SUITE\CONDA~1\envs\DATA_S~1\lib\site-packages\sklearn\utils\deprecation.py:151: FutureWarning: 'force_all_finite' was renamed to 'ensure_all_finite' in 1.6 and will be removed in 1.8.
##   warnings.warn(
## C:\Users\SUITE\CONDA~1\envs\DATA_S~1\lib\site-packages\sklearn\base.py:484: FutureWarning: `BaseEstimator._check_n_features` is deprecated in 1.6 and will be removed in 1.7. Use `sklearn.utils.validation._check_n_features` instead.
##   warnings.warn(
## C:\Users\SUITE\CONDA~1\envs\DATA_S~1\lib\site-packages\sklearn\base.py:493: FutureWarning: `BaseEstimator._check_feature_names` is deprecated in 1.6 and will be removed in 1.7. Use `sklearn.utils.validation._check_feature_names` instead.
##   warnings.warn(
# Verificar nueva distribución balanceada
counts = defaultdict(int)
for item in y_resampled:
    counts[item] += 1

print("Distribución después del balanceo:", dict(counts))
## Distribución después del balanceo: {np.int64(0): 1359, np.int64(1): 1359}

Separacion en data de entrenamiento y prueba

# Separar en entrenamiento y prueba después del balanceo
X_train, X_test, y_train, y_test = train_test_split(
    X_resampled, y_resampled, 
    test_size=0.2, 
    random_state=1442
)

print(f"Tamaño de X_train: {X_train.shape}")
## Tamaño de X_train: (2174, 5)
print(f"Tamaño de X_test: {X_test.shape}")
## Tamaño de X_test: (544, 5)
print(f"Tamaño de y_train: {y_train.shape}")
## Tamaño de y_train: (2174,)
print(f"Tamaño de y_test: {y_test.shape}")
## Tamaño de y_test: (544,)

Entrenando el modelo XCBClassifier

Definición de hiperparámetros

model = XGBClassifier(random_state=153468)
# Definición del rango de valores de los parámetros del modelo
params = { 'max_depth': [4,6,8],#Numero de nodos
           'learning_rate': [0.01, 0.03, 0.05],# eta
           'n_estimators': [100, 200],
           'colsample_bytree': [0.3, 0.7]}
          

Entrenamiento del modelo

grid = GridSearchCV(estimator=model,param_grid=params,refit=True,verbose=3,cv=5,n_jobs=1)
grid.fit(X_train,y_train)
GridSearchCV(cv=5,
             estimator=XGBClassifier(base_score=None, booster=None,
                                     callbacks=None, colsample_bylevel=None,
                                     colsample_bynode=None,
                                     colsample_bytree=None, device=None,
                                     early_stopping_rounds=None,
                                     enable_categorical=False, eval_metric=None,
                                     feature_types=None, gamma=None,
                                     grow_policy=None, importance_type=None,
                                     interaction_constraints=None,
                                     learning_rate=None,...
                                     max_cat_to_onehot=None,
                                     max_delta_step=None, max_depth=None,
                                     max_leaves=None, min_child_weight=None,
                                     missing=nan, monotone_constraints=None,
                                     multi_strategy=None, n_estimators=None,
                                     n_jobs=None, num_parallel_tree=None,
                                     random_state=153468, ...),
             n_jobs=1,
             param_grid={'colsample_bytree': [0.3, 0.7],
                         'learning_rate': [0.01, 0.03, 0.05],
                         'max_depth': [4, 6, 8], 'n_estimators': [100, 200]},
             verbose=3)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
grid.best_params_
## {'colsample_bytree': 0.7, 'learning_rate': 0.03, 'max_depth': 8, 'n_estimators': 200}
grid.best_score_
## np.float64(0.994020869749457)

Guardando el modelo

joblib.dump(grid, 'model.pkl')
## ['model.pkl']
model = joblib.load('model.pkl')
model.predict(X_train)
## array([1, 1, 1, ..., 0, 0, 1])

Evaluacion del modelo

Descripción de la imagen Descripción de la imagen

Evalución del modelo con python

Data de entrenamiento

y_pred_train = grid.predict(X_train)
print(confusion_matrix(y_true = y_train, y_pred = y_pred_train))
## [[1093    1]
##  [   0 1080]]
print(classification_report(y_true = y_train, y_pred = y_pred_train))#Output
##               precision    recall  f1-score   support
## 
##            0       1.00      1.00      1.00      1094
##            1       1.00      1.00      1.00      1080
## 
##     accuracy                           1.00      2174
##    macro avg       1.00      1.00      1.00      2174
## weighted avg       1.00      1.00      1.00      2174

El modelo logró predecir de manera prácticamente perfecta los datos de entrenamiento, cometiendo casi ningún error. Las métricas de precisión, recall y F1-score para ambas clases alcanzaron el valor ideal de 1.00, reflejando un rendimiento óptimo en el conjunto de entrenamiento. La exactitud (accuracy) también fue del 100%, confirmando la capacidad del modelo para clasificar correctamente todos los casos durante el entrenamiento.

Data de prueba

y_pred = grid.predict(X_test)
print(confusion_matrix(y_true = y_test, y_pred = y_pred))
## [[263   2]
##  [  0 279]]
print(classification_report(y_true = y_test, y_pred = y_pred))#Output
##               precision    recall  f1-score   support
## 
##            0       1.00      0.99      1.00       265
##            1       0.99      1.00      1.00       279
## 
##     accuracy                           1.00       544
##    macro avg       1.00      1.00      1.00       544
## weighted avg       1.00      1.00      1.00       544

El modelo mantiene un rendimiento sobresaliente con datos nuevos, presentando únicamente 2 errores en la clase 0 y ninguno en la clase 1. Tanto la precisión como el recall superan el 99% en ambas clases, reflejando una alta capacidad predictiva. El F1-score de 1.00 evidencia un equilibrio excelente entre precisión y recall. La exactitud se acerca al 100%, lo que demuestra un desempeño muy sólido y confiable en datos no vistos durante el entrenamiento.

La pequeña diferencia observada, con solo 2 falsos positivos en el conjunto de prueba, es mínima y no afecta de manera significativa la calidad general del modelo.

Guardando las probabilidades predichas

probabilities = grid.predict_proba(X_test)
df_pred = pd.DataFrame(probabilities,columns=label_encoder.classes_)
df_pred["target"] = y_test
df_pred["y_pred"] = y_pred
df_pred.to_csv("pred_model.csv",index=False)

Evaluacion del modelo con R

Descripción de la imagen
Descripción de la imagen