05-Análisis de supervivència

Author

P. Loma-Osorio

Published

Invalid Date

Análisi de supervivència

L’analisi de supervivència es una branca de l’estadística que s’utilitza per estudiar el temps que passa fins que ocorre un esdeveniment. També se’n diu anàlisi de temps a l’esdeveniment.

Les aplicacions clíniques habituals son l’anàlisi del temps fins a la mort, recidiva, ingres hospitalàri o perdua de seguiment d’un pacient.

El concepte de censura

En estudis reals, sovint no coneixem el temps exacte de l’esdeveniment per a tothom, ni tothom comença i acaba un estudi al mateix temps. Per fer calculs amb els seguiments dels que se disposa, existeix el concepte de censura.

📌 Censura per la dreta vol dir que sabem que l’esdeveniment no ha passat fins a cert moment, però no sabem què passa després.


🔍 Causes habituals de censura per la dreta

Un subjecte pot estar censurat per diversos motius:

  • Pèrdua de seguiment
  • Retirada voluntària de l’estudi
  • Final del període d’estudi sense que hagi passat l’esdeveniment

Aquests són casos de censura per la dreta, que és la més habitual. Existeixen censura per l’esquerra o per intervals que no tractarem en aquest curs.


Exemple gràfic: temps fins a esdeveniment o censura

Per il·lustrar la censura i la supervivència, observem la següent figura:

Com interpretar aquest gràfic?

Pregunta: quina proporció de pacients es mantenen lliures de l’esdeveniment al cap de 10 anys?

  • Pacients 6 i 7: encara no han tingut l’esdeveniment a l’any 10 → lliures d’esdeveniment
  • Pacients 2, 9 i 10: han tingut l’esdeveniment abans de l’any 10
  • Pacients 1, 3, 4, 5 i 8: han estat censurats abans de l’any 10 → no sabem si han tingut l’esdeveniment o no

📌 Tot i que no sabem què ha passat amb els censurats, sí sabem que han estat seguits durant un temps determinat sense l’esdeveniment.


Com han de ser les dades?

Per fer una anàlisi de supervivència en R, necessitem un conjunt de dades que inclogui com a mínim:

Variable Descripció
temps Temps de seguiment per a cada subjecte (pot estar en dies, mesos, anys…)
event Indicador de si ha passat l’esdeveniment (1) o si és censurat (0)

📌 Aquestes dues variables s’utilitzen per construir un objecte especial de tipus Surv(), que encapsula tota la informació necessària per als mètodes de supervivència: el temps, l’estat de censura i (més endavant) grups comparatius.


ℹ️ Què és un objecte Surv()?

Un objecte Surv() és un format especial que R utilitza per representar dades de supervivència.
Conté, per a cada cas:

  • el temps de seguiment
  • un indicador de si l’esdeveniment ha passat (1) o està censurat (0)

Aquest objecte és imprescindible per poder fer servir funcions com survfit(), coxph() o survdiff() del paquet survival.


Preparació de les dades

Abans de calcular el temps, cal assegurar-se que les dates estiguin en format correcte. A R això pot ser complicat, però hi ha un paquet molt útil per treballar amb dates:

lubridate és un paquet de la família tidyverse que facilita molt la conversió de textos en dates, així com càlculs, extracció d’anys, mesos, etc.

Per exemple, ymd("2020-01-01") crea una data en format “any-mes-dia”.


Exemple amb 5 pacients

library(lubridate)

S'està adjuntant el paquet: 'lubridate'
Els següents objectes estan emmascarats des de 'package:base':

    date, intersect, setdiff, union
library(survival)

ingres <- ymd(c("2020-01-01", "2020-02-15", "2020-03-01", "2020-01-20", "2020-01-10"))
alta   <- ymd(c("2020-01-12", "2020-03-05", "2020-03-20", "2020-01-28", "2020-01-25"))
event  <- c(1, 0, 1, 1, 0)

temps <- as.numeric(alta - ingres)

dades <- data.frame(subjecte = 1:5, ingres, alta, temps, event)
dades
  subjecte     ingres       alta temps event
1        1 2020-01-01 2020-01-12    11     1
2        2 2020-02-15 2020-03-05    19     0
3        3 2020-03-01 2020-03-20    19     1
4        4 2020-01-20 2020-01-28     8     1
5        5 2020-01-10 2020-01-25    15     0

Creació de l’objecte Surv()

surv_obj <- Surv(time = temps, event = event)
surv_obj
[1] 11  19+ 19   8  15+
plot(surv_obj)

Ara ja tenim un objecte Surv preparat per utilitzar-lo en l’estimació de corbes de supervivència amb survfit().


🏥 Exemple aplicat: dades de pacients amb càncer de pulmó

Treballarem amb el conjunt de dades lung, que forma part del paquet survival de R.

Aquest dataset prové d’un assaig clínic realitzat per la Veterans Administration i recull informació de pacients amb càncer de pulmó, incloent:

  • Temps de seguiment (en dies)
  • Estat vital (mort o censurat)
  • Sexe
  • Edat
  • Tractament

📦 El dataset està disponible automàticament quan carreguem el paquet survival.


Càrrega de dades i inspecció inicial

library(survival)

data(lung)   # Carrega el dataset
Warning in data(lung): data set 'lung' not found
head(lung)   # Mostra les primeres files
  inst time status age sex ph.ecog ph.karno pat.karno meal.cal wt.loss
1    3  306      2  74   1       1       90       100     1175      NA
2    3  455      2  68   1       0       90        90     1225      15
3    3 1010      1  56   1       0       90        90       NA      15
4    5  210      2  57   1       1       90        60     1150      11
5    1  883      2  60   1       0      100        90       NA       0
6   12 1022      1  74   1       1       50        80      513       0

Neteja i recodificació bàsica

  • status indica si el pacient ha mort (2) o està censurat (1)
  • sex està codificat com 1 = home, 2 = dona
lung <- lung %>%
  mutate(
    status = ifelse(status == 2, 1, 0),  # 1 = mort, 0 = censurat
    sex = factor(sex, levels = c(1, 2), labels = c("Home", "Dona"))
  )

table(lung$status)

  0   1 
 63 165 
table(lung$sex)

Home Dona 
 138   90 

Creació de l’objecte Surv()

surv_lung <- Surv(time = lung$time, event = lung$status)
head(surv_lung)
[1]  306   455  1010+  210   883  1022+

Ara tenim preparades les dades per a estimar la funció de supervivència.
Continuarem amb la corba de Kaplan-Meier i la seva representació visual.

Estimació de la funció de supervivència (Kaplan-Meier)

Ara que tenim el nostre objecte Surv() creat, podem estimar la funció de supervivència amb la funció survfit().


Estimació global (tots els pacients)

km_total <- survfit(surv_lung ~ 1, data = lung)
summary(km_total)$table
  records     n.max   n.start    events     rmean se(rmean)    median   0.95LCL 
228.00000 228.00000 228.00000 165.00000 376.27475  19.70779 310.00000 285.00000 
  0.95UCL 
363.00000 

📈 Visualització de la corba de supervivència

Per representar la corba utilitzarem el paquet survminer, que facilita gràfics clars i publicables.

library(survminer)
S'està carregant el paquet requerit: ggplot2
S'està carregant el paquet requerit: ggpubr

S'està adjuntant el paquet: 'survminer'
L'objecte següent està emmascarat per 'package:survival':

    myeloma
ggsurvplot(km_total,
           conf.int = TRUE,
           xlab = "Dies de seguiment",
           ylab = "Probabilitat de supervivència",
           title = "Corba de supervivència (Kaplan-Meier)",
           surv.median.line = "hv")

La línia de punts mostra la mediana de supervivència estimada.


Comparació per sexe

Podem estimar i visualitzar la supervivència segons el sexe:

km_sexe <- survfit(surv_lung ~ sex, data = lung)

ggsurvplot(km_sexe,
           data = lung,
           conf.int = TRUE,
           pval = TRUE,
           risk.table = TRUE,
           xlab = "Dies de seguiment",
           ylab = "Supervivència estimada",
           title = "Supervivència segons el sexe",
           legend.labs = c("Home", "Dona"))

Aquest gràfic ens permet observar si hi ha diferències clíniques rellevants i si són estadísticament significatives (valor p del test de log-rank).


⚖️ Comparació de grups: test de log-rank

Un cop hem representat les corbes de supervivència per grups (per exemple, per sexe), volem saber si la diferència observada és estadísticament significativa.

El test de log-rank (o test de Mantel-Haenszel) és la prova estàndard per comparar dues o més corbes de Kaplan-Meier.

Què comprova el test?

Comprova la hipòtesi nul·la que les funcions de supervivència són iguals entre grups. És a dir:

\[ H_0: S_1(t) = S_2(t) \quad \text{per a tot } t \]

Si el valor p és petit, rebutgem ( H_0 ) → hi ha diferències estadísticament significatives entre grups

  • Si és gran, no podem afirmar que hi hagi diferències

Aplicació amb survdiff()

logrank <- survdiff(surv_lung ~ sex, data = lung)
logrank
Call:
survdiff(formula = surv_lung ~ sex, data = lung)

           N Observed Expected (O-E)^2/E (O-E)^2/V
sex=Home 138      112     91.6      4.55      10.3
sex=Dona  90       53     73.4      5.68      10.3

 Chisq= 10.3  on 1 degrees of freedom, p= 0.001 

La sortida ens dona un estadístic de tipus \(\chi^2\) amb els graus de llibertat i el p-valor associat.

Model de Cox (riscos proporcionals)

El model de Cox ens permet estimar l’efecte de diverses variables (edat, sexe, tractament…) sobre el risc instantani de tenir l’esdeveniment (mort, recidiva, etc.).

És l’equivalent de la regressió logística per a dades de supervivència, però tenint en compte el temps de seguiment.


Què fa el model?

Modelitza el hazard (risc instantani de morir) en funció de predictors:

\(h(t) = h_0(t) \cdot \exp(\beta_1 X_1 + \beta_2 X_2 + \dots)\)

  • h_0(t): risc basal no especificat (flexible)
  • \(\exp(\beta_i)\) : hazard ratio (HR) associat a cada variable

⚙️ Ajust del model amb coxph()

cox_model <- coxph(surv_lung ~ sex + age, data = lung)
summary(cox_model)
Call:
coxph(formula = surv_lung ~ sex + age, data = lung)

  n= 228, number of events= 165 

             coef exp(coef)  se(coef)      z Pr(>|z|)   
sexDona -0.513219  0.598566  0.167458 -3.065  0.00218 **
age      0.017045  1.017191  0.009223  1.848  0.06459 . 
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

        exp(coef) exp(-coef) lower .95 upper .95
sexDona    0.5986     1.6707    0.4311    0.8311
age        1.0172     0.9831    0.9990    1.0357

Concordance= 0.603  (se = 0.025 )
Likelihood ratio test= 14.12  on 2 df,   p=9e-04
Wald test            = 13.47  on 2 df,   p=0.001
Score (logrank) test = 13.72  on 2 df,   p=0.001

Interpretació dels coeficients

Els coeficients s’interpreten com a hazard ratios:

  • Si ( HR > 1 ): el factor augmenta el risc
  • Si ( HR < 1 ): el factor redueix el risc
  • Si ( HR = 1 ): no té efecte

Per veure els HR i els seus intervals de confiança:

exp(coef(cox_model))          # HR
 sexDona      age 
0.598566 1.017191 
exp(confint(cox_model))       # IC 95% dels HR
            2.5 %    97.5 %
sexDona 0.4310936 0.8310985
age     0.9989686 1.0357467

Exemple d’interpretació

Un HR de 1.5 per a sexe = home indica que els homes tenen un risc un 50% més alt de morir que les dones, ajustant per edat.


📌 El model de Cox és útil per:

  • Ajustar per múltiples variables
  • Interpretar l’efecte de factors en termes de risc
  • Afegir interaccions, variables categòriques i contínues

🧪 Verificació de l’assumpció de riscos proporcionals

El model de Cox assumeix que l’efecte de cada predictor és constant en el temps. És a dir:

El hazard ratio (HR) per a cada variable no canvia al llarg del seguiment.

Això s’anomena l’assumpció de riscos proporcionals.


Què passa si no es compleix?

Si l’HR canvia amb el temps (per exemple, un tractament només és efectiu al principi), el model pot ser inadequat o enganyós.


Test estadístic amb cox.zph()

test_proporcionalitat <- cox.zph(cox_model)
test_proporcionalitat
       chisq df    p
sex    2.608  1 0.11
age    0.209  1 0.65
GLOBAL 2.771  2 0.25
  • Comprova si hi ha interacció entre el predictor i el temps
  • Si el valor p és petit (< 0.05), es viola l’assumpció

Gràfic de residuals de Schoenfeld

plot(test_proporcionalitat)

  • Si la línia del predictor s’allunya clarament de la línia horitzontal, pot haver-hi violació
  • Les línies de punts mostren el 95% IC del test

✅ Interpretació

Resultat Què vol dir
p > 0.05 L’assumpció de riscos proporcionals es compleix
p < 0.05 Podria haver-hi un efecte no constant en el temps

📌 Si es detecta violació, es poden considerar models amb efectes dependents del temps (nivell més avançat) o estratificar per aquella variable.


🧩 Resum del capítol de supervivència

En aquest capítol hem après a treballar amb dades de temps fins a esdeveniment, i hem vist com analitzar-les i representar-les correctament.


Què hem après?

Eina / concepte Què permet fer
Surv() Codificar temps i censura per a cada individu
survfit() + ggsurvplot() Estimar i visualitzar corbes de supervivència
survdiff() Comparar grups amb el test de log-rank
coxph() Ajustar un model amb múltiples predictors
cox.zph() Comprovar l’assumpció de riscos proporcionals

Bones pràctiques

  • Validar la censura: no oblidar que censurar no vol dir “no ha passat”, sinó “no sabem si ha passat”
  • Revisar supòsits: tant amb log-rank com amb Cox
  • Interpretar amb context clínic: un HR de 2 pot ser rellevant… o no, segons el cas