Here we demonstrate how to use this package to implement and run an actual trial. Only two functions are needed: one for each stage.
The first stage requires only the calculation of the likelihood of safety for each dose, based on observed toxicity. This function is used after enrolling each dose.
When the measure of toxicity is binary (yes/no observed DLT), the function takes as input: the cohort size (equal number of patients assigned at each dose, default is 3), number of DLTs observed, the acceptable and unacceptable toxicity rates, and the likelihood threshold value (default is 2).
For example, suppose we have the following data for a dose. Do we escalate to the next dose level, or declare this dose unsafe and move on to stage 2?
library(iAdapt)
# Acceptable (p_yes) and unacceptable (p_no) DLT rates used for establishing safety
p_no <- 0.40
p_yes <- 0.15
# Likelihood-ratio (LR) threshold
K <- 2
# Cohort size used in stage 1
coh.size <- 3
# number of observed DLTs
x <- 1
## [1] "Safe/Escalate"
## $LR
## [1] 0.75
Based on this data, because LR=0.75 > 1/2 (1/K) we would escalate to the next dose. However, if we observed 2 DLTs instead of 1, we would not because LR < 1/2
## [1] "Unsafe/Stop"
## $LR
## [1] 0.2
When the measure of toxicity is quasi-continuous (normalized total toxicity profile; nTTP. See Ezzalfani et al. for more details), the function takes as input: the observed adverse event grades for each patient across each toxicity type, the number of different toxicity types, the cohort size (equal number of patients assigned at each dose, default is 3), the acceptable and unacceptable toxicity rates, the likelihood threshold value (default is 2), and the standard deviation of the nTTP values (default is 0.15).
The output is interpreted the same as for DLT rate.
Guidance on how to specify the acceptable and unacceptable toxicity rates is found in the vignette “nTTP_simulation_example”.
ntox = 3 # three different types of toxicity
coh.size = 3 # number of patients enrolled per dose
# Observed AE grades for each patient on tested dose
obs = data.frame(tox1 = c(0, 1, 1),
tox2 = c(1, 0, 0),
tox3 = c(2, 0, 1))
# Toxicity burden weight matrix
W = matrix(c(0, 0.5, 0.75, 1.0, 1.5, # Burden weight for grades 0-4 for toxicity 1
0, 0.5, 0.75, 1.0, 1.5, # Burden weight for grades 0-4 for toxicity 2
0, 0.00, 0.00, 0.5, 1), # Burden weight for grades 0-4 for toxicity 3
nrow = ntox, byrow = TRUE)
# Acceptable (p2) and unacceptable nTTP values
p1 <- 0.35
p2 <- 0.10
LRtox.nTTP(obs, ntox, coh.size, W, p1, p2, K = 2, std.nTTP = 0.15)
## [1] "Safe/Escalate"
## $LR
## [1] 3.44
Once we have determined which doses are safe, we can move on to stage 2 and begin collecting information about effectiveness. If only only one dose was determined as safe in stage 1, then stage 2 will be omitted. The function at stage 2 returns the updated randomization probabilities and the dose allocation for the next enrolled patient, based on the observed efficacies up to that point in the trial.
This function operates the same regardless of toxicity measure.
As input, this function requires a vector of observed efficacies (for each patient) and a vector of the corresponding dose levels.
y.eff <- c(9, 1, 0, 34, 10, 27, 38, 42, 60, 75, 48, 62)
d.safe <- c(1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4)
rand.prob(y.eff, d.safe)
## $Rand.Prob
## [1] 0.02037092 0.16578310 0.35098480 0.46286117
##
## $Next.Dose
## [1] 4
In this example, the randomization probabilities for doses 1-4 are given by $Rand.Prob, and the next patient will be enrolled on dose level 4.