library(gsDesign2) library(gt) library(dplyr) library(tibble) library(ggplot2)
We set up futility bounds under a non-proportional hazards assumption. We consider methods presented by @kornfreidlin2018 for setting such bounds and then consider an alternate futility bound based on $\beta-$spending under a delayed or crossing treatment effect to simplify implementation. Finally, we show how to update this $\beta-$spending bound based on blinded interim data. We will consider an example to reproduce a line of @kornfreidlin2018 Table 1 with the alternative futility bounds considered.
@kornfreidlin2018 considered delayed effect scenarios and proposed a futility bound that is a modification of an earlier method proposed by @wieand. We begin with the enrollment and failure rate assumptions which @kornfreidlin2018 based on an example by @ttchen2013.
# Enrollment assumed to be 680 patients over 12 months with no ramp-up enrollRates <- tibble(Stratum = "All", duration = 12, rate = 680 / 12) # Failure rates ## Control exponential with median of 12 mos ## Delayed effect with HR = 1 for 3 months and HR = .693 thereafter ## Censoring rate is 0 failRates <- tibble(Stratum = "All", duration = c(3, 100), failRate = -log(.5) / 12, hr = c(1, .693), dropoutRate = 0) ## Study duration was 34.8 in Korn & Freidlin Table 1 ## We change to 34.86 here to obtain 512 expected events more precisely studyDuration <- 34.86
We now derive a fixed sample size based on these assumptions.
Ideally, we would allow a targeted event count and variable follow-up in fixed_design()
so that the study duration will be computed automatically.
fixedevents <- fixed_design(x = "AHR", alpha = 0.025, power = NULL, enrollRates = enrollRates, failRates = failRates, studyDuration = studyDuration) fixedevents %>% summary() %>% select(-Bound) %>% as_gt(footnote="Power based on 512 events") %>% fmt_number(columns = 3:4, decimals = 2) %>% fmt_number(columns = 5:6, decimals = 3)
The @wieand rule recommends stopping after 50% of planned events accrue if the observed HR > 1.
kornfreidlin2018 modified this by adding a second interim analysis after 75% of planned events and stop if the observed HR > 1
This is implemented here by requiring a trend in favor of control with a direction $Z$-bound at 0 resulting in the Nominal p bound being 0.5 for interim analyses in the table below.
A fixed bound is specified with the 'gs_b()function for
upperand
lowerand its correspoinding parameters
uparfor the upper (efficacy) bound and
lparfor the lower (futility) bound.
The final efficacy bound is for a 1-sided nominal p-value of 0.025; the futility bound lowers this to 0.0247 as noted in the lower-right-hand corner of the table below.
In the last row under *Alternate hypothesis* below we see the power is 88.44%.
@kornfreidlin2018 computed 88.4% power for this design with 100,000 simulations which estimate the standard error for the power calculation to be
r paste(100 * round(sqrt(.884 * (1 - .884)/100000),4),'%',sep='')`.
wieand <- gs_power_ahr(enrollRates = enrollRates, failRates = failRates, upper = gs_b, upar = c(rep(Inf, 2), qnorm(.975)), lower = gs_b, lpar = c(0, 0, -Inf), events = 512 * c(.5, .75, 1)) wieand %>% summary() %>% as_gt(title="Group sequential design with futility only at interim analyses", subtitle="Wieand futility rule stops if HR > 1")
Need to summarize here.
betaspending <- gs_power_ahr(enrollRates = enrollRates, failRates = failRates, upper = gs_b, upar = c(rep(Inf, 2), qnorm(.975)), lower = gs_spending_bound, lpar = list(sf = gsDesign::sfLDOF, total_spend = 0.025, param = NULL, timing = NULL), events = 512 * c(.5, .75, 1), test_lower = c(TRUE, TRUE, FALSE)) betaspending %>% summary() %>% as_gt(title="Group sequential design with futility only", subtitle="Beta-spending futility bound")
A classical $\beta-$spending bound would assume a constant treatment effect over time using the proportional hazards assumption. We use the average hazard ratio at the fixed design analysis for this purpose.
The @kornfreidlin2018 futility bound is set when at least 50% of the expected events have occurred and at least two thirds of the observed events have occurred later than 3 months from randomization. The expected timing for this is demonstrated below.
We consider the accumulation of events over time that occur during the no-effect interval for the first 3 months after randomization and events after this time interval.
This is done for the overall trial without dividing out by treatment group using the gsDesign2::AHR()
function.
We consider monthly accumulation of events through the 34.86 months planned trial duration.
We note in the summary of early expected events below that all events during the first 3 months on-study are expected prior to the first interim analysis.
event_accumulation <- AHR(enrollRates = enrollRates, failRates = failRates, totalDuration = c(1:34, 34.86), ratio = 1, simple = FALSE) head(event_accumulation, n = 7) %>% gt()
We can look at the proportion of events after the first 3 months as follows:
event_accumulation %>% group_by(Time) %>% summarize(`Total events` = sum(Events), "Proportion early" = first(Events) / `Total events`) %>% ggplot(aes(x=Time, y=`Proportion early`)) + geom_line()
For the @kornfreidlin2018 bound the targeted timing is when both 50% of events have occurred and at least 2/3 are more than 3 months after enrollment with 3 months being the delayed effect period. We see above that about 1/3 of events are still within 3 months of enrollment at month 20.
The bound proposed by @kornfreidlin2018
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.