45
45
# ' library(gsDesign2)
46
46
# '
47
47
# ' # set enrollment rates
48
- # ' enroll_rate <- define_enroll_rate(duration = 12, rate = 500 / 12 )
48
+ # ' enroll_rate <- define_enroll_rate(duration = 12, rate = 1 )
49
49
# '
50
50
# ' # set failure rates
51
51
# ' fail_rate <- define_fail_rate(
56
56
# ' )
57
57
# '
58
58
# ' # Example 1 ----
59
- # ' # Boundary is fixed
60
- # ' x <- gsSurv(
61
- # ' k = 3,
62
- # ' test.type = 4,
59
+ # ' # Information fraction driven design
60
+ # ' gs_design_wlr(
61
+ # ' enroll_rate = enroll_rate,
62
+ # ' fail_rate = fail_rate,
63
+ # ' ratio = 1,
63
64
# ' alpha = 0.025, beta = 0.2,
64
- # ' astar = 0, timing = 1,
65
- # ' sfu = sfLDOF, sfupar = 0,
66
- # ' sfl = sfLDOF, sflpar = 0 ,
67
- # ' lambdaC = 0.1 ,
68
- # ' hr = 0.6, hr0 = 1 ,
69
- # ' eta = 0.01, gamma = 10 ,
70
- # ' R = 12, S = NULL ,
71
- # ' T = 36, minfup = 24 ,
72
- # ' ratio = 1
65
+ # ' weight = function(x, arm0, arm1) {
66
+ # ' wlr_weight_fh(x, arm0, arm1, rho = 0, gamma = 0.5)
67
+ # ' } ,
68
+ # ' upper = gs_spending_bound ,
69
+ # ' upar = list(sf = gsDesign::sfLDOF, total_spend = 0.025) ,
70
+ # ' lower = gs_spending_bound ,
71
+ # ' lpar = list(sf = gsDesign::sfLDOF, total_spend = 0.2) ,
72
+ # ' analysis_time = 36,
73
+ # ' info_frac = 1:3/3
73
74
# ' )
74
75
# '
76
+ # ' # Example 2 ----
77
+ # ' # Calendar time driven design
75
78
# ' gs_design_wlr(
76
79
# ' enroll_rate = enroll_rate,
77
80
# ' fail_rate = fail_rate,
80
83
# ' weight = function(x, arm0, arm1) {
81
84
# ' wlr_weight_fh(x, arm0, arm1, rho = 0, gamma = 0.5)
82
85
# ' },
83
- # ' upper = gs_b,
84
- # ' upar = x$upper$bound,
85
- # ' lower = gs_b,
86
- # ' lpar = x$lower$bound,
87
- # ' analysis_time = c(12, 24, 36)
86
+ # ' upper = gs_spending_bound,
87
+ # ' upar = list(sf = gsDesign::sfLDOF, total_spend = 0.025),
88
+ # ' lower = gs_spending_bound,
89
+ # ' lpar = list(sf = gsDesign::sfLDOF, total_spend = 0.2),
90
+ # ' analysis_time = 1:3*12,
91
+ # ' info_frac = NULL
88
92
# ' )
89
93
# '
90
- # ' # Example 2 ----
91
- # ' # Boundary derived by spending function
94
+ # ' # Example 3 ----
95
+ # ' # Both calendar time and information fraction driven design
92
96
# ' gs_design_wlr(
93
97
# ' enroll_rate = enroll_rate,
94
98
# ' fail_rate = fail_rate,
101
105
# ' upar = list(sf = gsDesign::sfLDOF, total_spend = 0.025),
102
106
# ' lower = gs_spending_bound,
103
107
# ' lpar = list(sf = gsDesign::sfLDOF, total_spend = 0.2),
104
- # ' analysis_time = c(12, 24, 36)
108
+ # ' analysis_time = 1:3*12,
109
+ # ' info_frac = c(0.3, 0.7, 1)
105
110
# ' )
106
111
gs_design_wlr <- function (
107
112
enroll_rate = define_enroll_rate(
@@ -128,7 +133,9 @@ gs_design_wlr <- function(
128
133
h1_spending = TRUE ,
129
134
r = 18 , tol = 1e-6 ,
130
135
interval = c(.01 , 1000 )) {
131
- # Check input values ----
136
+ # --------------------------------------- #
137
+ # check input values #
138
+ # --------------------------------------- #
132
139
msg <- " gs_design_wlr(): analysis_time must be a
133
140
positive number or positive increasing sequence"
134
141
if (! is.vector(analysis_time , mode = " numeric" )) stop(msg )
@@ -145,52 +152,103 @@ gs_design_wlr <- function(
145
152
stop(" gs_design_wlr() hr must not be equal to 1 throughout the study as this is the null hypothesis." )
146
153
}
147
154
148
- # get the info_scale
155
+ # ---------------------------------------- #
156
+ # get some basic parameters #
157
+ # ---------------------------------------- #
149
158
info_scale <- match.arg(info_scale )
159
+ n_analysis <- max(length(analysis_time ), length(info_frac ))
150
160
151
- # Get information at input analysis_time ----
161
+ # ---------------------------------------- #
162
+ # get information at input #
163
+ # analysis_time, covering 3 scenarios:#
164
+ # (1) fixed design #
165
+ # (2) info-frac driven design with #
166
+ # a known study duration #
167
+ # (3) calendar time driven design #
168
+ # ---------------------------------------- #
152
169
y <- gs_info_wlr(enroll_rate , fail_rate ,
153
- ratio = ratio , event = NULL ,
154
- analysis_time = analysis_time , weight = weight , approx = approx ,
155
- interval = interval
156
- )
170
+ ratio = ratio , event = NULL ,
171
+ analysis_time = analysis_time , weight = weight , approx = approx ,
172
+ interval = interval ) % > %
173
+ dplyr :: select( - c( n , delta , sigma2 ) )
157
174
158
175
final_event <- y $ event [nrow(y )]
159
- if_alt <- y $ event / final_event
176
+ final_info <- max(y $ info )
177
+ info_frac_by_time <- y $ info / max(y $ info )
160
178
161
- # Check if info_frac needed for (any) IA timing
162
- n_analysis <- max(length(analysis_time ), length(info_frac ))
179
+ # if it is info frac driven group sequential design
180
+ # relabel the analysis to FA, and back calculate IAs from FA
181
+ if (n_analysis > 1 && length(analysis_time ) == 1 && length(info_frac ) > 1 ) {
182
+ y $ analysis <- n_analysis
183
+ }
184
+
185
+ # ---------------------------------------- #
186
+ # calculate the design to meet #
187
+ # targeted info_frac or analysis_time #
188
+ # or both #
189
+ # ---------------------------------------- #
163
190
next_time <- max(analysis_time )
164
191
192
+ # if it is calendar time driven design,
193
+ # e.g., info_frac = NULL, analysis_time = c(12, 14, 36)
165
194
if (length(info_frac ) == 1 ) {
166
- info_frac <- if_alt
195
+ info_frac <- info_frac_by_time
167
196
} else {
197
+ # if info_frac != NULL
168
198
if_indx <- info_frac [1 : (n_analysis - 1 )]
169
199
for (i in seq_along(if_indx )) {
170
- if (length(if_alt ) == 1 ) {
171
- y <- rbind(
172
- expected_time(enroll_rate , fail_rate ,
173
- target_event = info_frac [n_analysis - i ] * final_event ,
174
- ratio = ratio , interval = c(.01 , next_time )
175
- ) %> %
176
- mutate(theta = - log(ahr ), analysis = n_analysis - i ),
177
- y
178
- )
179
- } else if (info_frac [n_analysis - i ] > if_alt [n_analysis - i ]) {
180
- y [n_analysis - i , ] <- expected_time(enroll_rate , fail_rate ,
181
- target_event = IF [n_analysis - i ] * final_event ,
182
- ratio = ratio , interval = c(.01 , next_time )
183
- ) %> %
184
- dplyr :: transmute(analysis = n_analysis - i , time , event , ahr , theta = - log(ahr ), info , info0 )
200
+ # if it is info frac driven design with a known study duration,
201
+ # e.g., info_frac = 1:3/3, analysis_time = 36
202
+ if (length(info_frac_by_time ) == 1 ) {
203
+ # search the analysis time when the input info_frac arrives
204
+ ia_time <- uniroot(find_time_by_info_frac ,
205
+ interval = c(0.01 , next_time ),
206
+ enroll_rate = enroll_rate , fail_rate = fail_rate , ratio = ratio ,
207
+ weight = weight , approx = approx ,
208
+ final_info = final_info , next_time = next_time ,
209
+ input_info_frac = info_frac [n_analysis - i ])$ root
210
+
211
+ y_ia <- gs_info_wlr(enroll_rate , fail_rate , ratio = ratio ,
212
+ event = NULL , analysis_time = ia_time ,
213
+ weight = weight , approx = approx ,
214
+ interval = c(.01 , next_time )) %> %
215
+ dplyr :: select(- c(n , delta , sigma2 )) %> %
216
+ dplyr :: mutate(theta = - log(ahr ), analysis = n_analysis - i )
217
+ y <- dplyr :: bind_rows(y_ia , y )
218
+ # if it is driven by both info frac and analysis time,
219
+ # e.g., info_frac = 1:3/2, analysis_time = c(12, 24, 36)
220
+ } else if (info_frac [n_analysis - i ] > info_frac_by_time [n_analysis - i ]) {
221
+ # search the events when the input info_frac arrives
222
+ ia_event <- uniroot(find_event_by_info_frac ,
223
+ interval = c(0.01 , y $ event [y $ analysis == n_analysis - i + 1 ]),
224
+ enroll_rate = enroll_rate , fail_rate = fail_rate , ratio = ratio ,
225
+ weight = weight , approx = approx ,
226
+ final_info = final_info , next_time = next_time ,
227
+ input_info_frac = info_frac [n_analysis - i ])$ root
228
+
229
+ y_ia <- gs_info_wlr(enroll_rate , fail_rate , ratio = ratio ,
230
+ event = ia_event , analysis_time = NULL ,
231
+ weight = weight , approx = approx ,
232
+ interval = c(.01 , next_time )) %> %
233
+ dplyr :: select(- c(n , delta , sigma2 )) %> %
234
+ dplyr :: mutate(theta = - log(ahr ), analysis = n_analysis - i )
235
+
236
+ y_exclude_ia <- y %> % filter(analysis != n_analysis - i )
237
+ y <- dplyr :: bind_rows(y_ia , y_exclude_ia )
185
238
}
186
- next_time <- y $ time [n_analysis - i ]
239
+
240
+ next_time <- y $ time [y $ analysis == n_analysis - i ]
187
241
}
188
242
}
189
243
244
+ y <- y %> % arrange(analysis )
190
245
y $ analysis <- 1 : n_analysis
191
246
y $ n <- expected_accrual(time = y $ time , enroll_rate = enroll_rate )
192
247
193
- # h1 spending
248
+ # ---------------------------------------- #
249
+ # adjust theta1 and info1 #
250
+ # by h1 spending or not #
251
+ # ---------------------------------------- #
194
252
if (h1_spending ) {
195
253
theta1 <- y $ theta
196
254
info1 <- y $ info
@@ -199,7 +257,10 @@ gs_design_wlr <- function(
199
257
info1 <- y $ info0
200
258
}
201
259
202
- # Combine all the calculations ----
260
+ # ---------------------------------------- #
261
+ # calculate the design with known #
262
+ # 3 theta and 3 info #
263
+ # ---------------------------------------- #
203
264
suppressMessages(
204
265
allout <- gs_design_npe(
205
266
theta = y $ theta , theta1 = theta1 ,
@@ -230,10 +291,11 @@ gs_design_wlr <- function(
230
291
# add `~hr at bound` and `nominal p`
231
292
allout <- allout %> % mutate(
232
293
" ~hr at bound" = gsDesign :: zn2hr(z = z , n = event , ratio = ratio ),
233
- " nominal p" = pnorm(- z )
234
- )
294
+ " nominal p" = pnorm(- z ))
235
295
236
- # Return the output ----
296
+ # ---------------------------------------- #
297
+ # return the output #
298
+ # ---------------------------------------- #
237
299
# bounds table
238
300
bounds <- allout %> %
239
301
select(all_of(c(" analysis" , " bound" , " probability" , " probability0" , " z" , " ~hr at bound" , " nominal p" ))) %> %
@@ -244,6 +306,7 @@ gs_design_wlr <- function(
244
306
select(analysis , time , n , event , ahr , theta , info , info0 , info_frac ) %> %
245
307
unique() %> %
246
308
arrange(analysis )
309
+
247
310
# input table
248
311
input <- list (
249
312
enroll_rate = enroll_rate , fail_rate = fail_rate ,
@@ -254,18 +317,43 @@ gs_design_wlr <- function(
254
317
lower = lower , lpar = lpar ,
255
318
test_upper = test_upper , test_lower = test_lower ,
256
319
h1_spending = h1_spending , binding = binding ,
257
- info_scale = info_scale , r = r , tol = tol
258
- )
320
+ info_scale = info_scale , r = r , tol = tol )
259
321
260
322
# final output
261
323
ans <- list (
262
324
input = input ,
263
325
enroll_rate = enroll_rate %> % mutate(rate = rate * inflac_fct ),
264
326
fail_rate = fail_rate ,
265
327
bounds = bounds %> % filter(! is.infinite(z )),
266
- analysis = analysis
267
- )
328
+ analysis = analysis )
268
329
269
330
ans <- add_class(ans , if (! binding ) " non_binding" , " wlr" , " gs_design" )
270
331
return (ans )
271
332
}
333
+
334
+
335
+ # utility function to find the analysis time to get the planned/input info_frac
336
+ find_time_by_info_frac <- function (x , enroll_rate , fail_rate , ratio , weight , approx ,
337
+ final_info , next_time ,
338
+ input_info_frac ){
339
+ ia_info <- gs_info_wlr(analysis_time = x , event = NULL ,
340
+ enroll_rate = enroll_rate , fail_rate = fail_rate ,
341
+ weight = weight , approx = approx , ratio = ratio ,
342
+ interval = c(.01 , next_time ))
343
+
344
+ ia_info_frac <- ia_info $ info / final_info
345
+ return (ia_info_frac - input_info_frac )
346
+ }
347
+
348
+ # utility function to find the event to get the planned/input info_frac
349
+ find_event_by_info_frac <- function (x , enroll_rate , fail_rate , ratio , weight , approx ,
350
+ final_info , next_time ,
351
+ input_info_frac ){
352
+ ia_info <- gs_info_wlr(analysis_time = NULL , event = x ,
353
+ enroll_rate = enroll_rate , fail_rate = fail_rate ,
354
+ weight = weight , approx = approx , ratio = ratio ,
355
+ interval = c(.01 , next_time ))
356
+
357
+ ia_info_frac <- ia_info $ info / final_info
358
+ return (ia_info_frac - input_info_frac )
359
+ }
0 commit comments