This is a tutorial for the three main functions for using the subgroups specific time to event continual reassessment method (Sub-TITE), seen in the paper: Subgroup-specific dose finding in phase I clinical trials based on time to toxicity allowing adaptive subgroup combination **(Currently in PRESS)**.

Code for this paper exists in the SubTite package available on CRAN here:

https://cran.r-project.org/web/packages/SubTite/index.html

I’m going to briefly discuss the four main functions:

**GetPriorMeans():** Calibrates the prior means of the non-linear logistic regression parameters to match clinician elicited prior toxicity probabilities for each dose and subgroup considered in the trial.
**GetEss(): **Takes output form GetPriorMeans() and for different values of $\sigma_\alpha^2$ and $\sigma_\beta^2$, this approximates the prior effective sample size by matching the mean and variance of the prior toxicity probabilities to a Beta distribution.
**GetSubTite():** For a set of patient data in a dose-finding trial, given hyperparameters, and matrix of doses tried for each subgroup in the trial, this function determines what dose each subgroup should be enrolled at when the next patient arrives.
**SimTrial():** Simulates a Sub-TITE trial based on some true time to event distribution and returns the operating characteristics of the trial under that scenario.

Two C++ functions are included in the package and are run in the background, but in general these are the three important functions to use for designing and running a Sub-TITE trial.

GetPriorMeans()

For a given reference time T where we desire to optimize dose based on the probability of toxicity for time T, we first need to obtain the prior means of the regression parameters used to make dose assignment decisions. The function GetPriorMeans() does this by taking an input for the vector of dose levels considered and a matrix containing the clinician elicited prior probabilities of toxicity by time T for each subgroup and dose level considered in the trial. Below is an example of what one of these prior matrices would look like for a SubTITE trial with 3 subgroups and 5 doses.

Clinician = matrix(c(.2,.3,.4,.5,.6,.1,.2,.3,.4,.5,.05,.1,.15,.2,.3),byrow=TRUE,nrow=3)

> Clinician

[,1] [,2] [,3] [,4] [,5]

[1,] 0.20 0.3 0.40 0.5 0.6

[2,] 0.10 0.2 0.30 0.4 0.5

[3,] 0.05 0.1 0.15 0.2 0.3

Each row in this matrix represents a subgroup and each entry in each row corresponds to the elicited toxicity probability by time T for a given dose level. It is important to note here that these toxicity probabilities increase monotonically as the dose increases (as you move right in the matrix) which guarantees the assumption that increasing the dose increases the risk of a toxicity. The different rows do not need to be ordered in any manner within a dose level. We input this matrix and the standardized dose levels into the function GetPriorMeans() below to obtain the baseline and subgroup specific prior means for the intercept and dose-slope.

> Dose=c(-1,-.5,0,.5,1)

> GetPriorMeans(Clinician,Dose)

Which gives the prior means for each logistic regression parameter below, with alpha and beta being the baseline intercept and slope and alpha1, alpha2, beta1, beta2 being the subgroup specific intercepts and slope.

Likewise, as an additional example, if we only wanted a trial with two subgroups and used only the first two rows of the matrix Clinician for the trial, we would obtain the hypermeans:

These prior means are next used in the function GetESS() to obtain the prior variances on the intercept and slope parameters.

GetESS()

This function takes the standardized dose values and prior means for each parameter, along with fixed values for and . These two values should be calibrated such that the outputted number from the program is approximately 1, while maintaining that .

SimTrial()

Prior to using the function GetSubTite() within a trial, a responsibly planned Sub-TITE clinical trial should simulate replications of the trial under various scenarios and distribution families. This can be done in the function SimTrial() which uses c++ to perform the MCMC needed in the trial and additionally performs the entire trial in c++, vastly improving the time needed to simulate such a trial. Depending on sample size and number of trial simulations (>= 1000) length and the number of groups and doses to consider, the function takes 1-5 hours to run and return the operating characteristics of the design.

Let’s break down the function arguments with an example where we simulate toxicity times from the uniform distribution. We will simulate the first scenario shown in the paper by Chapple and Thall (2017) by imposing that up to 60 patients are enrolled with 2 subgroups and 5 doses to try. We also assume an accrual of 2 patients per month and use hyperparameters that were calibrated to the elicited toxicity probabilities seen in supplemental table 1. We will run 1000 simulation repetitions of the trial.

nSims=1000 ##Number of Trial Simulations to run

nGroups=2 ##Number of subgroups to consider in the trial

x=c(1,2,3,5,7)

Dose=(x-mean(x))/sd(x) ##Vector of Standardized doses

nDose=length(Dose)

DoseStart=1 ##Starting Dose (Or vector of starting doses)

Nmax=60 ##Maximum Number of patients to enroll

TGroup = 2 ## Suspends a group for TGroup after 3 enrolled

Upper=c(.85,.95) ## Cutoffs for stopping accrual in a subgroup

Accrue=2 ## Expected Patient Accual per month

T1=6 ## Toxicity Reference Time (in months)

Target=.3 ## Targeted Toxicity Probability

##Next we collect the hyperparameans and vector of hypermeans into a list to be fed into the function. Here I am using values seen in the paper by Chapple and Thall (2017).

Means=GetPriorMeans(Clinician,Dose)

meanmu=Means[[1]]

meanslope=Means[[2]]

MeanInts = Means[[3]]

MeanSlopes=Means[[4]]

##Now let’s make our TRUE scenario to simulate the trial under.

GroupProb = matrix(rep(NA,nDose*nGroups),ncol=nDose)

GroupProb[1,]=c(.18,.25,.45,.66,.74) ## These are the true toxicity probabilities

GroupProb[2,]=c(.10,.15,.30,.50,.60) ## for each dose for subgroups 1 and 2

groupprob=c(.5,.5) ##True proportion of patients in each group

Family=”Uniform” ##We will use the uniform distribution here

##Note that the Family argument supports the Exponential, Weibull, Gamma, Uniform and Lognormal distributions (all with captital letters).

Param1= GroupProb ##Parameter matrix for uniform distribution are

Param1=GroupProb ##Just the true toxicity probabilities

After the simulations are finished, the program returns the following summary of operating characteristics (for example):

Let’s walk through some of the details to explain what is being displayed.

Subgroup Specific Selection Probability of the Optimal Dose 0.565 0.762

This is the proportion of times in the simulated trials that the optimal dose for groups 1 and 2 were selected. Recall that the optimal doses for this scenario setup are dose 2 for subgroup 1 and dose 3 for subgroup 2. The next part of the output shows the proportion of times that each dose was selected as optimal for each subgroup.

Frequencies of Optimal Dose Selected for each Subgroup

Group 1

1 2 3 4

0.126 0.565 0.307 0.002

Group 2

1 2 3 4

0.008 0.116 0.762 0.114

Notice that for both subgroups, dose 5 was never selected as optimal. The next operating characteristic displayed is the absolute difference between the true toxicity probability for a chosen dose in each subgroup and the truly optimal dose for that subgroup. This is displayed below:

Average true toxicity probability difference between chosen dose and the optimal dose

[1] 0.0712965 0.0418000

Finally, we have the number of toxicities on average of the trials, and the overall average trial time in terms of months.

Average number of Toxicities for each Subgroup

[1] 9.558 9.729

Average Trial Time

[1] 37.44127

These operating characteristics should be examined under a myriad of different scenarios and families of distributions prior to running a trial to justify using the trial.

GetSubTite

After the principle investigator uses the function SimTrial() under a myriad of different scenarios to justify conducting the trial, we use the function GetSubTite during the trial to determine what doses to assign to the next patient for any subgroup and if any of the subgroups should have accrual temporarily suspended. To further illustrate the usefulness of the methodology, we will use the function in an example with 3 subgroups and 5 doses to test. Again we will generate toxicity times (for those that have toxicity prior to the reference time) from a uniform distribution. We used the hyperparameter values determined in the GetPriorMeans() example. The following is an example of using this function for dose assignment after 30 patients have been enrolled. **NOTE: THE GROUPS MUST BE LABELED 0,1,2,….**

T1=6 ##Toxicity Reference Time

Target=.3 ##Target Reference Time Toxicity Probability

Upper=c(.9,.95,.95) ##Cutoffs for subgroup suspension

n=30

Y=rep(NA,n)

I=Y

Groups = sample(1:3,n,replace=TRUE) – 1 **##GROUPS MUST BE LABELED 0,1,2**

Doses = sample(1:5,n,replace=TRUE) ##Dose number assignment for each patient

x=c(1,2,3,5,7)

Dose=(x-mean(x))/sd(x) ##Standardized Vector of Doses

##Generate Toxicity times (or censoring) based on group and dose assignment

GroupProb = matrix(rep(NA,length(Dose)*3),nrow=3)

GroupProb[1,]=c(.18,.25,.45,.66,.74) ## These are the true toxicity probabilities

GroupProb[2,]=c(.10,.15,.30,.50,.60) ## for each dose and subgroup

GroupProb[3,]=c(.10,.15,.30,.50,.60) +.1

##Next lets fill in the current trial data based on our true toxicity probabilities

for(b in 1:n){

I[b]= rbinom(1,1 , GroupProb[(Groups[b]+1),Doses[b]])

if(I[b]==0){

Y[b]=T1

}else{

Y[b]=runif(1,0,T1)

}

}

##Doses Tried so far in trial

DoseTried=matrix(c(1,1,0,0,0,1,1,1,1,1,1,1,0,0,0),ncol=5,byrow=TRUE)

##This gives the following matrix of doses tried. Only doses 1 and 2 have been tried so far for subgroups 1 and 3.

##Next we fill in our hypermeans for the linear terms

meanmu=-0.4467184

meanslope= 0.8861634

MeanInts = c(-0.5205379, -1.3752529)

MeanSlopes = c(0.1888923, 0.1148791)

**##MAKE SURE THAT THE PATIENT DOSE VECTOR IS STANDARDIZED AND NOT LABELED 1, 2, 3, 4, etc.**

Doses=Dose[Doses] ##Standardizes the patient doses

##Fill in Hyperparameter list for MCMC

Hyper=as.list(c(0,0,0,0))

Hyper[[1]]=meanmu

Hyper[[2]]=meanslope

Hyper[[3]]=MeanInts

Hyper[[4]]=MeanSlopes

##When we read this data into the function GetSubTite(), we get the following output:

The entries in [[2]] correspond to what groups should suspend accrual, here we note that none of the three subgroups are recommended for suspension of accrual. The entries in [[1]] show what dose each subgroup should be assigned if the next patient is a member of that subgroup. If the newly arrived patient is in subgroup 1, then they are enrolled at dose 2, otherwise they will be enrolled at dose 3.

Now let’s look at an example of the output of this function at the end of the trial, when all doses have been tried for each subgroup and 90 patients (=max sample size) have been enrolled.

This tells us that the optimal dose for subgroups 1 and 3 is dose 2 and the optimal dose for subgroup 2 is dose 3 based on the simulated data. These optimal doses are correctly chosen for this scenario.

I hope that this tutorial helps explain how to use the package SubTite() to design subgroup specific clinical trials and how to use the function GetSubTite() to actually use the program. If you are interested in conducting a subgroup specific time to event CRM trial (SubTite) please feel free to contact me for help on your trial design at agc6@rice.edu.

If you enjoyed this tutorial, check out some of the tutorials for my other packages or one of my data driven blog posts.

Happy coding!

-Andrew G Chapple