10 Scenario IX: time-to-event endpoints
10.1 Details
In this scenario, we visit a different scenario: Instead of continuous or binary endpoints, we now consider time-to-event endpoints. Therefore, we conduct a log-rank test, for which adoptr
provides the normal approximation of the test statistic. In order to obtain a one-parametric statistic, we need to fix the event probability \(d\) during the trial. We assume that a hazard ratio \(\theta>1\) implies favorable results. The null-hypothesis is given by \(\mathcal{H}_0:\theta\leq 1\). In both of the following two scenarios, we assume a constant event rate of \(d=0.7\) and the type one error should be bounded by \(\alpha<0.025\) and a power of \(0.8\) is necessary.
Attention: adoptr
calculates the number of necessary events, not the sample size. Thus, e.g. the function ExpectedSampleSize
does not really represent the expected sample size, but the expected number of events. To make a clear distinction, one can use ExpectedNumberOfEvents
instead.
The sample size can be calculated by dividing the number of events by the event rate.
<- Survival(0.7, two_armed = TRUE)
datadist <- PointMassPrior(1.0, 1)
H_0 <- 0.025
alpha <- 0.8 min_power
10.2 Variant IX-1: Minimizing expected number of events under point prior
Under the alternative, we us a hazard ratio of \(\theta=1.4\) for the point prior of the alternative.
<- PointMassPrior(1.4, 1)
prior
<- Power(datadist, H_0) <= alpha
toer_cnstr <- Power(datadist, prior) >= min_power pow_cnstr
10.2.1 Objective
The expected number of events under the alternative is minimized.
<- ExpectedNumberOfEvents(datadist, prior) ess
10.2.2 Constraints
No additional constraints besides type one and type two error are considered in this variant.
10.2.3 Initial Design
The initial group sequential, two-stage and one-stage designs are chosen heuristically.
<- 7L
order # data frame of initial designs
<- tibble(
tbl_designs type = c("one-stage", "group-sequential", "two-stage"),
initial = list(
OneStageDesign(150, 2.0, event_rate = 0.7),
GroupSequentialDesign(75, 0.8, 2.3, 170,
c2_pivots=c(0.9, 1.1, 1.3, 1.5, 1.7, 1.9, 2.1),
event_rate = 0.7),
TwoStageDesign(75, 0.8, 2.3,
c(220, 200, 170, 150, 140, 130, 120),
c2_pivots = c(0.9, 1.1, 1.3, 1.5, 1.7, 1.9, 2.1),
event_rate = 0.7)) )
The order of the integration is set to 7.
10.2.4 Optimization
<- tbl_designs %>%
tbl_designs mutate(
optimal = purrr::map(initial, ~minimize(
ess,subject_to(
toer_cnstr,
pow_cnstr
),
initial_design = .,
opts = opts)) )
10.2.5 Test cases
We first check that the maximum number of iterations was not reached.
%>%
tbl_designs transmute(
type, iterations = purrr::map_int(tbl_designs$optimal,
~.$nloptr_return$iterations) ) %>%
print(.); .} %>%
{::expect_true(all(.$iterations < opts$maxeval))} {testthat
## # A tibble: 3 × 2
## type iterations
## <chr> <int>
## 1 one-stage 22
## 2 group-sequential 1259
## 3 two-stage 2620
Besides, we check whether the type one error was not exceeded and the necessary power is reached.
%>%
tbl_designs transmute(
type, toer = purrr::map(tbl_designs$optimal,
~sim_pr_reject(.[[1]], 1.0, datadist)$prob),
power = purrr::map(tbl_designs$optimal,
~sim_pr_reject(.[[1]], 1.4, datadist)$prob) ) %>%
unnest(., cols = c(toer, power)) %>%
print(.); .} %>% {
{::expect_true(all(.$toer <= alpha * (1 + tol)))
testthat::expect_true(all(.$power >= min_power * (1 - tol))) } testthat
## # A tibble: 3 × 3
## type toer power
## <chr> <dbl> <dbl>
## 1 one-stage 0.0251 0.801
## 2 group-sequential 0.0250 0.798
## 3 two-stage 0.0250 0.801
Since the degrees of freedom of the three design classes are ordered as
‘two-stage’ > ‘group-sequential’ > ‘one-stage’,
the expected sample sizes (under the alternative) should be ordered
in reverse (‘two-stage’ smallest).
Additionally, expected sample sizes under both null and alternative
are computed both via evaluate()
and simulation-based.
<- ExpectedNumberOfEvents(datadist, H_0)
ess0
%>%
tbl_designs mutate(
ess = map_dbl(optimal,
~evaluate(ess, .$design) ),
ess_sim = map_dbl(optimal,
~sim_n(.$design, 1.4, datadist)$n ),
ess0 = map_dbl(optimal,
~evaluate(ess0, .$design) ),
ess0_sim = map_dbl(optimal,
~sim_n(.$design, 1.0, datadist)$n ) ) %>%
print(.); .} %>% {
{# sim/evaluate same under alternative?
::expect_equal(.$ess, .$ess_sim,
testthattolerance = tol_n,
scale = 1)
# sim/evaluate same under null?
::expect_equal(.$ess0, .$ess0_sim,
testthattolerance = tol_n,
scale = 1)
# monotonicity with respect to degrees of freedom
::expect_true(all(diff(.$ess) < 0)) } testthat
## # A tibble: 3 × 7
## type initial optimal ess ess_sim ess0 ess0_sim
## <chr> <list> <list> <dbl> <dbl> <dbl> <dbl>
## 1 one-stage <OnStgDsS> <adptrOpR [3]> 139 139 139 139
## 2 group-sequential <GrpSqnDS> <adptrOpR [3]> 114. 114. 96.3 96.4
## 3 two-stage <TwStgDsS> <adptrOpR [3]> 113. 113. 98.2 98.3
The expected sample size under the alternative must be lower or equal than
the expected sample size of the initial rpact
group-sequential design that
is based on the inverse normal combination test.
::expect_lte(
testthatevaluate(ess,
%>%
tbl_designs filter(type == "group-sequential") %>%
pull(optimal) %>%
1]] %>%
.[[$design ),
.evaluate(ess,
%>%
tbl_designs filter(type == "group-sequential") %>%
pull(initial) %>%
1]] ) ) .[[