|
1 | 1 | #' Decision cutpoints for boundary (based on posterior probability)
|
2 | 2 | #'
|
3 | 3 | #' This function is used to identify the efficacy and futility
|
4 |
| -#' boundaries based on posterior probabilities, i.e.: |
5 |
| -#' Efficacy boundary: find minimum x (xU) where Pr(P>p0|x,n,a,b) >= tU and |
6 |
| -#' Futility boundary: find maximum x (xL) where Pr(P>p1|x,n,a,b) <= tL |
| 4 | +#' boundaries based on the following rules: |
| 5 | +#' Efficacy boundary: find minimum x (xU) where Pr(RR > p1 |x, n, a, b) >= tU and |
| 6 | +#' Futility boundary: find maximum x (xL) where Pr(RR < p0 | x, n, a, b) >= tL |
7 | 7 | #'
|
8 |
| -#' @param nvec a vector of number of patients |
9 |
| -#' @param p0 the efficacy threshold parameter in the postprob function |
10 |
| -#' @param p1 the futility threshold parameter in the postprob function |
11 |
| -#' (default = p0) |
12 |
| -#' @param tL futility boundary probability threshold |
13 |
| -#' @param tU efficacy boundary probability threshold |
14 |
| -#' @param a the alpha parameter of the beta prior of treatment group |
15 |
| -#' @param b the beta parameter of the beta prior of treatment group |
16 |
| -#' @return A matrix where for each sample size in \code{nvec}, this function |
17 |
| -#' returns the maximum number of responses that meet the futility |
18 |
| -#' threshold (xL), its corresponding response rate (pL), posterior probability |
19 |
| -#' (postL), upper bound of one sided 95% CI for the response rate based on an |
20 |
| -#' exact binomial test (UciL), and the same boundary parameters for efficacy: |
21 |
| -#' the minimal number of responses that meet the efficacy threshold (xU), |
22 |
| -#' the corresponding response rate (pU), posterior probability (postU) and |
23 |
| -#' the lower bound of one sided 95% CI for the response rate based on exact |
24 |
| -#' binomial test (LciU). |
25 |
| -#' |
26 |
| -#' @importFrom stats binom.test |
| 8 | +#' @inheritParams postprob |
| 9 | +#' @inheritParams ocPostprob |
| 10 | +#' @typed looks : numeric |
| 11 | +#' A vector of number of patients in each look. |
| 12 | +#' @return A matrix for each same size in `looks`. For each sample size, the following is returned: |
| 13 | +#' - `xL` : the maximum number of responses that meet the futility threshold. |
| 14 | +#' - `pL` : response rate corresponding to `xL`. |
| 15 | +#' - `postL`: posterior probability corresponding to `xL`, i.e. Pr(RR < p0 | xL, n, a, b). |
| 16 | +#' - `pL_upper_ci` : upper bound of one sided 95% CI for the response rate `pL` based on an |
| 17 | +#' exact binomial test. |
| 18 | +#' - `xU` : the minimal number of responses that meet the efficacy threshold. |
| 19 | +#' - `pU` : response rate corresponding to `xU`. |
| 20 | +#' - `postU` : posterior probability corresponding to `xU`, i.e. Pr(RR > p1 |xU, n, a, b). |
| 21 | +#' - `pU_lower_ci` : lower bound of one sided 95% CI for the response rate `pU` based on exact |
| 22 | +#' binomial test. |
27 | 23 | #'
|
28 | 24 | #' @example examples/boundsPostprob.R
|
29 | 25 | #' @export
|
30 |
| -#' @keywords graphics |
31 |
| -boundsPostprob <- function(nvec, p0, p1 = p0, tL, tU, a, b) { |
32 |
| - z <- matrix(NA, length(nvec), 6) |
33 |
| - dimnames(z) <- list(nvec, c( |
34 |
| - "xL", "pL", "postL", |
35 |
| - "xU", "pU", "postU" |
36 |
| - )) |
| 26 | +boundsPostprob <- function(looks, p0, p1 = p0, tL, tU, parE = c(1, 1), weights) { |
| 27 | + assert_numeric(looks) |
| 28 | + assert_number(p0, lower = 0, upper = 1) |
| 29 | + assert_number(p1, lower = 0, upper = 1) |
| 30 | + assert_number(tL, lower = 0, upper = 1) |
| 31 | + assert_number(tU, lower = 0, upper = 1) |
| 32 | + assert_numeric(parE, min.len = 2, any.missing = FALSE) |
| 33 | + z <- matrix(NA, nrow = length(looks), ncol = 8) |
37 | 34 | znames <- c(
|
38 |
| - "xL", "pL", "postL", "UciL", |
39 |
| - "xU", "pU", "postU", "LciU" |
| 35 | + "xL", "pL", "postL", "pL_upper_ci", |
| 36 | + "xU", "pU", "postU", "pU_lower_ci" |
40 | 37 | )
|
41 |
| - z <- matrix(NA, length(nvec), length(znames)) |
42 |
| - dimnames(z) <- list(nvec, znames) |
| 38 | + dimnames(z) <- list(looks, znames) |
43 | 39 | k <- 0
|
44 |
| - for (n in nvec) { |
| 40 | + parE <- t(parE) |
| 41 | + if (missing(weights)) { |
| 42 | + weights <- rep(1, nrow(parE)) |
| 43 | + } |
| 44 | + assert_numeric(weights, min.len = 0, len = nrow(par), finite = TRUE) |
| 45 | + for (n in looks) { |
45 | 46 | k <- k + 1
|
46 | 47 | # initialize so will return NA if 0 or n in "continue" region
|
47 | 48 | xL <- NA
|
48 | 49 | xU <- NA
|
49 | 50 | for (x in 0:n) {
|
50 |
| - postp <- postprob(x, n, p1, parE = c(a, b)) |
51 |
| - if (postp <= tL) { |
| 51 | + postp_fut <- 1 - postprob(x, n, p0, parE, weights) # futility look |
| 52 | + if (postp_fut >= tL) { # Rule is P(RR < p0) > tL |
| 53 | + postL <- postp_fut |
52 | 54 | xL <- x
|
53 | 55 | }
|
54 |
| - if (p0 != p1) { |
55 |
| - postp <- postprob(x, n, p0, parE = c(a, b)) |
56 |
| - } |
57 |
| - if (postp >= tU) { |
| 56 | + postp_eff <- postprob(x, n, p1, parE, weights) # efficacy look |
| 57 | + if (postp_eff >= tU) { # Rule is P(RR > p1) > tU |
| 58 | + postU <- postp_eff |
58 | 59 | xU <- x
|
59 |
| - # done: leave innermost for loop |
60 | 60 | break
|
61 | 61 | }
|
62 | 62 | }
|
63 |
| - # calculate posterior probabilities at boundaries |
64 |
| - postL <- postprob(xL, n, p1, parE = c(a, b)) |
65 |
| - postU <- postprob(xU, n, p0, parE = c(a, b)) |
66 | 63 | # calculate lower CI at boundaries
|
67 |
| - UciL <- ifelse(!is.na(xL), stats::binom.test(xL, n, alt = "less")$conf.int[2], NA) |
68 |
| - LciU <- ifelse(!is.na(xU), stats::binom.test(xU, n, alt = "greater")$conf.int[1], NA) |
69 |
| - z[k, ] <- c(xL, xL / n, postL, UciL, xU, xU / n, postU, LciU) |
| 64 | + pL_upper_ci <- ifelse(!is.na(xL), stats::binom.test(xL, n, alt = "less")$conf.int[2], NA) |
| 65 | + pU_lower_ci <- ifelse(!is.na(xU), stats::binom.test(xU, n, alt = "greater")$conf.int[1], NA) |
| 66 | + z[k, ] <- c( |
| 67 | + xL, |
| 68 | + xL / n, |
| 69 | + postL, |
| 70 | + pL_upper_ci, |
| 71 | + xU, |
| 72 | + xU / n, |
| 73 | + postU, |
| 74 | + pU_lower_ci |
| 75 | + ) |
70 | 76 | }
|
71 |
| - return(round(data.frame(nvec, z), 4)) |
| 77 | + round(data.frame(looks, z), 4) |
72 | 78 | }
|
0 commit comments