Skip to contents

gs_design_npe() derives group sequential design size, bounds and boundary crossing probabilities based on proportionate information and effect size at analyses. 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 and proportionate statistical information at each analysis as well as a method of deriving bounds, such as spending. The routine enables two things not available in the gsDesign package: 1) non-constant effect, 2) more flexibility in boundary selection. For many applications, the non-proportional-hazards design function gs_design_nph() will be used; it calls this function. Initial bound types supported are 1) spending bounds, 2) fixed bounds, and 3) Haybittle-Peto-like bounds. The requirement is to have a boundary update method that can each bound without knowledge of future bounds. As an example, bounds based on conditional power that require knowledge of all future bounds are not supported by this routine; a more limited conditional power method will be demonstrated. Boundary family designs Wang-Tsiatis designs including the original (non-spending-function-based) O'Brien-Fleming and Pocock designs are not supported by gs_power_npe().

Usage

gs_design_npe(
  theta = 0.1,
  theta0 = NULL,
  theta1 = NULL,
  info = 1,
  info0 = NULL,
  info1 = NULL,
  info_scale = c(0, 1, 2),
  alpha = 0.025,
  beta = 0.1,
  upper = gs_b,
  upar = qnorm(0.975),
  lower = gs_b,
  lpar = -Inf,
  test_upper = TRUE,
  test_lower = TRUE,
  binding = FALSE,
  r = 18,
  tol = 1e-06
)

Arguments

theta

natural parameter for group sequential design representing expected incremental drift at all analyses; used for power calculation

theta0

natural parameter used for upper bound spending; if NULL, this will be set to 0

theta1

natural parameter used for lower bound spending; if NULL, this will be set to theta which yields the usual beta-spending. If set to 0, spending is 2-sided under null hypothesis.

info

proportionate statistical information at all analyses for input theta

info0

proportionate statistical information under null hypothesis, if different than alternative; impacts null hypothesis bound calculation

info1

proportionate statistical information under alternate hypothesis; impacts null hypothesis bound calculation

info_scale

the information scale for calculation

alpha

One-sided Type I error

beta

Type II error

upper

function to compute upper bound

upar

parameter to pass to function provided in upper

lower

function to compare lower bound

lpar

Parameter passed to function provided in lower

test_upper

indicator of which analyses should include an upper (efficacy) bound; single value of TRUE (default) indicates all analyses; otherwise, a logical vector of the same length as info should indicate which analyses will have an efficacy bound

test_lower

indicator of which analyses should include an lower bound; single value of TRUE (default) indicates all analyses; single value FALSE indicated no lower bound; otherwise, a logical vector of the same length as info should indicate which analyses will have a lower bound

binding

indicator of whether futility bound is binding; default of FALSE is recommended

r

Integer, at least 2; default of 18 recommended by Jennison and Turnbull

tol

Tolerance parameter for boundary convergence (on Z-scale)

Value

a tibble with columns Analysis, Bound, Z, Probability, theta, info, info0

Details

The inputs info and info0 should be vectors of the same length with increasing positive numbers. The design returned will change these by some constant scale factor to ensure the design has power 1 - beta. The bound specifications in upper, lower, upar, lpar will be used to ensure Type I error and other boundary properties are as specified.

Specification

The contents of this section are shown in PDF user manual only.

Author

Keaven Anderson keaven_anderson@merck.com

Examples

library(dplyr)
library(gsDesign)

# ---------------------------------# 
#         example 1                #
# ---------------------------------# 
# Single analysis
# Lachin book p 71 difference of proportions example
pc <- .28            # Control response rate
pe <- .40            # Experimental response rate
p0 <- (pc + pe) / 2  # Ave response rate under H0

# Information per increment of 1 in sample size
info0 <- 1 / (p0 * (1 - p0) * 4)
info <- 1 / (pc * (1 - pc) * 2 + pe * (1 - pe) * 2)

# Result should round up to next even number = 652
# Divide information needed under H1 by information per patient added
gs_design_npe(theta = pe - pc, info = info, info0 = info0)
#> # A tibble: 1 × 10
#>   Analysis Bound     Z Probability Probability0 theta  info info0 info1    IF
#>      <dbl> <chr> <dbl>       <dbl>        <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1        1 Upper  1.96         0.9        0.025  0.12  737.  725.  737.     1


# ---------------------------------# 
#         example 2                #
# ---------------------------------# 
# Fixed bound
x <- gs_design_npe(
  theta = c(.1, .2, .3),
  info = (1:3) * 80,
  info0 = (1:3) * 80,
  upper = gs_b,
  upar = gsDesign::gsDesign(k = 3, sfu = gsDesign::sfLDOF)$upper$bound,
  lower = gs_b,
  lpar = c(-1, 0, 0))
x
#> # A tibble: 6 × 10
#>   Analysis Bound     Z Probability Probability0 theta    IF  info info0 info1
#>      <int> <chr> <dbl>       <dbl>        <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1        1 Upper  3.71     0.00120     0.000104   0.1 0.333  45.6  45.6  45.6
#> 2        1 Lower -1        0.0470      0.159      0.1 0.333  45.6  45.6  45.6
#> 3        2 Upper  2.51     0.273       0.00605    0.2 0.667  91.1  91.1  91.1
#> 4        2 Lower  0        0.0619      0.513      0.2 0.667  91.1  91.1  91.1
#> 5        3 Upper  1.99     0.900       0.0249     0.3 1     137.  137.  137. 
#> 6        3 Lower  0        0.0619      0.606      0.3 1     137.  137.  137. 

# Same upper bound; this represents non-binding Type I error and will total 0.025
gs_power_npe(
  theta = rep(0, 3),
  info = (x %>% filter(Bound == "Upper"))$info,
  upper = gs_b,
  upar = (x %>% filter(Bound == "Upper"))$Z,
  lower = gs_b,
  lpar = rep(-Inf, 3))
#> # 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.000104     0      0 0.333  45.6  45.6  45.6
#> 2        2 Upper    2.51    0.00605      0      0 0.667  91.1  91.1  91.1
#> 3        3 Upper    1.99    0.0250       0      0 1     137.  137.  137. 
#> 4        1 Lower -Inf       0            0      0 0.333  45.6  45.6  45.6
#> 5        2 Lower -Inf       0            0      0 0.667  91.1  91.1  91.1
#> 6        3 Lower -Inf       0            0      0 1     137.  137.  137. 

# ---------------------------------# 
#         example 3                #
# ---------------------------------# 
# Spending bound examples
# Design with futility only at analysis 1; efficacy only at analyses 2, 3
# Spending bound for efficacy; fixed bound for futility
# NOTE: test_upper and test_lower DO NOT WORK with gs_b; must explicitly make bounds infinite
# test_upper and test_lower DO WORK with gs_spending_bound
gs_design_npe(
  theta = c(.1, .2, .3),
  info = (1:3) * 40,
  info0 = (1:3) * 40,
  upper = gs_spending_bound,
  upar = list(sf = gsDesign::sfLDOF, total_spend = 0.025, param = NULL, timing = NULL),
  lower = gs_b,
  lpar = c(-1, -Inf, -Inf),
  test_upper = c(FALSE, TRUE, TRUE))
#> # A tibble: 6 × 10
#>   Analysis Bound       Z Probability Probability0 theta    IF  info info0 info1
#>      <int> <chr>   <dbl>       <dbl>        <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1        1 Upper  Inf         0           0         0.1 0.333  44.6  44.6  44.6
#> 2        1 Lower   -1         0.0477      0.159     0.1 0.333  44.6  44.6  44.6
#> 3        2 Upper    2.51      0.267       0.00605   0.2 0.667  89.1  89.1  89.1
#> 4        2 Lower -Inf         0.0477      0.159     0.2 0.667  89.1  89.1  89.1
#> 5        3 Upper    1.99      0.900       0.0249    0.3 1     134.  134.  134. 
#> 6        3 Lower -Inf         0.0477      0.159     0.3 1     134.  134.  134. 
  
# one can try `info_scale = 1` or `info_scale = 0` here
gs_design_npe(
  theta = c(.1, .2, .3),
  info = (1:3) * 40,
  info0 = (1:3) * 30,
  info_scale = 1,
  upper = gs_spending_bound,
  upar = list(sf = gsDesign::sfLDOF, total_spend = 0.025, param = NULL, timing = NULL),
  lower = gs_b,
  lpar = c(-1, -Inf, -Inf),
  test_upper = c(FALSE, TRUE, TRUE))
#> # A tibble: 6 × 10
#>   Analysis Bound       Z Probability Probability0 theta    IF  info info0 info1
#>      <int> <chr>   <dbl>       <dbl>        <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1        1 Upper  Inf         0           0         0.1 0.333  44.6  44.6  44.6
#> 2        1 Lower   -1         0.0477      0.159     0.1 0.333  44.6  44.6  44.6
#> 3        2 Upper    2.51      0.267       0.00605   0.2 0.667  89.1  89.1  89.1
#> 4        2 Lower -Inf         0.0477      0.159     0.2 0.667  89.1  89.1  89.1
#> 5        3 Upper    1.99      0.900       0.0249    0.3 1     134.  134.  134. 
#> 6        3 Lower -Inf         0.0477      0.159     0.3 1     134.  134.  134. 

# ---------------------------------# 
#         example 4                #
# ---------------------------------# 
# Spending function bounds
# 2-sided asymmetric bounds
# Lower spending based on non-zero effect
gs_design_npe(
  theta = c(.1, .2, .3),
  info = (1:3) * 40,
  info0 = (1:3) * 30,
  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 Probability0 theta    IF  info info0 info1
#>      <int> <chr>  <dbl>       <dbl>        <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1        1 Upper  3.71     0.000145   0.00000917   0.1 0.333  43.5  32.7  43.5
#> 2        1 Lower -1.34     0.0139     0.0616       0.1 0.333  43.5  32.7  43.5
#> 3        2 Upper  2.51     0.258      0.00595      0.2 0.667  87.1  65.3  87.1
#> 4        2 Lower  0.150    0.0460     0.533        0.2 0.667  87.1  65.3  87.1
#> 5        3 Upper  1.99     0.900      0.0248       0.3 1     131.   98.0 131. 
#> 6        3 Lower  2.00     0.0908     0.946        0.3 1     131.   98.0 131. 

# ---------------------------------# 
#         example 5                #
# ---------------------------------# 
# Two-sided symmetric spend, O'Brien-Fleming spending
# Typically, 2-sided bounds are binding
xx <- gs_design_npe(
  theta = c(.1, .2, .3),
  info = (1:3) * 40,
  binding = TRUE,
  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::sfLDOF, total_spend = 0.025, param = NULL, timing = NULL))
xx
#> # A tibble: 6 × 10
#>   Analysis Bound      Z Probability Probability0 theta    IF  info info0 info1
#>      <int> <chr>  <dbl>       <dbl>        <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1        1 Upper  3.71     0.00104      0.000104   0.1 0.333  39.8  39.8  39.8
#> 2        1 Lower -3.08     0.000104     0.00104    0.1 0.333  39.8  39.8  39.8
#> 3        2 Upper  2.51     0.233        0.00605    0.2 0.667  79.5  79.5  79.5
#> 4        2 Lower -0.728    0.00605      0.233      0.2 0.667  79.5  79.5  79.5
#> 5        3 Upper  1.99     0.900        0.0250     0.3 1     119.  119.  119. 
#> 6        3 Lower  1.28     0.0250       0.900      0.3 1     119.  119.  119. 

# 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,
  upper = gs_b,
  lower = gs_b,
  upar = (xx %>% filter(Bound == "Upper"))$Z,
  lpar = -(xx %>% filter(Bound == "Upper"))$Z)
#> # 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.902        0.3    0.3 1       120   120   120
#> 4        1 Lower -3.71  0.00000704   0.1    0.1 0.333    40    40    40
#> 5        2 Lower -2.51  0.0000151    0.2    0.2 0.667    80    80    80
#> 6        3 Lower -1.99  0.0000151    0.3    0.3 1       120   120   120