7.5 Cox regression in software

Software 7.5.1 (The partial likelihood by hand). Example 7.2.1: the log partial likelihood written as a sum over events of \(\beta x_i - \log \sum _{\text {risk set}}e^{\beta x_l}\), maximised with optimize; then coxph:

> library(survival)
> tm <- c(2, 3, 5, 6, 8, 9, 11, 14)           # eight subjects, no ties
> ev <- c(1, 1, 0, 1, 1, 0, 1, 1)
> x  <- c(1, 0, 1, 1, 0, 1, 0, 0)             # a binary covariate
> logPL <- function(b) {
+   s <- 0
+   for (i in which(ev == 1)) {               # one term per event
+     risk <- tm >= tm[i]                     # the risk set at that time
+     s <- s + b * x[i] - log(sum(exp(b * x[risk])))
+   }
+   s
+ }
> o <- optimize(logPL, c(-5, 5), maximum = TRUE)
> round(c(beta = o$maximum, logPL = o$objective), 4)
   beta   logPL
 0.4442 -7.6200
> round(coef(coxph(Surv(tm, ev) ~ x)), 4)    # coxph agrees
     x
0.4442

Reading the output. The maximum is at \(\hat \beta = 0.4442\), and coxph gives the same value. The baseline hazard never appears: only the covariates of those at risk at each event time matter.

Python: lifelines.CoxPHFitter().fit(df, ’time’, ’event’).

Software 7.5.2 (Sex as a predictor). Example 7.3.1: the coefficient table, the hazard ratio with its interval, the three tests of \(\beta = 0\), and the log-rank statistic for comparison:

> fit <- coxph(Surv(time, status) ~ sex, data = lung)
> round(summary(fit)$coefficients, 4)
      coef exp(coef) se(coef)       z Pr(>|z|)
sex -0.531     0.588   0.1672 -3.1764   0.0015
> round(summary(fit)$conf.int, 4)             # HR for women vs men, and CI
    exp(coef) exp(-coef) lower .95 upper .95
sex     0.588     1.7007    0.4237     0.816
> round(c(wald = summary(fit)$waldtest[1], lr = summary(fit)$logtest[1],
+         score = summary(fit)$sctest[1]), 3)
 wald.test    lr.test score.test
    10.090     10.634     10.325
> round(survdiff(Surv(time, status) ~ sex, data = lung)$chisq, 3)  # log-rank
[1] 10.327

Reading the output. \(\hat \beta = -0.531\), HR \(0.588\) (\(0.424\) to \(0.816\)), \(p = 0.0015\) by the Wald test. exp(-coef) \(= 1.70\) is the hazard ratio the other way round: men’s hazard was \(1.7\) times women’s. The score test, \(10.325\), is the log-rank test up to the treatment of ties.

Python: cph.print_summary() shows exp(coef) and its interval.

Software 7.5.3 (Several covariates). Example 7.3.2: hazard ratios, intervals and \(p\)-values for age, sex and ECOG score, and the hazard ratio for ten years of age:

> lg <- na.omit(lung[, c("time", "status", "age", "sex", "ph.ecog")])
> m <- coxph(Surv(time, status) ~ age + sex + ph.ecog, data = lg)
> round(cbind(HR = exp(coef(m)), exp(confint(m)),
+             p = summary(m)$coefficients[, 5]), 4)
            HR  2.5 % 97.5 %      p
age     1.0111 0.9929 1.0297 0.2324
sex     0.5754 0.4142 0.7994 0.0010
ph.ecog 1.5900 1.2727 1.9864 0.0000
> round(exp(10 * coef(m)["age"]), 4)          # HR for ten years of age
  age
1.117

Reading the output. After adjustment, each point of ECOG score multiplies the hazard by \(1.59\), and being female by \(0.58\); ten years of age multiply it by \(1.12\), not significantly. These are the values of Figure 7.

Python: the same model with CoxPHFitter and a formula ’age + sex + ph_ecog’.

Questions on this section

Stuck on something here? Ask below and it stays attached to this topic.