Introduction: The R package gsDesign

First, we import the library gsDesign into R.

################################################
### Sequential monitoring of clinical trials ###
################################################
library(gsDesign)

Defining user-specified spending functions

The function gsDesign can carry out lots of analyses. Here we focus on the definition of a user-specified spending fuction.

The CAST study had a function that spent half the alpha level (of 2.5%) during the interim analyses prior to the final analysis, and half of the alpha level during the final analysis.

We first define the spending function:

sfcast <- function(alpha,  t,  param)
{  

  # set spending using constant alpha/2 until last interim then alpha/2 at final
  x <- list(name="CAST example", param=param, parname=NULL, 
            sf=sfcast, spend=cumsum(c(rep(alpha/(2*(length(t)-1)),length(t)-1), alpha/2)))  
  
  class(x) <- "spendfn"
  
  x
}

Notice that the object that is being created (\(x\)) has a number of attributes, such as name, param and it must be of class spendfn to be recognized by the package.

Now let’s try it out!

First we will plot the spending fuunction to see hwo the alpha level is being spent

t<-0:20/20
plot(t, sfcast(0.025, t, NULL)$spend, type="l", 
     xlab="Proportion of information", 
     ylab="Cumulative proportion of total spending")
<b>Figure 1. </b>Alpha spending function based on user-specified spending function, assuming  &alpha;=0.025.

Figure 1. Alpha spending function based on user-specified spending function, assuming α=0.025.

Using sfcast in the CAST trial example

Now, let’s define the CAST trial bounds:

x <- gsDesign(k=length(t), test.type=2, sfu=sfcast, alpha=0.025)
plot(x$timing, x$upper$bound, type="l", ylim=c(-3.5,3.5) ,
     ylab="Z(t)", xlab="Trial fraction")
lines(x$timing, x$lower$bound)
<b>Figure 2. </b> Group sequential bounds in the CAST study.

Figure 2. Group sequential bounds in the CAST study.

Now recall that the first DSMB review of the CAST study was after 32/425 events were observed (i.e., \(t=0.075\)) and the log-rank test statistic was \(Z(t)=-2.82\). During the second DSMB meeting, 42/300 events were observed (\(t=14\%\)) after the expected number of total events was revised to 300 from 425. During that time, \(Z(t)=-3.20\).

To see whether we have crossed a boundary in either case, we simply plot these two points on the previous plot.

x <- gsDesign(k=length(t), test.type=2, sfu=sfcast, alpha=0.025)
plot(x$timing, x$upper$bound, type="l", ylim=c(-3.5,3.5) ,
     ylab="Z(t)", xlab="Trial fraction")
lines(x$timing, x$lower$bound)
text(32/425, -2.82, "X")
text(42/300, -3.2, "X", col="red")
<b>Figure 3. </b> Group sequential bounds in the CAST study with log-rank statistics at the two DSMB reviews.

Figure 3. Group sequential bounds in the CAST study with log-rank statistics at the two DSMB reviews.