10 Overlevelsesanalyse
Eksempel: Lungecancer uge 9.
Se også forelæsningerne i Introduktion til overlevelsesanalyse uge 9 (find link fra forside på Absalon), hvor der ligger nogle videoer om overlevelsesanalyse i R.
I en overlevelsesanalyse består outcome-variablen af to elementer:
- En variabel der beskriver overlevelsestiden (her
time
) - En variabel der beskriver om observationen af overlevelsestiden er et event/dødsfald eller en censurering (her
status
)
Afhængigt af hvilken pakke / funktioner man bruger skal man klistre disse to variable sammen i et survival-outcome med
Surv(time, status)
hvis funktioner frasurvival
ellertimereg
-pakken benyttes.Hist(time,status)
hvis funktioner fraprodlim
-pakken benyttes.
Det er selvfølgelig lidt besværligt at man skal angive sit outcome forskelligt afhængigt af hvilken pakke man bruger - men vi bruger her kun prodlim
-pakken til at tegne lidt pænere Kaplan-Meier kurver, så udgangspunktet er at vi skal angive outcome som Surv(time,status)
(og gør vi det galt, skal R nok brokke sig …).
10.1 Kaplan-Meier kurver
For at lave Kaplan-Meier kurver kan man enten benytte funktionen survfit()
fra survival
-pakken eller prodlim()
fra prodlim
-pakken og herefter benytte funktionen plot()
.
Benyttes prodlim()
tilføjes der automatisk konfidenslinjer. Benyttes survfit()
er det også muligt at tilføje konfidenslinjer ved at angive conf.int=T
i plot()
-funktionen, men figuren bliver ikke lige så pæn som ved prodlim()
.
library( survival )
<- survfit( Surv(time, status) ~ treat, data=d )
km1 plot( km1 )
library(prodlim)
<- prodlim( Hist(time, status) ~ treat, data=d )
km2 plot( km2 )
10.1.1 Kumulative incidenser
For at plotte kumulativ incidens kan man igen benytte plot()
på et prodlim
-objekt, men nu med argument type = "cuminc"
.
plot( km2, type = "cuminc")
10.1.2 Kumulerede hazard funktioner
Vi plotter de kumulerede hazard funktioner som funktion af tid, og vurderer om kurverne er proportionale da vi i så fald har proportionale hazards og kan fortsætte med en Cox-regression.
Her angiver vi fun = "cumhaz"
. For at give kurverne forskellige farver tiltøjes col = 1:2
, da der er to grupper. Derefter kan vi tilføje en forklaring til figuren med legend
-funktionen.
plot( km1, fun = "cumhaz", col = 1:2 )
legend( "bottomright", c("0","1"), inset = .01, title = "Treatment", lty = 1, col = 1:2)
10.1.3 Log-rank test
Log-rank testet bruges til at teste, om overlevelseskurverne er ens. Hertil benyttes survdiff
-functionen.
P-værdien aflæses nederst i outputtet (p= 0.90, dvs der er ikke tegn på at kurverne er forskellige)
survdiff( Surv(time, status) ~ treat, data = d)
Call:
survdiff(formula = Surv(time, status) ~ treat, data = d)
N Observed Expected (O-E)^2/E (O-E)^2/V
treat=0 69 64 64.5 0.00388 0.00823
treat=1 68 64 63.5 0.00394 0.00823
Chisq= 0 on 1 degrees of freedom, p= 0.9
10.2 Cox proportional hazards model
For at opstille en Cox Proportional Hazards model benyttes funktionen coxph()
.
Som i en lineær model med lm
-funktionen angives relationen mellem outcome (Surv(time, status)
) og kovariater som en formel, hvor ~
adskiller outcome fra kovariater, og kovariater tilføjes med +
, dvs.
\[\text{outcome} \sim \text{kovariat1} + \text{kovariat2} + \text{kovariat3}\]
Interaktioner mellem kovariater kan angives i formlen med *
eller :
som vi plejer.
Nedenfor angiver vi en Cox Proportional Hazards model med treat
, celltype
og karno
som kovariater uden interaktioner.
<- coxph( Surv(time, status) ~ factor(treat) + celltype + karno,
cox.mod1 data = d )
Parameterestimater fås derefter med summary()
-funktionen. Estimater for hazard-ratio aflæses under exp(coef)
og konfidensintervaller for exp(coef)
aflæses under lower .95
og upper .95
. P-værdien aflæses under Pr(>|z|)
summary( cox.mod1 )
Her er summary()
så venlig også at beregne CI for os, så vi behøver ikke at benytte confint()
(som dog også virker her).
10.2.1 Test
Overordnet test for association for hver variabel i en Cox model, kan laves med drop1()
, hvor vi angiver Cox modellen samt test = "Chisq"
.
drop1( cox.mod1, test = "Chisq")
Single term deletions
Model:
Surv(time, status) ~ factor(treat) + celltype + karno
Df AIC LRT Pr(>Chi)
<none> 960
factor(treat) 1 960 1.7 0.19263
celltype 3 972 18.1 0.00042 ***
karno 1 993 35.2 3e-09 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Vi kan teste hypoteser om flere parametre i en Cox model simultant vha. linearHypothesis
-funktionen fra car
-pakken. Her angives Cox modellen samt hypoteserne, der skal testes. I eksemplet nedenfor tester vi om koefficienterne hørende til large
, smallcell
og squamous
for variablen celltype
alle er 0 (et test med 3 frihedsgrader da vi sætter 3 koefficienter til 0). Dette er altså det samme som det overordnede test for effekt af celltype
, men man får en lidt anden p-værdi fordi der benyttes en anden metode til beregning af p-værdien
library(car)
linearHypothesis( cox.mod1, c("celltypelarge = 0",
"celltypesmallcell = 0",
"celltypesquamous = 0"))
Linear hypothesis test
Hypothesis:
celltypelarge = 0
celltypesmallcell = 0
celltypesquamous = 0
Model 1: restricted model
Model 2: Surv(time, status) ~ factor(treat) + celltype + karno
Res.Df Df Chisq Pr(>Chisq)
1 135
2 132 3 17.5 0.00056 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
10.2.2 Modelkontrol: Proportional Hazards
For alle kovariater skal antagelsen om proportional hazards tjekkes. Det kan gøres grafisk såvel som ved et test.
Nedenfor beskrives først den ‘gammeldags’ metode, som kan benyttes for kvalitative forklarende variable (kvantitative kan checkes på tilsvarende vis ved inddeling af den kvantative kovariat i grupper).
Dernæst beskrives en mere moderne metode baseret på de såkaldte Score-residualer. Denne metode kan benyttes på både kvantitative og kvalitative variable.
10.2.2.1 Kvalitative forklarende variable
Man kan lave en grafisk kontrol af antagelse om proportionale hazards mellem grupper for en kvalitativ variabel ved at tjekke parallelitet af de kumulerede hazard-funktioner på loglog-skala.
Dette kan gøres på følgende måde:
Først fittes en coxph
-model, hvor variablen, vi ønsker at tjekke proportionale hazards for, sættes i strata()
(se kode nedenfor).
Hvis der er øvrige kovariater i modellen ud over variablen i strata
, laver vi et datasæt med en enkelt, vilkårlig værdi af hver af disse variabler. Dette gøres med data.frame()
.
Hernæst laver vi et surfit
-objekt med surfit()
-funktionen. Her angiver vi vores Cox model, samt det nye datasæt i newdata =
.
Dette survfit
-objekt kan vi benytte til at lave plot af kumumlerede hazard funktioner på loglog-skala med plot()
-funktionen, hvor vi angiver fun = "cloglog"
.
I eksemplet nedenfor har vi valgt at tjekke proportional hazards mellem grupperne i variablen treat
. Proceduren gentages for de øvrige kvalitative variable en af gangen, hvor disse angives i strata
.
<- coxph( Surv(time,status) ~ strata(treat) + celltype + karno, data = d )
cox.mod2 = data.frame( celltype = "squamous", karno = 50) # Vaelg vilkaarlig vaerdi for celltype og karno
newD = survfit( cox.mod2, newdata = newD )
survfit2
plot( survfit2, fun = "cloglog", ylab = "log(-log(S))", col = 1:2, xlab = "time")
legend("bottomright", c("0","1"), lty = 1, col = 1:2, title = "Treatment", inset = .01)
10.2.2.2 Kvalitative og kvantitative kovariater
En anden måde at tjekke proportionale rater grafisk er vha. Score-residualer. Til dette benytter vi cox.aalen()
-funktionen i timreg
-pakken.
I cox.aalen()
skal alle kovariater med antagelse om proportionalitet angives i prop()
(se kode nedenfor).
Med plot()
kan vi plotte de kumulerede Score-residualer ved at angive score=T
. Derved får ét plot for hver parameter med antagelse om proportionalitet i modellen. Ved at angive specific.comps =
, kan man kontrollere hvilke af figurerne, man vil have vist.
Da cox.aalen()
benytter nogle tilfældige simulationer, kan vi sikre, at vi får samme resultat, hver gang vi kører funktionen på den samme model, ved at angive et såkald seed i set.seed()
. Her angives et tilfældigt tal. Hver gang set.seed()
køres inden cox.aalen
, får man så det samme resultat. Her skal man vurdere, om den sorte (observerede) kurve stemmer overens med de grå (simulerede) kurver.
library( timereg )
set.seed( 123 ) # Valgfrit tal
par(mfrow=c(3,2))
<- cox.aalen( Surv(time,status) ~ prop( factor(treat)) + prop(celltype) + prop(karno), data = d)
ca1 plot( ca1, score = T )
Som en hjælp til at vurdere, om proportional hazards er opfyldt kan vi få en p-værdi frem ved at benytte summary()
på cox.aalen
modellen defineret ovenfor.
P-værdier aflæses under p-value H_0
.
summary( ca1 )
I princippet kan vi klare os med p-værdierne for at vurdere proportional hazards antagelsen - men det er en god idé at se på plottene for at få en idé om, hvor en eventuel afvigelse er.
10.2.3 Modelkontrol: Linearitet
Antagelsen om linearitet på log-hazard skala kan tjekkes ved brug af kumulerede martingal residualer. Martingal residualerne fås ved at lave en cox.aalen
-model, hvor vi angiver residuals=1
og n.sim=0
.
Hernæst benyttes cum.residuals()
med cum.resid=1
for at få de kumulerede residualer.
Test for ikke-linearitet laves da vha. summary()
og aflæses under p-value H_0: B(t)=0
.
set.seed( 345 )
<- cox.aalen( Surv(time,status) ~ prop(factor(treat)) + prop(celltype) + prop(karno),
ca3 data=d, residuals=1, n.sim=0 )
<- cum.residuals( ca3, cum.resid=1, data=d )
resids summary(resids)
Desuden kan vi plotte residualerne ved at sætte score=2
plot( resids, score=2 )
10.2.4 Tidsafhængige kovariater
I en Cox proportional hazards model (coxph()
) er det muligt at lade effekten af en variabel ændre sig over tid.
I formellinjen sættes tt()
omkring den variabel, der skal afhænge af tiden, og selve tidstransformationen variablen angives i argumentet tt=
som en funktion (tt = function(k,t,...){}
). Her repræsenterer k
variablen, der afhænger af tiden, og t
repræsenterer tid.
I eksemplet nedenfor lader vi effekten kovariaten karno
være opdelt i 4 tidsintervaller (\(t\leq23\), \(t\in(23,62]\), \(t\in(62,147.5]\) og \(t>147.5\)). Det betyder altså, at vi skal rapportere fire forskellige HR’er - en for hvert tidsinterval.
I funktionsudtrykket i tt=
, laver vi med cbind()
en vektor med 4 indgange, hvor hver indgang tager værdien \(k\), netop hvis \(t\) er i det interval, som svarer til den indgang, og ellers antager den værdien 0. Det skyldes at fx. (t<=23)=1
hvis \(t\leq23\) og 0 ellers, og derfor er k*(t<=23) = k
hvis \(t\leq23\) og 0 ellers.
Der kan derfor kun være én indgang ad gangen, der er forskellig fra 0. På den måde opdeles effekten af karno
i 4 tidsintervaller.
I summary()
på modellen kan vi se, at der er 4 parametre for tt(karno)
svarende til en for hvert tidsinterval.
<- coxph( Surv(time,status) ~ factor(treat) + celltype + tt(karno),
cox.mod3 data = d,
tt = function(k, t, ...) { cbind( k*(t<=23),
*(t>23 & t<=62),
k*(t>62 & t<=147.5),
k*(t>147.5)) }
k
)summary(cox.mod3)
Call:
coxph(formula = Surv(time, status) ~ factor(treat) + celltype +
tt(karno), data = d, tt = function(k, t, ...) {
cbind(k * (t <= 23), k * (t > 23 & t <= 62), k * (t > 62 &
t <= 147.5), k * (t > 147.5))
})
n= 137, number of events= 128
coef exp(coef) se(coef) z Pr(>|z|)
factor(treat)1 0.08422 1.08787 0.20516 0.41 0.68143
celltypelarge -0.76177 0.46684 0.30110 -2.53 0.01141 *
celltypesmallcell -0.21004 0.81055 0.26795 -0.78 0.43311
celltypesquamous -1.09673 0.33396 0.30059 -3.65 0.00026 ***
tt(karno)1 -0.05179 0.94953 0.00928 -5.58 2.4e-08 ***
tt(karno)2 -0.03656 0.96410 0.00911 -4.01 6.0e-05 ***
tt(karno)3 -0.00770 0.99233 0.01218 -0.63 0.52756
tt(karno)4 -0.00310 0.99691 0.01284 -0.24 0.80920
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
exp(coef) exp(-coef) lower .95 upper .95
factor(treat)1 1.088 0.919 0.728 1.626
celltypelarge 0.467 2.142 0.259 0.842
celltypesmallcell 0.811 1.234 0.479 1.370
celltypesquamous 0.334 2.994 0.185 0.602
tt(karno)1 0.950 1.053 0.932 0.967
tt(karno)2 0.964 1.037 0.947 0.981
tt(karno)3 0.992 1.008 0.969 1.016
tt(karno)4 0.997 1.003 0.972 1.022
Concordance= 0.752 (se = 0.023 )
Likelihood ratio test= 74.7 on 8 df, p=6e-13
Wald test = 73.5 on 8 df, p=1e-12
Score (logrank) test = 82.5 on 8 df, p=2e-14