
Binomial endpoint
binomial.RmdContext
This example is motivated by the Personalised Immunotherapy Platform (PIP) trial (Lo et al. 2022), which is a phase II randomised controlled trial that is currently under development that will compare immunotherapy combinations for advanced melanoma patients.
This study aims to assess the effectiveness of different novel drug combinations compared to a control arm in treatment-naive patients that are predicted to be unlikely to respond to standard first line immunotherapy treatments. The primary outcome is the objective response rate (ORR, i.e., the probability of observing a one/success) according to the Response Evaluation Criteria in Solid Tumors (RECIST) (partial or complete response vs stable disease or progressive disease) at 6 months post randomisation.
Design
The chosen design has the following characteristics:
- treatment arms: Our case study has 6 treatment arms: standard of care (arm “A”), five drug combinations (referred to as arms “B”, “C”, “D”, “E” and “F”).
- alternative hypotheses: This is a superiority trial where we are interested in demonstrating efficacy compared to the standard of care arm and intervention arms will not be compared to one another.
- interim analyses and maximum sample size: We will begin the interim analyses at 60 participants completing follow-up, and perform interim analyses every 12 participants completing follow-up thereafter. Our maximum sample size is 216 participants and so a total of 14 looks are planned.
- endpoint conditional distribution: the binary endpoint is assumed to follow a binomial distribution.
-
group allocation: the trial will start with equal
randomisation (1:1:1:1:1:1) and RAR will be used from interim analysis 1
onwards. The randomisation probabilities are calculated using a
variation of Trippa et al. (2012) and
Cellamare et al. (2017) where the
allocation probabilities at look
are proportional to:
- control arm:
where
- corresponds to the sample size of arm (where denotes the control arm) at stage and is evaluated across intervention arms only (i.e. for ),
- is the number of active arms (excluding control) at look ,
- is a tuning parameter.
- intervention arms:
where
- denotes the th target parameter (on the logit scale),
- denotes the (RAR-related) clinically meaningful treatment effect value,
- and
with
- , the sample size of arm at stage ,
- , the maximum sample size,
- and , two tuning parameters. Refer to Gotmaker et al. (2019) for details
- control arm:
where
- efficacy stopping rule: Stopping arms for efficacy is not permitted at the interim analyses as the main objective of the adaptations is to drop poorly performing arms.
-
end of trial efficacy rule: An intervention may be
declared effective at the end of the trial if the posterior probability
of efficacy is above a certain (high) threshold:
where
- denotes the (efficacy-related) clinically meaningful treatment effect value,
- is a tuning parameter.
-
futility stopping rule: Early stopping of
intervention arms for futility may occur if there is a low posterior
probability of observing at least a 10% absolute increase (improvement)
in the proportion of responders, i.e., when
where
- denotes the (futility-related) clinically meaningful treatment effect values assuming a control ORR of 40%,
- is the cut-off value used to declare futility.
- trial stopping rule: The trial will run until a futility decision has been reached for each intervention arm or once the maximum sample size has been reached.
Note that, in the above, the tuning parameter values have been optimised to lead to suitable operating characteristics.
In the following, we define the group allocation, RAR, futility and efficacy functions corresponding to the design described above
RAR
We need to generate a function that defines the allocation probabilities of patients to the different active arms as a function of
- relevant ingredients, i.e.,
-
posterior, for the posterior probabilities of the target parameter being greater thandelta.RAR = 0, -
nandN, respectively the sample size per arm at the look of interest and the max sample size to define the information fraction, -
ref, for a vector of logicals indicating which group is the reference one, -
active, for a vector of logicals indicating which groups are active at the look of interest,
-
- relevant tuning parameters (to be added to
RAR.controlinbatss.glm), namely , , and , that we will respectively callgamma,etaandnu
# function
prob.trippa = function(posterior,n,N,ref,active,gamma,eta,nu){
g = sum(active)
h = gamma*(sum(n)/N)^eta
prob = rep(NA,g)
# reference/control arm allocation
prob[1] = (exp(max(n[!ref])-n[ref])^nu)/(g-1)
# targets/interventions (that haven't been dropped)
prob[2:g] = (posterior^h)/(sum(posterior^h))
unlist(prob)
}
# test after at the first look with two set of posterior probabilities
# 1/ c(.5,.5,.5,.5,.5)
# 2/ c(.5,.5,.5,.5,.6)
prob.trippa(c(.5,.5,.5,.5,.5),n=c(A=10,B=10,C=10,D=10,E=10,F=10),N=16,
ref = c(TRUE,rep(FALSE,5)), active = rep(TRUE,6),
gamma=3, eta=1.4, nu=0.1)
prob.trippa(c(.5,.5,.5,.5,.6),n=c(A=10,B=10,C=10,D=10,E=10,F=10),N=16,
ref = c(TRUE,rep(FALSE,5)), active = rep(TRUE,6),
gamma=3, eta=1.4, nu=0.1)Note that this corresponds to the function RAR.trippa
available in the BATSS package.
Group allocation
We need to generate a function that randomises the m
participants of the next look according to the allocation ratios
prob, where m and prob are
ingredients described in the Ingredients
section and where prob is the output of the
user-defined RAR function.
# function
treatalloc.fun = function(m,prob){
prob = abs(prob)/sum(abs(prob))
m0.g = floor(prob*m)
m0 = sum(m0.g)
factor(rep(names(prob),m0.g+rmultinom(1,m-m0,prob)),
levels=names(prob))
}
# test on m = 60 patients and equal allocation per group
table(treatalloc.fun(m=60,prob=c(A=1,B=1,C=1,D=1,E=1,F=1)))treatalloc.fun first allocates the largest possible
number of units to the different groups given their exact target
probabilities and then assigns randomly the remaining units to the
different groups according to multinomial draws.
Note that this corresponds to the function
alloc.balanced available in the BATSS
package.
Efficacy rule
We need to generate a function that leads to a logical output when used at the last look of the trial and takes as input
- the ingredient
posteriorfor the posterior probability of the target parameter being greater thandelta.eff = 0, - the additional parameter
that we will name
b.eff(and that needs to be added toeff.arm.controlinbatss.glm).
# function
efficacy.arm.fun = function(posterior,b.eff){
posterior > 1-b.eff
}
# test for a parameter with a posterior = 0.999
efficacy.arm.fun(0.999, b.eff = 0.045)
# test for a parameter with a posterior = 0.95
efficacy.arm.fun(0.95, b.eff = 0.045)Arm futility stopping rule
We need to generate a function that leads to a logical output and takes as input
- the ingredient
posteriorfor the posterior probability of the target parameter being greater thandelta.fut = log(1.5), - the additional parameter
that we will name
b.fut(and that needs to be added tofut.arm.controlinbatss.glm)
# function
futility.arm.fun = function(posterior,b.fut){
posterior < b.fut
}
# test
futility.arm.fun(0.9, b.fut=0.1)
futility.arm.fun(0.075, b.fut=0.1)Note that this corresponds to the function
fut.arm.simple available in the BATSS
package.
Trial futility stopping rule
We need to generate a function that, based on the ingredient
fut.target (indicating if futility was declared for each
target parameter at the stage of interest or before) leads to a logical
output equal to TRUE if all target parameters were declared
futile and FALSE otherwise.
# function
futility.trial.fun = function(fut.target){
all(fut.target)
}
# test
futility.trial.fun(c(B=TRUE,C=TRUE,D=TRUE,E=TRUE,F=TRUE))
futility.trial.fun(c(B=TRUE,C=TRUE,D=TRUE,E=TRUE,F=FALSE))Note that this corresponds to the function fut.trial.all
available in the BATSS package and that this behaviour
is the default one of the function batss.glm when
fut.trial = NULL.
Monte Carlo Simulations
We consider two scenarios:
- Scenario 1: each arm has an ORR of 0.4 (global null),
-
Scenario 2:
- arms “A”, “B” and “C” have an ORR of 0.4,
- arms “D” has an ORR of 0.5,
- arms “E” and “F” have an ORR of 0.7.
Scenario 1
# number of trials
R = 25
# logit function
logit = function(p){log(p/(1 - p))}
# simulation
scenario1 = batss.glm(
model = y~group,
var = list(y = rbinom,
group = treatalloc.fun),
var.control = list(y = list(size = 1)),
family = "binomial",
link = "logit",
beta = c(logit(0.4), rep(0,5)),
which = c(2:6),
R = R,
alternative = c("greater"),
RAR = prob.trippa,
RAR.control = list("gamma"=3, "eta"=1.4,"nu"=0.1),
delta.RAR = 0,
prob0 = c(A=1,B=1,C=1,D=1,E=1,F=1),
N = 216,
interim = list(recruited=list(m0=60, m=12)),
eff.arm = efficacy.arm.fun,
delta.eff = c(rep(NA,13), 0),
eff.arm.control = list(b.eff = 0.045),
delta.fut = log(1.5),
fut.arm = futility.arm.fun,
fut.arm.control = list(b.fut = 0.1),
computation = "parallel",
mc.cores = 12,
H0 = FALSE,
extended = 1) You can note that
-
yis generated via the functionrbinomand has an ORR of respectively-
expit(logit(0.4)) = 0.4for the reference group, wherelogit(0.4) = -0.405corresponds to the log odds of ‘success’ for the control group, -
expit(logit(0.4+0)) = 0.4for all other groups (as contrasts of type treatment are used for the factortreatmentin the self-defined functiontreatalloc.fun), where0corresponds to the log odds ratio value related to groups “B” to “F” (i.e., no effect),
-
- the target parameters (corresponding to the shift in ORR of
treatment arms “B” to “F” compared to the “control” on the logit scale)
are in position 2 to 6 of the fitted coefficients obtained when using
the formula
y~group, -
prob0provides- the (equal) allocation probabilities at the start of the trial,
- the names of the groups,
-
eff.armandfut.armare set to the functions defined above (i.e.,efficacy.arm.funandfutility.arm.fun), -
delta.effis set toNAfor all 13 interim analyses and to0for the last look. This ensures that the trial can’t stop early for efficacy. This aim could be achieved using other strategies, like setting the output of the function indicated undereff.trialtoFALSE, -
fut.trialis not specified and therefore equal toNULL(default) which leads to the behaviour wished in this case, - the values of the additional parameters of
efficacy.arm.funandfutility.arm.fun(i.e.,b.effandb.fut) are specified undereff.arm.controlandfut.arm.control, -
delta.effanddelta.futare respectively set to0andlog(1.5).
We chose here a low number of seeds/trials (R=25) to
save time.
Scenario 2
# number of trials
R = 25
# logit function
logit = function(p){log(p/(1 - p))}
# simulation
scenario1 = batss.glm(
model = y~group,
var = list(y = rbinom,
group = treatalloc.fun),
var.control = list(y = list(size = 1)),
family = "binomial",
link = "logit",
beta = c(logit(0.4), # log odd of reference group
0,0, # log odds ratio of groups B and C
logit(0.5)-logit(0.4), # log odds ratio of group D
rep(logit(0.7)-logit(0.4),2)), # log odds ratio of groups E and F
which = c(2:6),
R = R,
alternative = c("greater"),
RAR = prob.trippa,
RAR.control = list("gamma"=3, "eta"=1.4,"nu"=0.1),
delta.RAR = 0,
prob0 = c(A=1,B=1,C=1,D=1,E=1,F=1),
N = 216,
interim = list(recruited=list(m0=60, m=12)),
eff.arm = efficacy.arm.fun,
delta.eff = c(rep(NA,13), 0),
eff.arm.control = list(b.eff = 0.045),
delta.fut = log(1.5),
fut.arm = futility.arm.fun,
fut.arm.control = list(b.fut = 0.1),
computation = "parallel",
mc.cores = 9,
H0 = FALSE,
extended = 1) Same as above except for beta.