Ejercicio 1

Autor/a

Mario Camacho

Un inmunólogo dispone de los siguientes datos de un conjunto de pacientes con SIDA.

Realizar una estimación de la función de supervivencia sin utilizar ninguna de las covariables. Obtener el valor de la mediana, de la supervivencia a los 20 meses, la curva de Kaplan Meier y la mediana del tiempo de seguimiento.

Lectura de datos.

ejercicio1 <- haven::read_sav("~/Master_Bioestadistica/Supervivencia/Tema_01/Datos Ejercicio 1/ejercicio1.sav")
ejercicio1 |> dim(); summary(ejercicio1)
## [1] 46  7
##     tiempos          estado          psicolog           sexo        estadio    
##  Min.   :12.00   Min.   :0.0000   Min.   :0.0000   Min.   :0.0   Min.   :1.00  
##  1st Qu.:27.25   1st Qu.:1.0000   1st Qu.:0.0000   1st Qu.:0.0   1st Qu.:1.00  
##  Median :45.50   Median :1.0000   Median :0.0000   Median :0.5   Median :2.00  
##  Mean   :55.11   Mean   :0.7826   Mean   :0.4565   Mean   :0.5   Mean   :1.87  
##  3rd Qu.:78.00   3rd Qu.:1.0000   3rd Qu.:1.0000   3rd Qu.:1.0   3rd Qu.:2.00  
##  Max.   :99.00   Max.   :1.0000   Max.   :1.0000   Max.   :1.0   Max.   :3.00  
##     tratamie          edad      
##  Min.   :1.000   Min.   :23.00  
##  1st Qu.:2.000   1st Qu.:34.00  
##  Median :2.000   Median :44.50  
##  Mean   :2.217   Mean   :42.41  
##  3rd Qu.:3.000   3rd Qu.:46.00  
##  Max.   :3.000   Max.   :67.00
head(ejercicio1)
## # A tibble: 6 × 7
##   tiempos estado psicolog  sexo       estadio       tratamie           edad
##     <dbl>  <dbl> <dbl+lbl> <dbl+lbl>  <dbl+lbl>     <dbl+lbl>         <dbl>
## 1      14      1 1 [Si]    1 [Hombre] 3 [Estadío 3] 1 [Tratamiento a]    46
## 2      13      1 0 [No]    0 [Mujer]  3 [Estadío 3] 1 [Tratamiento a]    46
## 3      12      1 0 [No]    1 [Hombre] 3 [Estadío 3] 1 [Tratamiento a]    45
## 4      23      0 0 [No]    0 [Mujer]  3 [Estadío 3] 1 [Tratamiento a]    34
## 5      23      0 0 [No]    0 [Mujer]  3 [Estadío 3] 1 [Tratamiento a]    34
## 6      24      1 0 [No]    1 [Hombre] 3 [Estadío 3] 2 [Tratamiento b]    23
ejercicio1$tiempos |> hist()

Transformación log de la función de supervivencia \(\hat{S}(t)\)

Estimo la función con KM y hago transformación para garantizar que el IC está dentro del intervalo [0,1].

KM_1 <- survival::survfit(Surv(tiempos, estado)~1, data=ejercicio1, conf.type='log')
KM_1
## Call: survfit(formula = Surv(tiempos, estado) ~ 1, data = ejercicio1, 
##     conf.type = "log")
## 
##       n events median 0.95LCL 0.95UCL
## [1,] 46     36     56      45      89
summary(KM_1)
## Call: survfit(formula = Surv(tiempos, estado) ~ 1, data = ejercicio1, 
##     conf.type = "log")
## 
##  time n.risk n.event survival std.err lower 95% CI upper 95% CI
##    12     46       1   0.9783  0.0215        0.937        1.000
##    13     45       1   0.9565  0.0301        0.899        1.000
##    14     44       1   0.9348  0.0364        0.866        1.000
##    24     39       3   0.8629  0.0522        0.766        0.971
##    25     36       2   0.8149  0.0593        0.707        0.940
##    34     34       2   0.7670  0.0647        0.650        0.905
##    35     32       2   0.7191  0.0690        0.596        0.868
##    45     30       6   0.5753  0.0762        0.444        0.746
##    46     23       2   0.5252  0.0773        0.394        0.701
##    56     21       2   0.4752  0.0776        0.345        0.655
##    67     19       3   0.4002  0.0765        0.275        0.582
##    78     15       3   0.3201  0.0739        0.204        0.503
##    89     11       3   0.2328  0.0688        0.130        0.415
##    97      7       2   0.1663  0.0632        0.079        0.350
##    99      5       3   0.0665  0.0443        0.018        0.246
Código
survminer::ggsurvplot(
  KM_1,
  conf.int = TRUE,                      # Intervalos de confianza
  conf.int.style = "ribbon",            # Sombreado para el IC
  break.time.by = 5,                    # Ejes en intervalos de 5
  risk.table = TRUE,                     # Tabla de riesgo debajo
  risk.table.height = 0.25,              # Tamaño de la tabla de riesgo
  risk.table.col = "black",              # Color del texto de la tabla de riesgo
  pval = TRUE,                           # Mostrar p-valor
  surv.median.line = "hv",               # Línea en la mediana de supervivencia
  legend.title = "T. log",   # Título de la leyenda
  legend.labs = c("Supervivencia Global"), # Etiqueta de la leyenda
  palette = c("#E63946"),                # Color principal de la curva
  xlab = "Tiempo (días)",                # Etiqueta del eje X
  ylab = "Probabilidad de Supervivencia",# Etiqueta del eje Y
  title = "Curva de Supervivencia Kaplan-Meier", # Título principal
  ggtheme = theme_minimal() +             # Estilo de la gráfica
    theme(
      plot.title = element_text(hjust = 0.5, size = 16, face = "bold"),
      axis.title = element_text(size = 14, face = "bold"),
      axis.text = element_text(size = 12),
      legend.position = "bottom",
      legend.text = element_text(size = 12)
    )
)

Valor de la mediana.

Surv(tiempos, estado == 0)
- Surv() crea un objeto de supervivencia.
- tiempos: Variable que indica el tiempo hasta el evento o censura.
- estado == 0: Define que el evento de interés ocurre cuando estado == 0 (es decir, considera 0 como el evento, en lugar del típico 1).

~ 1
- Se ajusta un modelo sin covariables, lo que significa que se estima una única curva de supervivencia para toda la población

conf.type = “log”
- Define que los intervalos de confianza se calculen en la escala logarítmica (log-log), lo cual mejora la precisión en situaciones con eventos raros o en la cola de la distribución.

Mes en el cual el 50 % de los individuos de la cohorte han experimentado el evento de interés. Indica el punto en el cual la probabilidad de que un individuo sobreviva es del 50 %.

follow_up_1 <- survival::survfit(Surv(tiempos, estado==1)~1, data=ejercicio1, conf.type="log")
follow_up_1
Call: survfit(formula = Surv(tiempos, estado == 1) ~ 1, data = ejercicio1, 
    conf.type = "log")

      n events median 0.95LCL 0.95UCL
[1,] 46     36     56      45      89

Supervivencia a los 20 meses.

t_objetivo <- 20
idx <- which(KM_1$time == t_objetivo)
idx_before <- max(which(KM_1$time <= t_objetivo))
idx_after <- min(which(KM_1$time >= t_objetivo))

t_before <- KM_1$time[idx_before]
t_after <- KM_1$time[idx_after]
S_t_before <- KM_1$surv[idx_before]
S_t_after <- KM_1$surv[idx_after]

# Interpolación lineal
if (t_before == t_after) {
  S_t <- S_t_before
} else {
  S_t <- S_t_before + (S_t_after - S_t_before) * (t_objetivo - t_before) / (t_after - t_before)
}

S_t
[1] 0.9347826
summary(KM_1, times = 20, extend = TRUE) 
Call: survfit(formula = Surv(tiempos, estado) ~ 1, data = ejercicio1, 
    conf.type = "log")

 time n.risk n.event survival std.err lower 95% CI upper 95% CI
   20     43       3    0.935  0.0364        0.866            1
S_t <- 0.935
se_Greenwood <- 0.0364

set.seed(12345)
simu_normal <- rnorm(1000, mean = S_t, sd = se_Greenwood)

hist(simu_normal, breaks=30, probability = TRUE, col = "blue", 
     main = paste("Aproximación Normal para S(", t_objetivo, ")", sep = ""),
     xlab = "S(t)", ylab = "Densidad")

curve(dnorm(x, mean = S_t, sd = se_Greenwood), col = "red", lwd = 2, add = TRUE)
abline(v = S_t, col = "black", lty = 2)  # estimación de S(t)

cat("S(8):", S_t, "\nError estándar de Greenwood:", se_Greenwood, "\n")
S(8): 0.935 
Error estándar de Greenwood: 0.0364 

Mediana del tiempo de seguimiento.

Independiente al evento de interés, relacionado con la duración del seguimiento de los pacientes. Tiempo medio en el que los individuos fueron seguidos antes de que terminara el estudio o se censuraran.

median(ejercicio1$tiempos)
[1] 45.5
follow_up <- survival::survfit(Surv(tiempos, estado==0)~1, data=ejercicio1, conf.type="log")
follow_up
Call: survfit(formula = Surv(tiempos, estado == 0) ~ 1, data = ejercicio1, 
    conf.type = "log")

      n events median 0.95LCL 0.95UCL
[1,] 46     10     99      99      NA

Solventamos el problema de las colas (no sé qué problema es. ah, el del extremo del intervalo) para el tiempo mediano de seguimiento: cambiando el 1 por el 0 si tenemos muchos empates. entonces la estimacion de la mediana del tiempo de seguimiento mejora.

library(prodlim)
quantile(prodlim(Hist(tiempos, estado)~1,data=ejercicio1,reverse=TRUE))
Quantiles of the potential follow up time distribution based on the Kaplan-Meier method 
applied to the censored times reversing the roles of event status and censored.

Table of quantiles and corresponding confidence limits:
       q quantile lower upper
   <num>    <num> <num> <num>
1:  0.00       NA    NA    NA
2:  0.25       NA    NA    NA
3:  0.50       NA    89    NA
4:  0.75       78    67    NA
5:  1.00       23    23    23


Median with interquartile range (IQR):
    Median (IQR)
          <char>
1: NA (78.00;NA)

es bastante posible que no se alcance la mediana porque el 50 % de los individuos no llegan a alcanzar el evento. el 75 % de los individuos se les sigue 78 meses y ya, se les pierde el seguimiento. en muchas ocasiones no puedo aportar la mediana si el pronóstico es bueno.

en este caso se podría criticar el experimento diciendo que el tiempo de seguimiento es muy corto.