Introduction of gs_power_npe()
gs_power_npe()
derives group sequential bounds and boundary crossing probabilities for a design. It allows a non-constant treatment effect over time, but also can be applied for the usual homogeneous effect size designs. It requires + treatment effect (theta
, theta1
) + statistical information at each analysis (info
, info0
, info1
) + a method of deriving bounds, such as fixed bounds or spending (upper
, upar
, lower
, lpar
).
The routine enables two things not available in the gsDesign package:
- non-constant effect,
- more flexibility in boundary selection.
Usage of gs_power_npe()
Example 1: Fixed bound
no futility bound
# Same fixed efficacy bounds, (i.e., non-binding bound), null hypothesis
gs_power_npe(
theta = rep(0, 3),
info = (1:3) * 40,
upar = gsDesign::gsDesign(k = 3,sfu = gsDesign::sfLDOF)$upper$bound,
lpar = rep(-Inf, 3)) %>%
filter(Bound == "Upper") %>% gt()
Analysis |
Bound |
Z |
Probability |
theta |
theta1 |
IF |
info |
info0 |
info1 |
1 |
Upper |
3.710303 |
0.0001035057 |
0 |
0 |
0.3333333 |
40 |
40 |
40 |
2 |
Upper |
2.511427 |
0.0060485045 |
0 |
0 |
0.6666667 |
80 |
80 |
80 |
3 |
Upper |
1.993048 |
0.0250002354 |
0 |
0 |
1.0000000 |
120 |
120 |
120 |
with futility bound
# Fixed bound
gs_power_npe(
theta = c(.1, .2, .3),
info = (1:3) * 40,
upper = gs_b,
upar = gsDesign::gsDesign(k = 3,sfu = gsDesign::sfLDOF)$upper$bound,
lower = gs_b,
lpar = c(-1, 0, 0)) %>% gt()
Analysis |
Bound |
Z |
Probability |
theta |
theta1 |
IF |
info |
info0 |
info1 |
1 |
Upper |
3.710303 |
0.001042508 |
0.1 |
0.1 |
0.3333333 |
40 |
40 |
40 |
2 |
Upper |
2.511427 |
0.234899008 |
0.2 |
0.2 |
0.6666667 |
80 |
80 |
80 |
3 |
Upper |
1.993048 |
0.868689026 |
0.3 |
0.3 |
1.0000000 |
120 |
120 |
120 |
1 |
Lower |
-1.000000 |
0.051291779 |
0.1 |
0.1 |
0.3333333 |
40 |
40 |
40 |
2 |
Lower |
0.000000 |
0.071509147 |
0.2 |
0.2 |
0.6666667 |
80 |
80 |
80 |
3 |
Lower |
0.000000 |
0.071522125 |
0.3 |
0.3 |
1.0000000 |
120 |
120 |
120 |
futility only at analysis 1
gs_power_npe(
theta = c(.1, .2, .3),
info = (1:3) * 40,
upper = gs_b,
upar = c(Inf, 3, 2),
lower = gs_b,
lpar = c(qnorm(.1), -Inf, -Inf)) %>% gt()
Analysis |
Bound |
Z |
Probability |
theta |
theta1 |
IF |
info |
info0 |
info1 |
1 |
Upper |
Inf |
0.00000000 |
0.1 |
0.1 |
0.3333333 |
40 |
40 |
40 |
2 |
Upper |
3.000000 |
0.11291848 |
0.2 |
0.2 |
0.6666667 |
80 |
80 |
80 |
3 |
Upper |
2.000000 |
0.88742529 |
0.3 |
0.3 |
1.0000000 |
120 |
120 |
120 |
1 |
Lower |
-1.281552 |
0.02780962 |
0.1 |
0.1 |
0.3333333 |
40 |
40 |
40 |
2 |
Lower |
-Inf |
0.02780962 |
0.2 |
0.2 |
0.6666667 |
80 |
80 |
80 |
3 |
Lower |
-Inf |
0.02780962 |
0.3 |
0.3 |
1.0000000 |
120 |
120 |
120 |
Example 2: spending bounds
lower spending based on non-zero effect
gs_power_npe(
theta = c(.1, .2, .3), # non-zero effect
info = (1:3) * 40,
upper = gs_spending_bound,
upar = list(sf = gsDesign::sfLDOF, total_spend = 0.025, param = NULL, timing = NULL),
lower = gs_spending_bound,
lpar = list(sf = gsDesign::sfHSD, total_spend = 0.1, param = -1, timing = NULL))
## # A tibble: 6 × 10
## Analysis Bound Z Probability theta theta1 IF info info0 info1
## <int> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1 Upper 3.71 0.00104 0.1 0.1 0.333 40 40 40
## 2 2 Upper 2.51 0.235 0.2 0.2 0.667 80 80 80
## 3 3 Upper 1.99 0.883 0.3 0.3 1 120 120 120
## 4 1 Lower -1.36 0.0230 0.1 0.1 0.333 40 40 40
## 5 2 Lower 0.0726 0.0552 0.2 0.2 0.667 80 80 80
## 6 3 Lower 1.86 0.100 0.3 0.3 1 120 120 120
2-sided symmetric spend
x <- gs_power_npe(
theta = rep(0, 3),
info = (1:3) * 40,
# typically, 2-sided bounds are binding
binding = TRUE,
upper = gs_spending_bound,
# O'Brien-Fleming spending
upar = list(sf = gsDesign::sfLDOF, total_spend = 0.025, param = NULL, timing = NULL),
lower = gs_spending_bound,
lpar = list(sf = gsDesign::sfLDOF, total_spend = 0.025, param = NULL, timing = NULL))
x %>% gt()
Analysis |
Bound |
Z |
Probability |
theta |
theta1 |
IF |
info |
info0 |
info1 |
1 |
Upper |
3.710303 |
0.0001035057 |
0 |
0 |
0.3333333 |
40 |
40 |
40 |
2 |
Upper |
2.511434 |
0.0060483891 |
0 |
0 |
0.6666667 |
80 |
80 |
80 |
3 |
Upper |
1.993051 |
0.0250000000 |
0 |
0 |
1.0000000 |
120 |
120 |
120 |
1 |
Lower |
-3.710303 |
0.0001035057 |
0 |
0 |
0.3333333 |
40 |
40 |
40 |
2 |
Lower |
-2.511434 |
0.0060483891 |
0 |
0 |
0.6666667 |
80 |
80 |
80 |
3 |
Lower |
-1.993051 |
0.0250000000 |
0 |
0 |
1.0000000 |
120 |
120 |
120 |
# Re-use these bounds under alternate hypothesis
# Always use binding = TRUE for power calculations
gs_power_npe(
theta = c(.1, .2, .3),
info = (1:3) * 40,
binding = TRUE,
upar = (x %>% filter(Bound == "Upper"))$Z,
lpar = -(x %>% filter(Bound == "Upper"))$Z) %>% gt()
Analysis |
Bound |
Z |
Probability |
theta |
theta1 |
IF |
info |
info0 |
info1 |
1 |
Upper |
3.710303 |
1.042508e-03 |
0.1 |
0.1 |
0.3333333 |
40 |
40 |
40 |
2 |
Upper |
2.511434 |
2.349812e-01 |
0.2 |
0.2 |
0.6666667 |
80 |
80 |
80 |
3 |
Upper |
1.993051 |
9.020711e-01 |
0.3 |
0.3 |
1.0000000 |
120 |
120 |
120 |
1 |
Lower |
-3.710303 |
7.035242e-06 |
0.1 |
0.1 |
0.3333333 |
40 |
40 |
40 |
2 |
Lower |
-2.511434 |
1.510031e-05 |
0.2 |
0.2 |
0.6666667 |
80 |
80 |
80 |
3 |
Lower |
-1.993051 |
1.512598e-05 |
0.3 |
0.3 |
1.0000000 |
120 |
120 |
120 |
Inner Logic of gs_spending_bound()
TODO