7  Survival Analysis

7.1 Logrank test

The logrank test is used to compare two Kaplan-Meier (KM) survival curves. The null hypothesis is no difference between the curves.

In survival analysis, we observe subjects until an event of interest occurs. The outcome is time until event. If the subject does not experience the event, they are censored.

In an experimental clinical setting, subjects are entered into the experiment during an accrual period. At the end of the accrual period, new subjects are no longer entered into the experiment. The subjects are then observed for an arbitrary amount of time called the follow-up period. Accrual and follow-up periods can be measured in days, weeks, months, or even years.

A common summary measure of KM survival curves is median survival time. This is the length of time at which 50% of the subjects have yet to experience the event of interest. Median survival times are often used to compare two KM survival curves.

Another measure is the hazard, which measures the probability the event will occur in the next instant assuming it hasn’t already occurred. Dividing the hazard of one group by the other gives the hazard ratio (HR). Say the “treated” group is in the numerator and the “control” group is in the denominator. If the HR is less than 1, we conclude longer survival times in the treated group (i.e., lower hazard). If the HR is greater than 1, we conclude shorter survival times in the treated group (i.e., higher hazard). The hazard ratio is often the effect size in a power and sample size analysis for the logrank test.

For sample size estimation based on power, we need the following:

  • power
  • ratio between groups
  • hazard ratio
  • accrual time
  • follow-up time
  • amount of censoring
  • significance level of the test

For power estimation based on sample size, we need the following:

  • sample size in each group
  • hazard ratio
  • accrual time
  • follow-up time
  • amount of censoring
  • significance level of the test

7.1.1 Example: sample size

We’re planning a study with control and treatment groups and we will observe the elapsed time until an event of interest. We will compare the KM survival curve for each group using a logrank test. We want to sample enough subjects to reject the null of no difference in the survival curves assuming the median survival times for the control and treated groups are 15 and 25 months, respectively. Assume the following:

  • power: 0.90
  • ratio between groups: 1 (i.e., equal sample sizes)
  • median survival times: 15 months (control) and 25 months (treated)
  • accrual time: 12 months
  • follow-up time: 24 months
  • amount of censoring: 15% of subjects will not experience the event while observed
  • significance level: 0.05

We use the npsurvSS package (Yung and Liu 2024) to make the calculation.

First we need to create an “arm” object that contains information about each group using the create_arm() function. We need to populate the following arguments as follows:

  • size: set to 1 for each arm to specify equal sample sizes for each group.
  • accr_time: set to 12.
  • follow_time: set to 24.
  • surv_scale: use the per2haz() function to convert median survival time to an exponential rate parameter. For example, per2haz(15)
  • loss_scale: exponential rate parameter to specify amount of censoring. after 24 months of follow-up. For 15%, this can be solved as follows: \(-log(0.85)/24 = 0.0068\). See Crespi (2025) section 16.5.3 for details.

Notice the only argument that changes between the groups is the surv_scale argument.

library(npsurvSS)
grp1 <- create_arm(size = 1, accr_time = 12, follow_time = 24,
                   surv_scale = per2haz(15), 
                   loss_scale = -log(0.85)/24)
grp2 <- create_arm(size = 1, accr_time = 12, follow_time = 24,
                   surv_scale = per2haz(25), 
                   loss_scale = -log(0.85)/24)

Now we use the size_two_arm() function to calculate the sample size. Use the power and alpha arguments to specify power and significance level, respectively. Use the sides argument to specify a two-sided test.

size_two_arm(grp1, grp2, power = 0.90, alpha = 0.05, sides = 2)
       n0        n1         n        d0        d1         d 
133.69905 133.69905 267.39810  92.41487  69.00804 161.42291 

The output shows the required sample size for each group (n0, n1) and the total sample size (n) as well as the expected number of events for each group (d0, d1) and the total number of events (d). “n0” and “d0” refer to grp1, the control group; “n1” and “d1” refer to grp2, the treated group. The treated requires fewer events since we assume they have a longer median survival time.

7.1.2 Example: power

We’re planning a study with control and treatment groups and we will observe the elapsed time until an event of interest. We will compare the KM survival curve for each group using a logrank test. We have enough budget to recruit 100 subjects per group. What power do we have to reject the null of no difference in the survival curves assuming the median survival times for the control and treated groups are 15 and 25 months, respectively. Assume the following:

  • sample size: 100 per group
  • median survival times: 15 months (control) and 25 months (treated)
  • accrual time: 12 months
  • follow-up time: 24 months
  • amount of censoring: 15% of subjects will not experience the event while observed
  • significance level: 0.05

First we need to create an “arm” object that contains information about each group using the create_arm() function. We need to populate the following arguments as follows:

  • size: set to 100 for each arm
  • accr_time: set to 12.
  • follow_time: set to 24.
  • surv_scale: use the per2haz() function to convert median survival time to an exponential rate parameter. For example, per2haz(15)
  • loss_scale: exponential rate parameter to specify amount of censoring. after 24 months of follow-up. For 15%, this can be solved as follows: \(-log(0.85)/24 = 0.0068\). See Crespi (2025) section 16.5.3 for details.
grp1 <- create_arm(size = 120, accr_time = 12, follow_time = 24,
                   surv_scale = per2haz(15), 
                   loss_scale = -log(0.85)/24)
grp2 <- create_arm(size = 120, accr_time = 12, follow_time = 24,
                   surv_scale = per2haz(25), 
                   loss_scale = -log(0.85)/24)

Now we use the power_two_arm() function to calculate the power of the test. Use the alpha and sides arguments to specify significance level and two-sided test, respectively.

power_two_arm(grp1, grp2, alpha = 0.05, sides = 2)
[1] 0.8654795