This is only based on observed death as they unfold in time.
library(ISLR2)
library(survival)
attach(BrainCancer)
options(max.print = 50)
fit.surv <- survfit(Surv(time, status) ~ 1)
plot(fit.surv, xlab = "Months", ylab = "Estimated Probability of Survival", lwd = 3)
To see the difference between male and female:
fit.sex <- survfit(Surv(time, status) ~ sex)
plot(fit.sex, xlab = "Months", ylab = "Estimated Probability of Survival", col = c(2,4), lwd = 3)
legend("bottomleft", levels(sex), col = c(2,4), lty = 1, lwd = 3 )
we can perform a log-rank test to compare the survival of males to females.
logrank.test <- survdiff(Surv(time, status) ~ sex)
logrank.test
## Call:
## survdiff(formula = Surv(time, status) ~ sex)
##
## N Observed Expected (O-E)^2/E (O-E)^2/V
## sex=Female 45 15 18.5 0.676 1.44
## sex=Male 43 20 16.5 0.761 1.44
##
## Chisq= 1.4 on 1 degrees of freedom, p= 0.2
P value is 0.2, so there is no significant difference between male and female.
we fit Cox proportional hazards models. Regardless of which test we use, we see that there is no clear evidence for a difference in survival between males and females.
fit.cox <- coxph(Surv(time, status) ~ sex)
summary(fit.cox)
## Call:
## coxph(formula = Surv(time, status) ~ sex)
##
## n= 88, number of events= 35
##
## coef exp(coef) se(coef) z Pr(>|z|)
## sexMale 0.4077 1.5033 0.3420 1.192 0.233
##
## exp(coef) exp(-coef) lower .95 upper .95
## sexMale 1.503 0.6652 0.769 2.939
##
## Concordance= 0.565 (se = 0.045 )
## Likelihood ratio test= 1.44 on 1 df, p=0.2
## Wald test = 1.42 on 1 df, p=0.2
## Score (logrank) test = 1.44 on 1 df, p=0.2
summary(fit.cox)$logtes
## test df pvalue
## 1.438822 1.000000 0.230330
summary(fit.cox)$waldtest
## test df pvalue
## 1.4200000 1.0000000 0.2332618
summary(fit.cox)$sctest
## test df pvalue
## 1.4404951 1.0000000 0.2300592
As we learned in this chapter, the score test from the Cox model is exactly equal to the log rank test statistic!
logrank.test$chisq
## [1] 1.440495
Now we fit a model that makes use of additional predictors.
fit.all <- coxph(Surv(time, status) ~ sex + diagnosis + loc + ki + gtv + stereo)
fit.all
## Call:
## coxph(formula = Surv(time, status) ~ sex + diagnosis + loc +
## ki + gtv + stereo)
##
## coef exp(coef) se(coef) z p
## sexMale 0.18375 1.20171 0.36036 0.510 0.61012
## diagnosisLG glioma 0.91502 2.49683 0.63816 1.434 0.15161
## diagnosisHG glioma 2.15457 8.62414 0.45052 4.782 1.73e-06
## diagnosisOther 0.88570 2.42467 0.65787 1.346 0.17821
## locSupratentorial 0.44119 1.55456 0.70367 0.627 0.53066
## ki -0.05496 0.94653 0.01831 -3.001 0.00269
## gtv 0.03429 1.03489 0.02233 1.536 0.12466
## stereoSRT 0.17778 1.19456 0.60158 0.296 0.76760
##
## Likelihood ratio test=41.37 on 8 df, p=1.776e-06
## n= 87, number of events= 35
## (1 observation deleted due to missingness)
The diagnosis variable has been coded so that the baseline corresponds to meningioma. The results indicate that the risk associated with HG glioma is more than eight times (i.e. e^2.15 = 8.62) the risk associated with meningioma.
Finally, we plot survival curves for each diagnosis category, adjusting for the other predictors.
modaldata <- data.frame( diagnosis = levels(diagnosis),
sex = rep("Female", 4),
loc = rep("Supratentorial", 4),
ki = rep(mean(ki), 4),
gtv = rep(mean(gtv), 4),
stereo = rep("SRT", 4)
)
survplots <- survfit(fit.all, newdata = modaldata)
plot(survplots, xlab = "Months", ylab = "Estimated Probability of Survival", col = 3:6, lwd = 3 )
legend("bottomleft", levels(diagnosis), col = 3:6, lty = 1, lwd = 3 )
fit.posresp <- survfit(Surv(time, status) ~ posres, data = Publication)
plot(fit.posresp, xlab = "Months", ylab = "Probability of not being published", col = 3:4, lwd = 3)
legend("topright", c("Negative Result","Positive Result"), col = 3:4, lty = 1, lwd = 3)
The p-values from fitting Cox’s proportional hazards model to the posres variable are quite large, providing no evidence of a difference in time-to-publication between studies with positive versus negative results.
fit.pub <- coxph(Surv(time, status) ~ posres, data = Publication)
fit.pub
## Call:
## coxph(formula = Surv(time, status) ~ posres, data = Publication)
##
## coef exp(coef) se(coef) z p
## posres 0.1481 1.1596 0.1616 0.916 0.36
##
## Likelihood ratio test=0.83 on 1 df, p=0.3611
## n= 244, number of events= 156
p-values from fitting Cox’s proportional hazards model to the posres variable are quite large, providing no evidence of a difference in time-to-publication between studies with positive versus negative results.
logrank.test <- survdiff(Surv(time, status) ~ posres, data = Publication)
logrank.test
## Call:
## survdiff(formula = Surv(time, status) ~ posres, data = Publication)
##
## N Observed Expected (O-E)^2/E (O-E)^2/V
## posres=0 146 87 92.6 0.341 0.844
## posres=1 98 69 63.4 0.498 0.844
##
## Chisq= 0.8 on 1 degrees of freedom, p= 0.4
Same results for log-rank test.
However, the results change dramatically when we include other predictors in the model. Here we have excluded the funding mechanism variable.
fit.pub2 <- coxph(Surv(time, status) ~ . -mech, data = Publication)
fit.pub2
## Call:
## coxph(formula = Surv(time, status) ~ . - mech, data = Publication)
##
## coef exp(coef) se(coef) z p
## posres 5.708e-01 1.770e+00 1.760e-01 3.244 0.00118
## multi -4.086e-02 9.600e-01 2.512e-01 -0.163 0.87079
## clinend 5.462e-01 1.727e+00 2.620e-01 2.085 0.03710
## sampsize 4.678e-06 1.000e+00 1.472e-05 0.318 0.75070
## budget 4.385e-03 1.004e+00 2.465e-03 1.779 0.07518
## impact 5.832e-02 1.060e+00 6.676e-03 8.735 < 2e-16
##
## Likelihood ratio test=149.2 on 6 df, p=< 2.2e-16
## n= 244, number of events= 156
We see that there are a number of statistically significant variables, including whether the trial focused on a clinical endpoint, the impact of the study, and whether the study had positive or negative results.
There are three covariates: Operators (the number of call center operators available at the time of the call, which can range from 5 to 15), Center (either A, B, or C), and Time of day (Morning, Afternoon, or Evening).
The model.matrix() function is used in many regression packages for building an X matrix from data. Not only does it produce a matrix corresponding to the predictors but it also automatically transforms any qualitative variables into dummy variables. The -1 omits the intercept.
set.seed(4)
N <- 2000
Operators <- sample(5:15, N, replace = T)
Center <- sample(c("A","B","C"), N, replace = T )
Time <- sample(c("Mor", "After", "Eve"), N, replace = T)
X <- model.matrix(~ Operators + Center + Time)[,-1]
X
## Operators CenterB CenterC TimeEve TimeMor
## 1 12 1 0 0 1
## 2 15 0 0 0 0
## 3 7 0 1 1 0
## 4 7 0 0 0 0
## 5 11 0 1 0 1
## 6 7 1 0 0 1
## 7 10 0 0 0 0
## 8 9 0 1 0 1
## 9 14 1 0 0 0
## 10 7 0 0 0 0
## [ reached getOption("max.print") -- omitted 1990 rows ]
We specify the coefficients and the hazard function.
true.beta <- c (0.04 , -0.3 , 0, 0.2 , -0.2)
h.fn <- function (x) return (0.00001 * x)
Here, we have set the coefficient associated with Operators to equal 0:04; in other words, each additional operator leads to a e^0.04 = 1.041-fold increase in the “risk that the call will be answered, given the Center and Time covariates. This makes sense: the greater the number of operators at hand, the shorter the wait time! The coefficient associated with Center = B is −0:3, and Center = A is treated as the baseline. This means that the risk of a call being answered at Center B is 0:74 times the risk that it will be answered at Center A; in other words, the wait times are a bit longer at Center B.
We are now ready to generate data under the Cox proportional hazards model. The sim.survdata() function allows us to specify the maximum possible failure time, which in this case corresponds to the longest possible wait time for a customer; we set this to equal 1000 seconds.
library(coxed)
queuing <- sim.survdata (N = N, T = 1000 , X = X, beta = true.beta , hazard.fun = h.fn)
names(queuing)
## [1] "data" "xdata" "baseline" "xb"
## [5] "exp.xb" "betas" "ind.survive" "marg.effect"
## [9] "marg.effect.data"
failed is an indicator of whether the call was answered (failed = T) or the customer hung up before the call was answered (failed = F). We see that almost 90% of calls were answered.
head(queuing$data)
mean(queuing$data$failed)
## [1] 0.89
par(mfrow = c(1, 2))
fit.Center <- survfit(Surv(y, failed) ~ Center, data = queuing$data )
plot ( fit.Center , xlab = "Seconds ", ylab = "Probability of Still Being on Hold ", col = c(2, 4, 5), lwd = 3)
legend ("topright", c("Call Center A", "Call Center B", "Call Center C"), col = c(2, 4, 5) , lty = 1, lwd = 3)
Next, we stratify by Time.
fit.Time <- survfit ( Surv (y, failed) ~ Time , data = queuing$data )
plot ( fit.Time , xlab = " Seconds ", ylab = " Probability of Still Being on Hold ",col = c(2, 4, 5), lwd = 3)
legend ("topright", c(" Morning ", " Afternoon ", " Evening "), col = c(5, 2, 4) , lty = 1, lwd = 3)
We can use a log-rank test to determine whether these differences are statistically significant.
survdiff(Surv (y, failed ) ~ Center , data = queuing$data )
## Call:
## survdiff(formula = Surv(y, failed) ~ Center, data = queuing$data)
##
## N Observed Expected (O-E)^2/E (O-E)^2/V
## Center=A 683 603 579 0.971 1.45
## Center=B 667 600 701 14.641 24.64
## Center=C 650 577 499 12.062 17.05
##
## Chisq= 28.3 on 2 degrees of freedom, p= 7e-07
survdiff(Surv (y, failed ) ~ Time , data = queuing$data )
## Call:
## survdiff(formula = Surv(y, failed) ~ Time, data = queuing$data)
##
## N Observed Expected (O-E)^2/E (O-E)^2/V
## Time=After 688 616 619 0.0135 0.021
## Time=Eve 653 582 468 27.6353 38.353
## Time=Mor 659 582 693 17.7381 29.893
##
## Chisq= 46.8 on 2 degrees of freedom, p= 7e-11
We find that differences between centers are highly significant, as are differences between times of day.
Finally, we fit Cox’s proportional hazards model to the data.
fit.queuing <- coxph ( Surv (y, failed ) ~ ., data = queuing $ data )
fit.queuing
## Call:
## coxph(formula = Surv(y, failed) ~ ., data = queuing$data)
##
## coef exp(coef) se(coef) z p
## Operators 0.04174 1.04263 0.00759 5.500 3.8e-08
## CenterB -0.21879 0.80349 0.05793 -3.777 0.000159
## CenterC 0.07930 1.08253 0.05850 1.356 0.175256
## TimeEve 0.20904 1.23249 0.05820 3.592 0.000328
## TimeMor -0.17352 0.84070 0.05811 -2.986 0.002828
##
## Likelihood ratio test=102.8 on 5 df, p=< 2.2e-16
## n= 2000, number of events= 1780
The p-values for Center = B, Time = Even. and Time = Morn. are very small. It is also clear that the hazard | that is, the instantaneous risk that a call will be answered | increases with the number of operators. Since we generated the data ourselves, we know that the true coefficients for Operators, Center = B, Center = C, Time = Even. and Time = Morn. are 0.04, −0.3, 0, 0.2, and −0.2, respectively. The coefficient estimates resulting from the Cox model are fairly accurate.