Skip to content

Commit bc27a8a

Browse files
Add minimum risk weighting strategy to gs_design_rd and gs_power_rd (#613)
* Development - Add the MR weighting strategy to `gs_info_rd` - Update the `weight = ...` in `gs_design_rd` and `gs_power_rd` - Update Rd files - Add developer tests to `gs_power_rd` and `gs_design_rd` regarding the MR weights * Reviewers, Co-authored with John - Add `alpha_per_k_per_s` and `beta_per_k_per_s` to the globals.R - Add an example of MR weight in gs_info_rd.R - Update news.md
1 parent 5c7b82f commit bc27a8a

10 files changed

Lines changed: 119 additions & 19 deletions

NEWS.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
# gsDesign2 1.1.9
2+
3+
## New features
4+
5+
- The minimal risk weighting strategy has been added to `gs_design_rd()` and `gs_power_rd()` for risk difference design (#611, thanks to @LittleBeannie).
6+
- The `sequential_pval()` function has been added to calculate the sequential p-value for a AHR group sequential design (#605, thanks to @LittleBeannie).
7+
18
# gsDesign2 1.1.8
29

310
## New features

R/globals.R

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ utils::globalVariables(
5252
c(
5353
"prevalence", "z", "info", "theta", "rd", "info0", "n",
5454
"probability", "probability0", "info_frac0",
55-
"~risk difference at bound", "nominal p"
55+
"~risk difference at bound", "nominal p",
56+
"alpha_per_k_per_s", "beta_per_k_per_s"
5657
),
5758
# From `gs_design_wlr()`
5859
c(

R/gs_design_rd.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ gs_design_rd <- function(p_c = tibble::tibble(stratum = "All", rate = .2),
135135
beta = .1,
136136
ratio = 1,
137137
stratum_prev = NULL,
138-
weight = c("unstratified", "ss", "invar"),
138+
weight = c("unstratified", "ss", "invar", "mr"),
139139
upper = gs_b,
140140
lower = gs_b,
141141
upar = gsDesign(k = 3, test.type = 1, sfu = sfLDOF, sfupar = NULL)$upper$bound,

R/gs_info_rd.R

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@
9292
#' )
9393
#'
9494
#' # Example 5 ----
95-
#' # stratified case under sample size weighting and H0: rd0 != 0
95+
#' # stratified case under minimal risk weighting and H0: rd0 = 0
9696
#' gs_info_rd(
9797
#' p_c = tibble::tibble(
9898
#' stratum = c("S1", "S2", "S3"),
@@ -107,13 +107,13 @@
107107
#' analysis = rep(1:3, 3),
108108
#' n = c(50, 100, 200, 40, 80, 160, 60, 120, 240)
109109
#' ),
110-
#' rd0 = 0.02,
110+
#' rd0 = 0,
111111
#' ratio = 1,
112-
#' weight = "ss"
112+
#' weight = "mr"
113113
#' )
114114
#'
115115
#' # Example 6 ----
116-
#' # stratified case under inverse variance weighting and H0: rd0 != 0
116+
#' # stratified case under sample size weighting and H0: rd0 != 0
117117
#' gs_info_rd(
118118
#' p_c = tibble::tibble(
119119
#' stratum = c("S1", "S2", "S3"),
@@ -130,12 +130,13 @@
130130
#' ),
131131
#' rd0 = 0.02,
132132
#' ratio = 1,
133-
#' weight = "invar"
133+
#' # users can switch to either "invar" or "mr" weighting as well
134+
#' weight = "ss"
134135
#' )
135136
#'
136137
#' # Example 7 ----
137138
#' # stratified case under inverse variance weighting and H0: rd0 != 0 and
138-
#' # rd0 difference for different statum
139+
#' # rd0 difference for different stratum
139140
#' gs_info_rd(
140141
#' p_c = tibble::tibble(
141142
#' stratum = c("S1", "S2", "S3"),
@@ -173,7 +174,7 @@ gs_info_rd <- function(
173174
),
174175
rd0 = 0,
175176
ratio = 1,
176-
weight = c("unstratified", "ss", "invar")) {
177+
weight = c("unstratified", "ss", "invar", "mr")) {
177178
n_analysis <- max(n$analysis)
178179
weight <- match.arg(weight)
179180

@@ -237,6 +238,24 @@ gs_info_rd <- function(
237238
mutate(weight_per_k_per_s = 1 / sigma2_H1_per_k_per_s / sum_inv_var_per_s) |>
238239
select(-sum_inv_var_per_s)
239240
)
241+
} else if (weight == "mr") {
242+
suppressMessages(
243+
tbl <- tbl |>
244+
left_join(
245+
tbl |>
246+
group_by(analysis) |>
247+
summarize(sum_inv_var_per_s = sum(1 / sigma2_H1_per_k_per_s))
248+
) |>
249+
ungroup() |>
250+
group_by(analysis) |>
251+
mutate(alpha_per_k_per_s = (p_c - p_e) * sum_inv_var_per_s - sum((p_c - p_e) / sigma2_H1_per_k_per_s),
252+
beta_per_k_per_s = 1/sigma2_H1_per_k_per_s * (1 + alpha_per_k_per_s * sum((p_c - p_e) * n / max(n))),
253+
weight_per_k_per_s = beta_per_k_per_s / sum_inv_var_per_s -
254+
alpha_per_k_per_s / sigma2_H1_per_k_per_s / (sum_inv_var_per_s + sum(alpha_per_k_per_s * (p_c - p_e) / sigma2_H1_per_k_per_s)) *
255+
sum((p_c - p_e) * beta_per_k_per_s) / sum_inv_var_per_s
256+
) |>
257+
select(-c(sum_inv_var_per_s, alpha_per_k_per_s, beta_per_k_per_s))
258+
)
240259
}
241260

242261
# Pool the strata together ----

R/gs_power_rd.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ gs_power_rd <- function(
243243
),
244244
rd0 = 0,
245245
ratio = 1,
246-
weight = c("unstratified", "ss", "invar"),
246+
weight = c("unstratified", "ss", "invar", "mr"),
247247
upper = gs_b,
248248
lower = gs_b,
249249
upar = gsDesign(k = 3, test.type = 1, sfu = sfLDOF, sfupar = NULL)$upper$bound,

man/gs_design_rd.Rd

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/gs_info_rd.Rd

Lines changed: 8 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/gs_power_rd.Rd

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/test-developer-gs_design_rd.R

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,39 @@ test_that("fixed design", {
2828

2929
expect_equal(x1, x2$analysis$n)
3030
})
31+
32+
test_that("Stratified GSD: if RD is constant across strata, then MR weights are equal to the INVAR weights", {
33+
# Reference: Section 3 of Mehrotra, Devan V., and Radha Railkar.
34+
# "Minimum risk weights for comparing treatments in stratified binomial trials." Statistics in Medicine 19.6 (2000): 811-825.
35+
x_invar <- gs_design_rd(
36+
p_c = tibble::tibble(stratum = c("biomarker positive", "biomarker negative"),
37+
rate = c(.2, .25)),
38+
p_e = tibble::tibble(stratum = c("biomarker positive", "biomarker negative"),
39+
rate = c(.15, .20)),
40+
rd0 = 0, info_frac = c(0.7, 1),
41+
alpha = .025, beta = .1, ratio = 1,
42+
stratum_prev = tibble::tibble(stratum = c("biomarker positive", "biomarker negative"),
43+
prevalence = c(.4, .6)),
44+
weight = "invar",
45+
upper = gs_spending_bound, lower = gs_b,
46+
upar = list(sf = gsDesign::sfLDOF, total_spend = 0.025, param = NULL, timing = NULL),
47+
lpar = rep(-Inf, 2))
48+
49+
x_mr <- gs_design_rd(
50+
p_c = tibble::tibble(stratum = c("biomarker positive", "biomarker negative"),
51+
rate = c(.2, .25)),
52+
p_e = tibble::tibble(stratum = c("biomarker positive", "biomarker negative"),
53+
rate = c(.15, .20)),
54+
rd0 = 0, info_frac = c(0.7, 1),
55+
alpha = .025, beta = .1, ratio = 1,
56+
stratum_prev = tibble::tibble(stratum = c("biomarker positive", "biomarker negative"),
57+
prevalence = c(.4, .6)),
58+
weight = "mr",
59+
upper = gs_spending_bound, lower = gs_b,
60+
upar = list(sf = gsDesign::sfLDOF, total_spend = 0.025, param = NULL, timing = NULL),
61+
lpar = rep(-Inf, 2))
62+
63+
expect_equal(x_invar$analysis, x_mr$analysis)
64+
expect_equal(x_invar$bound, x_mr$bound)
65+
})
66+

tests/testthat/test-developer-gs_power_rd.R

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,39 @@ test_that("fixed design", {
2929

3030
expect_equal(x1, x2$bound$probability)
3131
})
32+
33+
test_that("Stratified GSD: if RD is constant across strata, then MR weights are equal to the INVAR weights", {
34+
# Reference: Section 3 of Mehrotra, Devan V., and Radha Railkar.
35+
# "Minimum risk weights for comparing treatments in stratified binomial trials." Statistics in Medicine 19.6 (2000): 811-825.
36+
x_invar <- gs_power_rd(
37+
p_c = tibble::tibble(stratum = c("biomarker positive", "biomarker negative"),
38+
rate = c(.2, .25)),
39+
p_e = tibble::tibble(stratum = c("biomarker positive", "biomarker negative"),
40+
rate = c(.15, .20)),
41+
n = tibble::tibble(stratum = rep(c("biomarker positive", "biomarker negative"), each = 2),
42+
n = c(1000, 1500, 1000, 1500),
43+
analysis = c(1, 2, 1, 2)),
44+
rd0 = 0, ratio = 1,
45+
weight = "invar",
46+
upper = gs_spending_bound, lower = gs_b,
47+
upar = list(sf = gsDesign::sfLDOF, total_spend = 0.025, param = NULL, timing = NULL),
48+
lpar = rep(-Inf, 2))
49+
50+
x_mr <- gs_power_rd(
51+
p_c = tibble::tibble(stratum = c("biomarker positive", "biomarker negative"),
52+
rate = c(.2, .25)),
53+
p_e = tibble::tibble(stratum = c("biomarker positive", "biomarker negative"),
54+
rate = c(.15, .20)),
55+
n = tibble::tibble(stratum = rep(c("biomarker positive", "biomarker negative"), each = 2),
56+
n = c(1000, 1500, 1000, 1500),
57+
analysis = c(1, 2, 1, 2)),
58+
rd0 = 0, ratio = 1,
59+
weight = "mr",
60+
upper = gs_spending_bound, lower = gs_b,
61+
upar = list(sf = gsDesign::sfLDOF, total_spend = 0.025, param = NULL, timing = NULL),
62+
lpar = rep(-Inf, 2))
63+
64+
expect_equal(x_invar$analysis, x_mr$analysis)
65+
expect_equal(x_invar$bound, x_mr$bound)
66+
})
67+

0 commit comments

Comments
 (0)