Title: | Interim Analysis of Operational Futility in Randomized Trials with Time-to-Event Endpoints and Fixed Follow-Up |
---|---|
Description: | Randomized clinical trials commonly follow participants for a time-to-event efficacy endpoint for a fixed period of time. Consequently, at the time when the last enrolled participant completes their follow-up, the number of observed endpoints is a random variable. Assuming data collected through an interim timepoint, simulation-based estimation and inferential procedures in the standard right-censored failure time analysis framework are conducted for the distribution of the number of endpoints--in total as well as by treatment arm--at the end of the follow-up period. The future (i.e., yet unobserved) enrollment, endpoint, and dropout times are generated according to mechanisms specified in the simTrial() function in the 'seqDesign' package. A Bayesian model for the endpoint rate, offering the option to specify a robust mixture prior distribution, is used for generating future data (see the vignette for details). Inference can be restricted to participants who received treatment according to the protocol and are observed to be at risk for the endpoint at a specified timepoint. Plotting functions are provided for graphical display of results. |
Authors: | Yingying Zhuang [aut], Michal Juraska [aut, cre], Doug Grove [ctb], Peter Gilbert [ctb], Alexander Luedtke [ctb], Sanne Roels [ctb], An Vandebosch [ctb] |
Maintainer: | Michal Juraska <mjuraska@fredhutch.org> |
License: | GPL-2 |
Version: | 0.4 |
Built: | 2024-02-14 08:01:01 UTC |
Source: | CRAN |
Considers MITT data collected through an interim timepoint and generates independent time-to-event data-sets, by treatment arm, to assess the distribution of the number of treatment arm-specific endpoints at the end of the follow-up period. A Bayesian model for treatment arm-specific endpoint rates is used for generating future data (see the vignette).
completeTrial.byArm(interimData, nTrials, trtNames, N, enrollRate = NULL,
enrollRatePeriod, eventPriorWeight, eventPriorRate,
fixedDropOutRate = NULL, ppAnalysis = FALSE, missVaccProb = NULL,
ppAtRiskTimePoint = NULL, fuTime, visitSchedule,
visitSchedule2 = NULL, saveFile = NULL, saveDir = NULL,
randomSeed = NULL)
interimData |
a data frame capturing observed MITT data at an interim timepoint that contains one row per enrolled participant in the MITT cohort and the following variables: |
nTrials |
the number of trials to be simulated |
trtNames |
a character vector of treatment labels as specified in |
N |
a numeric vector specifying the target number of enrolled participants in each treatment arm, with the arms in the same order as in |
enrollRate |
a treatment arm-pooled weekly enrollment rate used for completing enrollment if |
enrollRatePeriod |
the length (in weeks) of the time period preceding the time of the last enrolled participant in |
eventPriorWeight |
a numeric value in |
eventPriorRate |
a numeric vector of treatment arm-specific prior mean incidence rates for the endpoint, expressed as numbers of events per person-year at risk, with the arms in the same order as in |
fixedDropOutRate |
the pre-trial assumed annual treatment arm-pooled dropout rate. If |
ppAnalysis |
a logical value ( |
missVaccProb |
a probability that a participant misses at least one vaccination. If |
ppAtRiskTimePoint |
a minimal follow-up time (in weeks) for a participant to qualify for inclusion in the per-protocol cohort ( |
fuTime |
a follow-up time (in weeks) of each participant |
visitSchedule |
a numeric vector of visit weeks at which testing for the endpoint is conducted |
visitSchedule2 |
a numeric vector of visit weeks at which testing for the endpoint is conducted in a subset of participants (e.g., those who discontinue administration of the study product but remain in follow-up). If |
saveFile |
a character string specifying an |
saveDir |
a character string specifying a path for the output directory. If supplied, the output is saved as an |
randomSeed |
seed of the random number generator for simulation reproducibility |
If saveDir
is specified, the output list (named trialObj
) is saved as an .RData
file; otherwise it is returned. The output object is a list with the following components:
trialData
: a list with nTrials
components each of which is a data.frame
with the variables arm
, entry
, exit
, event
, and dropout
storing the treatment assignments, enrollment times, study exit times, event indicators, and dropout indicators, respectively. The observed follow-up times can be recovered as exit
- entry
. If ppAnalysis=TRUE
, then the indicators of belonging to the per-protocol cohort (named pp
) are included.
nTrials
: the number of simulated trials
N
: the total number of enrolled trial participants
rates
: a list with three components:
enrollRate
: the treatment arm-pooled weekly enrollment rate
dropRate
: fixedDropOutRate
, or, if NULL
, the annual treatment arm-pooled dropout rate in interimData
eventPostRate
: a list with length(trtNames)
components (labeled by the levels of the arm
variable in interimData
) each of which is a numeric vector of length nTrials
of the sampled treatment arm-specific posterior annual event rates
BetaOverBetaPlusTk
: a list with length(trtNames)
components (labeled by the levels of the arm
variable in interimData
) each of which is the arm-specific weight placed on the prior mean event rate
TkOverTstar
: a list with length(trtNames)
components (labeled by the levels of the arm
variable in interimData
) each of which is the ratio of the observed arm-specific person-time at risk to the estimated total arm-specific person-time at risk, with the arm-specific event rates set equal to the components of eventPriorRate
in the estimator for the total arm-specific person-time at risk
randomSeed
: seed of the random number generator for simulation reproducibility
arm <- rep(c("C3","T1","T2"), each=250)
schedule <- rbinom(length(arm), 1, 0.01)
entry <- rpois(length(arm), lambda=60)
entry <- entry - min(entry)
last_visit_dt <- entry + runif(length(arm), min=0, max=80)
event <- rbinom(length(arm), 1, 0.01)
dropout <- rbinom(length(arm), 1, 0.02)
dropout[event==1] <- 0
exit <- rep(NA, length(arm))
exit[event==1] <- last_visit_dt[event==1] + 5
exit[dropout==1] <- last_visit_dt[dropout==1] + 5
followup <- ifelse(event==1 | dropout==1, 0, 1)
interimData <- data.frame(arm=arm, schedule2=schedule, entry=entry, exit=exit,
last_visit_dt=last_visit_dt, event=event, dropout=dropout, complete=0, followup=followup)
completeData <- completeTrial.byArm(interimData=interimData, nTrials=5,
trtNames=c("C3","T1","T2"), N=c(500,500,500), enrollRatePeriod=24, eventPriorWeight=0.5,
eventPriorRate=c(0.001,0.0004,0.0004), fuTime=80, visitSchedule=seq(0, 80, by=4),
visitSchedule2=c(0,seq(from=8,to=80,by=12)), randomSeed=9)
### alternatively, to save the .RData output file (no '<-' needed):
completeTrial.byArm(interimData=interimData, nTrials=5, trtNames=c("C3","T1","T2"),
N=c(500,500,500), enrollRatePeriod=24, eventPriorWeight=0.5,
eventPriorRate=c(0.001,0.0004,0.0004), fuTime=80, visitSchedule=seq(0, 80, by=4),
visitSchedule2=c(0,seq(from=8,to=80,by=12)), saveDir="./", randomSeed=9)
Considers MITT data collected through an interim timepoint and generates independent time-to-event data-sets, ignoring treatment assignments, to assess the distribution of the number of treatment arm-pooled endpoints at the end of the follow-up period. A Bayesian model for the treatment arm-pooled endpoint rate, offering the option to specify a robust mixture prior distribution, is used for generating future data (see the vignette).
completeTrial.pooledArms(interimData, nTrials, N, enrollRate = NULL,
enrollRatePeriod = NULL, eventPriorWeight, eventPriorRate = NULL,
fixedDropOutRate = NULL, ppAnalysis = FALSE, missVaccProb = NULL,
ppAtRiskTimePoint = NULL, fuTime, mixture = FALSE,
mix.weights = NULL, eventPriorWeightRobust = NULL, visitSchedule,
visitSchedule2 = NULL, saveFile = NULL, saveDir = NULL,
randomSeed = NULL)
interimData |
a data frame capturing observed MITT data at an interim timepoint that contains one row per enrolled participant in the MITT cohort and the following variables: |
nTrials |
the number of trials to be simulated |
N |
the total target number of enrolled participants |
enrollRate |
a treatment arm-pooled weekly enrollment rate used for completing enrollment if fewer than |
enrollRatePeriod |
the length (in weeks) of the time period preceding the time of the last enrolled participant in |
eventPriorWeight |
a numeric value in |
eventPriorRate |
a numeric value of a treatment arm-pooled prior mean incidence rate for the endpoint, expressed as the number of events per person-year at risk. If |
fixedDropOutRate |
the pre-trial assumed annual dropout rate. If |
ppAnalysis |
a logical value ( |
missVaccProb |
a probability that a participant misses at least one vaccination. If |
ppAtRiskTimePoint |
a minimal follow-up time (in weeks) for a participant to qualify for inclusion in the per-protocol cohort ( |
fuTime |
a follow-up time (in weeks) of each participant |
mixture |
a logical value indicating whether to use the robust mixture approach (see the vignette). If equal to |
mix.weights |
a numeric vector of length 2 representing prior weights (values in |
eventPriorWeightRobust |
a numeric value representing the weight |
visitSchedule |
a numeric vector of visit weeks at which testing for the endpoint is conducted |
visitSchedule2 |
a numeric vector of visit weeks at which testing for the endpoint is conducted in a subset of participants (e.g., those who discontinue administration of the study product but remain in follow-up). If |
saveFile |
a character string specifying an |
saveDir |
a character string specifying a path for the output directory. If supplied, the output is saved as an |
randomSeed |
seed of the random number generator for simulation reproducibility |
If saveDir
is specified, the output list (named trialObj
) is saved as an .RData
file; otherwise it is returned. The output object is a list with the following components:
trialData
: a list with nTrials
components each of which is a data.frame
with the variables arm
, entry
, exit
, event
, and dropout
storing the treatment assignments, enrollment times, study exit times, event indicators, and dropout indicators respectively. The observed follow-up times can be recovered as exit
- entry
. If ppAnalysis=TRUE
, then the indicators of belonging to the per-protocol cohort (named pp
) are included.
nTrials
: the number of simulated trials
N
: the total number of enrolled trial participants
rates
: a list with three components:
enrollRate
: the treatment arm-pooled weekly enrollment rate
dropRate
: fixedDropOutRate
, or, if NULL
, the annual treatment arm-pooled dropout rate in interimData
eventPostRate
: a numeric vector of length nTrials
of the treatment arm-pooled annual event rates sampled from the posterior distribution
BetaOverBetaPlusTk
: the weight placed on the prior mean event rate
TkOverTstar
: the ratio of the observed person-time at risk to the estimated total person-time at risk, with the event rate set equal to eventPriorRate
in the estimator for the total person-time at risk
randomSeed
: seed of the random number generator for simulation reproducibility
w.post
: the weights, summing up to 1, of the gamma components of the posterior mixture distribution of the treatment arm-pooled event rate. If mixture=FALSE
, then w.post=NA
.
arm <- rep(c("C3","T1","T2"), each=250)
schedule <- rbinom(length(arm), 1, 0.01)
entry <- rpois(length(arm), lambda=60)
entry <- entry - min(entry)
last_visit_dt <- entry + runif(length(arm), min=0, max=80)
event <- rbinom(length(arm), 1, 0.01)
dropout <- rbinom(length(arm), 1, 0.02)
dropout[event==1] <- 0
exit <- rep(NA, length(arm))
exit[event==1] <- last_visit_dt[event==1] + 5
exit[dropout==1] <- last_visit_dt[dropout==1] + 5
followup <- ifelse(event==1 | dropout==1, 0, 1)
interimData <- data.frame(arm=arm, schedule2=schedule, entry=entry, exit=exit,
last_visit_dt=last_visit_dt, event=event, dropout=dropout, complete=0,
followup=followup)
completeData <- completeTrial.pooledArms(interimData=interimData, nTrials=5, N=1500,
enrollRatePeriod=24, eventPriorWeight=0.5, eventPriorRate=0.001, fuTime=80,
visitSchedule=seq(0, 80, by=4),
visitSchedule2=c(0,seq(from=8,to=80,by=12)), randomSeed=9)
### alternatively, to save the .RData output file (no '<-' needed):
completeTrial.pooledArms(interimData=interimData, nTrials=5, N=1500,
enrollRatePeriod=24, eventPriorWeight=0.5, eventPriorRate=0.001, fuTime=80,
visitSchedule=seq(0, 80, by=4),
visitSchedule2=c(0,seq(from=8,to=80,by=12)), saveDir="./", randomSeed=9)
Takes the output from the completeTrial.byArm
function and generates a plot describing characteristics of the estimated distribution of the treatment arm-specific number of endpoints.
plotRCDF.byArm(armLabel, trtNames, eventTimeFrame = NULL,
eventPPcohort = FALSE, eventPriorRate, eventPriorWeight, xlim = NULL,
xlab = NULL, ylab = NULL, fileDir)
armLabel |
a character string matching a treatment label in the |
trtNames |
a character vector of all treatment labels listed in the same order as in |
eventTimeFrame |
a time frame within which endpoints are counted, specified in weeks as |
eventPPcohort |
a logical value. If |
eventPriorRate |
a numeric vector of treatment arm-specific prior mean incidence rates for the endpoint, expressed as numbers of events per person-year at risk, matching the order of treatment arms in |
eventPriorWeight |
a numeric vector in which each value represents a weight (i.e., a separate scenario) assigned to the prior gamma distribution of the treatment arm-specific event rate at the time when 50% of the estimated person-time at risk in the given |
xlim |
a numeric vector of the form |
xlab |
a character string for the user-specified x-axis label. If |
ylab |
a character string for the user-specified y-axis label. If |
fileDir |
a character string specifying a path for the input directory |
None. The function is called solely for plot generation.
completeTrial.byArm
and plotRCDF.pooledArms
arm <- rep(c("C3","T1","T2"), each=250)
schedule <- rbinom(length(arm), 1, 0.01)
entry <- rpois(length(arm), lambda=60)
entry <- entry - min(entry)
last_visit_dt <- entry + runif(length(arm), min=0, max=80)
event <- rbinom(length(arm), 1, 0.01)
dropout <- rbinom(length(arm), 1, 0.02)
dropout[event==1] <- 0
exit <- rep(NA, length(arm))
exit[event==1] <- last_visit_dt[event==1] + 5
exit[dropout==1] <- last_visit_dt[dropout==1] + 5
followup <- ifelse(event==1 | dropout==1, 0, 1)
interimData <- data.frame(arm=arm, schedule2=schedule, entry=entry, exit=exit,
last_visit_dt=last_visit_dt, event=event, dropout=dropout, complete=0, followup=followup)
weights <- c(0.2, 0.4, 0.6)
for (j in 1:length(weights)){
completeTrial.byArm(interimData=interimData, nTrials=50,
trtNames=c("C3","T1","T2"),N=c(500,500,500),
enrollRatePeriod=24, eventPriorWeight=weights[j], eventPriorRate=c(0.06,0.03,0.03),
fuTime=80, visitSchedule=seq(0, 80, by=4), visitSchedule2=c(0,seq(from=8,to=80,by=12)),
saveDir="./", randomSeed=9)
}
pdf(file=paste0("./","rcdf_byArm_arm=T1_",
"eventPriorRateC3=0.06_eventPriorRateT1=0.03_eventPriorRateT2=0.03.pdf"), width=6,
height=5)
plotRCDF.byArm(armLabel="T1", trtNames=c("C3","T1","T2"), eventPriorRate=c(0.06,0.03,0.03),
eventPriorWeight=weights, fileDir="./")
dev.off()
Takes the output from the completeTrial.pooledArms
function and generates a plot describing characteristics of the estimated distribution of the treatment arm-pooled number of endpoints.
plotRCDF.pooledArms(eventTimeFrame = NULL, eventPPcohort = FALSE,
target, power.axis = TRUE, power.TE = NULL, eventPriorRate,
eventPriorWeight, xlim = NULL, xlab = NULL, ylab = NULL,
power.lab = NULL, xPosLegend = 0.67, fileDir)
eventTimeFrame |
a time frame within which endpoints are counted, specified in weeks as |
eventPPcohort |
a logical value. If |
target |
a vector of target numbers of endpoints for reporting of the estimated probability that the total number of endpoints will be |
power.axis |
a logical value. If |
power.TE |
a numeric value of treatment efficacy for which power is shown on the top axis. If |
eventPriorRate |
a numeric value of the treatment arm-pooled prior mean incidence rate for the endpoint, expressed as the number of events per person-year at risk |
eventPriorWeight |
a numeric vector in which each value represents a weight (i.e., a separate scenario) assigned to the prior gamma distribution of the treatment arm-pooled event rate at the time when 50% of the estimated total person-time at risk has been accumulated |
xlim |
a numeric vector of the form |
xlab |
a character string for the user-specified x-axis label. If |
ylab |
a character string for the user-specified y-axis label. If |
power.lab |
a character string for the user-specified power-axis label. If |
xPosLegend |
a numeric value in |
fileDir |
a character string specifying a path for the input directory |
None. The function is called solely for plot generation.
completeTrial.pooledArms
and plotRCDF.byArm
arm <- rep(c("C3","T1","T2"), each=250)
schedule <- rbinom(length(arm), 1, 0.01)
entry <- rpois(length(arm), lambda=60)
entry <- entry - min(entry)
last_visit_dt <- entry + runif(length(arm), min=0, max=80)
event <- rbinom(length(arm), 1, 0.01)
dropout <- rbinom(length(arm), 1, 0.02)
dropout[event==1] <- 0
exit <- rep(NA, length(arm))
exit[event==1] <- last_visit_dt[event==1] + 5
exit[dropout==1] <- last_visit_dt[dropout==1] + 5
followup <- ifelse(event==1 | dropout==1, 0, 1)
interimData <- data.frame(arm=arm, schedule2=schedule, entry=entry, exit=exit,
last_visit_dt=last_visit_dt, event=event, dropout=dropout, complete=0,
followup=followup)
weights <- c(0.2, 0.4, 0.6)
for (j in 1:length(weights)){
completeTrial.pooledArms(interimData=interimData, nTrials=50, N=1500, enrollRatePeriod=24,
eventPriorWeight=weights[j], eventPriorRate=0.06, fuTime=80, visitSchedule=seq(0, 80, by=4),
visitSchedule2=c(0,seq(from=8,to=80,by=12)), saveDir="./", randomSeed=9)
}
pdf(file=paste0("./","rcdf_pooled_eventPriorRate=",0.06,".pdf"), width=6, height=5)
plotRCDF.pooledArms(target=c(60,30), power.axis=FALSE, eventPriorRate=0.06,
eventPriorWeight=weights, fileDir="./")
dev.off()