Negative binomial endpoint
negbin.Rmd
Context
This case study is inspired by the CHANGE-MS study (Curtin et al. 2016), which was a multi-arm, phase II randomised controlled trial which aimed to study the efficacy and safety of temelimab for the treatment of relapsing-remitting multiple sclerosis (MS) and determine whether temelimab slowed down or stopped the progression of MS.
The primary endpoint is the cumulative number of new active lesions that were identified on brain MRI scans that were performed on 4 occasions (once a month) from weeks 12-24 post-randomisation. The data were overdispersed (Curtin et al. 2016; Hartung et al. 2022) and modelled using a negative binomial GLM; we will use the same model here.
Design
The chosen design has the following characteristics:
- treatment arms: Our case study has 4 treatment arms: placebo (control), low, medium and high doses of temelimab (intervention arms referred to as arms “A”, “B” and “C”, respectively).
- alternative hypotheses: This is a superiority trial where we are interested in demonstrating efficacy compared to the placebo arm and intervention arms will not be compared to one another.
- interim analyses and maximum sample size: We will begin the interim analyses at 100 participants completing follow-up, and perform interim analyses every 40 participants completing follow-up thereafter. Our maximum sample size is 260 participants, which is the same as the original study (Curtin et al. 2016).
- power: Similar to the original study, we aim to have 90% power to detect a 60% reduction (RR=0.4) in the mean cumulative number of lesions in the highest dose intervention arm (from a mean of 4 (control) to 1.6 lesions).
- endpoint conditional distribution: the endpoint is assumed to follow a negative binomial distribution with nuisance parameter equal to \(\phi = 0.5\) when considering the parametrisation leading to the following variance: \[\text{Var}(Y) = \mu + \frac{\mu^2}{\phi}.\]
- group allocation: we will consider equal allocation probabilities per group (no response-adaptive randomisation).
-
efficacy stopping rule: Early stopping of
intervention arms for efficacy may occur if there is a high posterior
probability of (any) benefit at look j, i.e., when \[\text{Prob}(\beta_k < \delta_{\epsilon}|y, X)
> 1-b_{\epsilon}\left(\frac{\sum_{k=1}^{K}
n_k}{N}\right)^{p_{\epsilon}}\] where
- \(\beta_k\) denotes the \(k\)th target parameter,
- \(\delta_{\epsilon} = 0\) denotes the (efficacy-related) clinically meaningful treatment effect value,
- \(N\) is the maximum sample size,
- \(n_k\) is the number of participants that have completed follow up in arm \(k=1,...,K\) at look \(j\) so that \((\sum_{k=1}^{K} n_k)/N\) corresponds to the information fraction,
- \(b_{\epsilon} = 0.009\) and \(p_{\epsilon}=3\) are tuning parameters that determine the shape of the efficacy function (Gotmaker et al. 2019).
-
futility stopping rule: Early stopping of
intervention arms for futility may occur if there is a low posterior
probability of seeing a minimum clinically important difference of a 20%
reduction (i.e., RR=0.8), i.e., when \[
\text{Prob}(\beta_k < \delta_{f}|y, X) < b_f\] where
- \(\delta_{f} = log(0.8)\) denotes the (futility-related) clinically meaningful treatment effect values,
- \(b_{f} = 0.2025\) is a the cut-off value used to declare futility.
- trial stopping rule: The trial will run until an efficacy or futility decision has been reached for each intervention arm or once the maximum sample size has been reached.
Note that, in the above,
- the parameter values of \(b_{f}\), \(b_{\epsilon}\) and \(p_{\epsilon}\) have been optimised via a grid search to lead to suitable operating characteristics,
- the parameters values of \(\delta_{\epsilon}\) and \(\delta_{f}\) have been defined by clinicians.
Self-defined R functions
In the following, we define the group allocation, futility and efficacy functions corresponding to the design described above
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.
# 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 = 40 patients and equal allocation per group
table(treatalloc.fun(m=40,prob=c(control=.25,A=.25,B=.25,C=.25)))
table(treatalloc.fun(m=40,prob=c(control=1,A=1,B=1,C=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. - when no RAR rule is used, the allocation probabilities used
throughout the trial by
batss.glm
are the ones indicated underprob0
.
Arm efficacy stopping rule
We need to generate a function that leads to a logical output and takes as input
- the ingredients
-
posterior
for the posterior probability of the target parameter being smaller thandelta.eff = 0
, -
n
andN
, respectively the sample size per arm at the look of interest and the max sample size to define the information fraction,
-
- the additional parameters (to be added to
eff.arm.control
inbatss.glm
)-
\(b_{\epsilon}\) that we will name
b.eff
, -
\(p_{\epsilon}\) that we will name
p.eff
.
-
\(b_{\epsilon}\) that we will name
# function
efficacy.arm.fun = function(posterior,n,N,b.eff,p.eff){
posterior > (1-(b.eff*(sum(n)/N)^p.eff))
}
# test at interim 1 for a parameter with a posterior = 0.999
efficacy.arm.fun(0.999, n=c(control=25,A=25,B=25,C=25),N=260,
b.eff = 0.009,p.eff=3)
# test at interim 1 for a parameter with a posterior = 0.9995
efficacy.arm.fun(0.9995, n=c(control=25,A=25,B=25,C=25),N=260,
b.eff = 0.009,p.eff=3)
Note that this correspond to the function
eff.arm.infofract
available in the BATSS
package.
Trial efficacy stopping rule
We need to generate a function that, based on the ingredient
eff.target
(indicating if efficacy was reached for each
target parameter at the stage of interest or before) leads to a logical
output equal to TRUE
if all target parameters reached
efficacy and FALSE
otherwise.
# function
efficacy.trial.fun = function(eff.target){
all(eff.target)
}
# test
efficacy.trial.fun(c(A=TRUE,B=TRUE,C=TRUE))
efficacy.trial.fun(c(A=TRUE,B=TRUE,C=FALSE))
Note that this corresponds to the function eff.trial.all
available in the BATSS package and that this behaviour
is the default one of the function batss.glm
when
eff.trial = NULL
.
Arm futility stopping rule
We need to generate a function that leads to a logical output and takes as input
- the ingredients
posterior
for the posterior probability of the target parameter being smaller thandelta.fut = log(0.8)
, - the additional parameters \(b_{f}\)
that we will name
b.fut
(and that needs to be added tofut.arm.control
inbatss.glm
)
# function
futility.arm.fun = function(posterior,b.fut){
posterior < b.fut
}
# test
futility.arm.fun(0.9, b.fut=.2025)
futility.arm.fun(0.1, b.fut=.2025)
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(A=TRUE,B=TRUE,C=TRUE))
futility.trial.fun(c(A=TRUE,B=TRUE,C=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 three scenarios:
- Scenario 1 = ‘global null’: each arm has a cumulative mean number of lesions equal to 4,
- Scenario 2 = ‘one treatment works’: each arm has a cumulative mean number of lesions equal to 4 except the last one, “C”, that shows a 60% reduction in the cumulative mean number of lesions compared to the other groups (RR = 0.4),
- Scenario 3 = ‘better, best’: the control arm has a cumulative mean number of lesions equal to 4 and the other arms respectively lead to a 20%, 40% and 60% reduction in the cumulative mean number of lesions compared to the reference group (i.e., RR = 0.8 for “A”, RR = 0.6 for “B” and RR = 0.4 for “C”).
Scenario 1
R = 25
scenario1 = batss.glm(
model = y~treatment,
var = list(y = rnbinom,
treatment = treatalloc.fun),
var.control = list(y = list(size = 1/2)),
family = "nbinomial",
link = "log",
beta = c(log(4),log(1),log(1),log(1)),
which = c(2:4),
R = R,
alternative = c("less"),
RAR = NULL,
prob0 = c(control = .25, A = .25, B = .25, C = .25),
N = 260,
interim = list(recruited=c(100,140,180,220)),
eff.arm = efficacy.arm.fun,
delta.eff = 0,
eff.arm.control = list("b.eff"=0.009, "p.eff"=3),
delta.fut = log(0.8),
fut.arm = futility.arm.fun,
fut.arm.control = list("b.fut"=0.2025),
computation = "parallel",
mc.cores = 10,
H0 = TRUE,
extended = 1)
You can note that
-
y
is generated via the functionrnbinom
with nuisance parameter equal to 0.5 (size = 1/2
) and expected values on thelog
scale of respectively-
log(4)
for the control group, -
log(4)+log(1) = log(4)
for all other groups (as contrasts of type treatment are used for the factortreatment
in the self-defined functiontreatalloc.fun
),
-
- the target parameters (corresponding to the shift in means of
treatment arms “A”, “B” and “C” compared to the “control” on the log
scale) are in position 2, 3 and 4 of the fitted coefficients obtained
when using the formula
y~treatment
, -
prob0
provides- the (equal) allocation probabilities throughout the trial (as
RAR = NULL
), - the names of the groups,
- the (equal) allocation probabilities throughout the trial (as
-
eff.arm
andfut.arm
are set to the functions defined above (i.e.,efficacy.arm.fun
andfutility.arm.fun
), -
eff.trial
andfut.trial
are 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.fun
andfutility.arm.fun
(i.e.,b.eff
,p.eff
andb.fut
) are specified undereff.arm.control
andfut.arm.control
, -
delta.eff
anddelta.fut
are respectively set to0
andlog(0.8)
.
We chose here a low number of seeds/trials (R=25
) to
save time.
Scenario 2
R = 25
scenario2 = batss.glm(
model = y~treatment,
var = list(y = rnbinom,
treatment = treatalloc.fun),
var.control = list(y = list(size = 1/2)),
family = "nbinomial",
link = "log",
beta = c(log(4),log(1),log(1),log(0.4)),
which = c(2:4),
R = R,
alternative = c("less"),
RAR = NULL,
prob0 = c(control = .25, A = .25, B = .25, C = .25),
N = 260,
interim = list(recruited=c(100,140,180,220)),
eff.arm = efficacy.arm.fun,
delta.eff = 0,
eff.arm.control = list("b.eff"=0.009, "p.eff"=3),
delta.fut = log(0.8),
fut.arm = futility.arm.fun,
fut.arm.control = list("b.fut"=0.2025),
computation = "parallel",
mc.cores = 10,
H0 = TRUE,
extended = 1)
Same as above except for beta
which now leads to a mean
of
-
exp(log(4)) = 4
for the control group, -
exp(log(4) + log(1)) = 4
(no effect) for the groups “A” and “B”, -
exp(log(4) + log(0.4)) = 1.6
(target effect) for group “C” which corresponds to a RR of1.6/4 = 0.4
.
Scenario 3
R = 25
scenario3 = batss.glm(
model = y~treatment,
var = list(y = rnbinom,
treatment = treatalloc.fun),
var.control = list(y = list(size = 1/2)),
family = "nbinomial",
link = "log",
beta = c(log(4),log(0.8),log(0.6),log(0.4)),
which = c(2:4),
R = R,
alternative = c("less"),
RAR = NULL,
prob0 = c(control = .25, A = .25, B = .25, C = .25),
N = 260,
interim = list(recruited=c(100,140,180,220)),
eff.arm = efficacy.arm.fun,
delta.eff = 0,
eff.arm.control = list("b.eff"=0.009, "p.eff"=3),
delta.fut = log(0.8),
fut.arm = futility.arm.fun,
fut.arm.control = list("b.fut"=0.2025),
computation = "parallel",
mc.cores = 10,
H0 = TRUE,
extended = 1)
Same as above except for beta
which now leads to a mean
of
-
exp(log(4)) = 4
for the control group, -
exp(log(4) + log(0.8)) = 3.2
(small effect) for group “A” which corresponds to a RR of3.2/4 = 0.8
, -
exp(log(4) + log(0.6)) = 2.4
(medium effect) for group “B” which corresponds to a RR of2.4/4 = 0.6
, -
exp(log(4) + log(0.4)) = 1.6
(target effect) for group “C” which corresponds to a RR of1.6/4 = 0.4
.