diff --git a/R/dbetabinom.R b/R/dbetabinom.R index a563510e..840631b0 100644 --- a/R/dbetabinom.R +++ b/R/dbetabinom.R @@ -83,9 +83,20 @@ h_getBetamixPost <- function(x, n, par, weights) { assert_number(x, lower = 0, upper = n, finite = TRUE) assert_number(n, lower = 0, finite = TRUE) assert_matrix(par, min.rows = 1, max.cols = 2, mode = "numeric") - assert_numeric(weights, min.len = 0, len = nrow(par), finite = TRUE) - # We renormalize weights. - weights <- weights / sum(weights) + assert_numeric( + weights, + min.len = 0, + len = nrow(par), + finite = TRUE + ) + assert_true(all(weights >= 0)) + # Correcting weights that do not sum to 1 + if (sum(weights) != 1) { + warning("Weights have been corrected. Advise to review allocated weights") + # We renormalize weights. + weights <- weights / sum(weights) + } + # We now compute updated parameters. postPar <- par postPar[, 1] <- postPar[, 1] + x diff --git a/R/postprob.R b/R/postprob.R index 383bf790..4f8ec9b3 100644 --- a/R/postprob.R +++ b/R/postprob.R @@ -68,7 +68,14 @@ postprobBeta <- function(x, n, p, a = 1, b = 1) { #' #' @example examples/postprob.R #' @export -postprob <- function(x, n, p, parE = c(1, 1), weights, betamixPost, log.p = FALSE) { +postprob <- function( + x, + n, + p, + parE = c(1, 1), + weights, + betamixPost, + log.p = FALSE) { if (missing(betamixPost)) { assert_flag(log.p) if (is.vector(parE)) { @@ -79,7 +86,7 @@ postprob <- function(x, n, p, parE = c(1, 1), weights, betamixPost, log.p = FALS } assert_matrix(parE) if (missing(weights)) { - weights <- rep(1, nrow(parE)) + weights <- rep(1 / nrow(parE), nrow(parE)) } betamixPost <- lapply( x, diff --git a/R/predprob.R b/R/predprob.R index f550e018..59ce10b3 100644 --- a/R/predprob.R +++ b/R/predprob.R @@ -45,8 +45,7 @@ #' #' @example examples/predprob.R #' @export -predprob <- function(x, n, Nmax, p, thetaT, parE = c(1, 1), - weights) { +predprob <- function(x, n, Nmax, p, thetaT, parE = c(1, 1), weights) { # m = Nmax - n future observations m <- Nmax - n if (is.vector(parE)) { @@ -55,18 +54,29 @@ predprob <- function(x, n, Nmax, p, thetaT, parE = c(1, 1), parE <- t(parE) } if (missing(weights)) { - weights <- rep(1, nrow(parE)) + weights <- rep(1 / nrow(parE), nrow(parE)) } betamixPost <- h_getBetamixPost(x = x, n = n, par = parE, weights = weights) - density <- with( betamixPost, dbetabinomMix(x = 0:m, m = m, par = par, weights = weights) ) - assert_numeric(density, lower = 0, upper = 1 + .Machine$double.eps, finite = TRUE, any.missing = FALSE) + assert_numeric( + density, + lower = 0, + upper = 1 + .Machine$double.eps, + finite = TRUE, + any.missing = FALSE + ) assert_number(thetaT, lower = 0, upper = 1, finite = TRUE) # posterior probabilities to be above threshold p - posterior <- postprob(x = x + c(0:m), n = Nmax, p = p, parE = parE, weights = weights) + posterior <- postprob( + x = x + c(0:m), + n = Nmax, + p = p, + parE = parE, + weights = weights + ) list( result = sum(density * (posterior > thetaT)), table = data.frame( diff --git a/examples/postprob.R b/examples/postprob.R index 5ca01017..46221f49 100644 --- a/examples/postprob.R +++ b/examples/postprob.R @@ -10,21 +10,23 @@ postprob(x = 16, n = 23, p = 0.60, par = c(0.6, 0.4)) # 2 component beta mixture prior : # i.e., P_E ~ 0.6*beta(0.6,0.4) + 0.4*beta(1,1) and Pr(P_E > p | data) = 0.823 postprob( - x = 16, n = 23, p = 0.60, - par = - rbind( - c(0.6, 0.4), - c(1, 1) - ), + x = 16, + n = 23, + p = 0.60, + par = rbind( + c(0.6, 0.4), + c(1, 1) + ), weights = c(0.6, 0.4) ) postprob( - x = 0:23, n = 23, p = 0.60, - par = - rbind( - c(0.6, 0.4), - c(1, 1) - ), + x = 0:23, + n = 23, + p = 0.60, + par = rbind( + c(0.6, 0.4), + c(1, 1) + ), weights = c(0.6, 0.4) ) diff --git a/examples/predprob.R b/examples/predprob.R index d3c98b8e..c80990ea 100644 --- a/examples/predprob.R +++ b/examples/predprob.R @@ -2,25 +2,41 @@ # Nmax = 40, x = 16, n = 23, beta(0.6,0.4) prior distribution, # thetaT = 0.9. The control response rate is 60%: predprob( - x = 16, n = 23, Nmax = 40, p = 0.6, thetaT = 0.9, + x = 16, + n = 23, + Nmax = 40, + p = 0.6, + thetaT = 0.9, parE = c(0.6, 0.4) ) # Lowering/Increasing the probability threshold thetaT of course increases # /decreases the predictive probability of success, respectively: predprob( - x = 16, n = 23, Nmax = 40, p = 0.6, thetaT = 0.8, + x = 16, + n = 23, + Nmax = 40, + p = 0.6, + thetaT = 0.8, parE = c(0.6, 0.4) ) predprob( - x = 16, n = 23, Nmax = 40, p = 0.6, thetaT = 0.95, + x = 16, + n = 23, + Nmax = 40, + p = 0.6, + thetaT = 0.95, parE = c(0.6, 0.4) ) # Mixed beta prior predprob( - x = 20, n = 23, Nmax = 40, p = 0.6, thetaT = 0.9, + x = 20, + n = 23, + Nmax = 40, + p = 0.6, + thetaT = 0.9, parE = rbind(c(1, 1), c(25, 15)), - weights = c(3, 1) + weights = c(0.5, 0.5) ) diff --git a/man/postprob.Rd b/man/postprob.Rd index cf78cc9b..9e92d05b 100644 --- a/man/postprob.Rd +++ b/man/postprob.Rd @@ -53,22 +53,24 @@ postprob(x = 16, n = 23, p = 0.60, par = c(0.6, 0.4)) # 2 component beta mixture prior : # i.e., P_E ~ 0.6*beta(0.6,0.4) + 0.4*beta(1,1) and Pr(P_E > p | data) = 0.823 postprob( - x = 16, n = 23, p = 0.60, - par = - rbind( - c(0.6, 0.4), - c(1, 1) - ), + x = 16, + n = 23, + p = 0.60, + par = rbind( + c(0.6, 0.4), + c(1, 1) + ), weights = c(0.6, 0.4) ) postprob( - x = 0:23, n = 23, p = 0.60, - par = - rbind( - c(0.6, 0.4), - c(1, 1) - ), + x = 0:23, + n = 23, + p = 0.60, + par = rbind( + c(0.6, 0.4), + c(1, 1) + ), weights = c(0.6, 0.4) ) } diff --git a/man/predprob.Rd b/man/predprob.Rd index 27440d07..842d1e64 100644 --- a/man/predprob.Rd +++ b/man/predprob.Rd @@ -55,27 +55,43 @@ A table with the following contents will be included in the return output : # Nmax = 40, x = 16, n = 23, beta(0.6,0.4) prior distribution, # thetaT = 0.9. The control response rate is 60\%: predprob( - x = 16, n = 23, Nmax = 40, p = 0.6, thetaT = 0.9, + x = 16, + n = 23, + Nmax = 40, + p = 0.6, + thetaT = 0.9, parE = c(0.6, 0.4) ) # Lowering/Increasing the probability threshold thetaT of course increases # /decreases the predictive probability of success, respectively: predprob( - x = 16, n = 23, Nmax = 40, p = 0.6, thetaT = 0.8, + x = 16, + n = 23, + Nmax = 40, + p = 0.6, + thetaT = 0.8, parE = c(0.6, 0.4) ) predprob( - x = 16, n = 23, Nmax = 40, p = 0.6, thetaT = 0.95, + x = 16, + n = 23, + Nmax = 40, + p = 0.6, + thetaT = 0.95, parE = c(0.6, 0.4) ) # Mixed beta prior predprob( - x = 20, n = 23, Nmax = 40, p = 0.6, thetaT = 0.9, + x = 20, + n = 23, + Nmax = 40, + p = 0.6, + thetaT = 0.9, parE = rbind(c(1, 1), c(25, 15)), - weights = c(3, 1) + weights = c(0.5, 0.5) ) } \references{ diff --git a/tests/testthat/test-dbetabinom.R b/tests/testthat/test-dbetabinom.R index e17a936e..60b3e547 100644 --- a/tests/testthat/test-dbetabinom.R +++ b/tests/testthat/test-dbetabinom.R @@ -170,7 +170,11 @@ test_that("dbetaMix gives the correct result with a 1 mixture component", { }) test_that("dbetaMix gives the correct result with increased parameters", { - result <- dbetaMix(x = 0.3, par = rbind(c(0.2, 0.4), c(1, 2)), weights = c(0.1, 0.9)) + result <- dbetaMix( + x = 0.3, + par = rbind(c(0.2, 0.4), c(1, 2)), + weights = c(0.1, 0.9) + ) expect_equal(result, 1.307458, tolerance = 1e-4) }) @@ -180,48 +184,57 @@ test_that("dbetaMix gives error when weights do not sum to 1", { x = 0.3, par = rbind(c(0.2, 0.4), c(1, 1)), weights = c(1, 1) - ), "failed" + ), + "failed" ) }) test_that("dbetaMix gives the correct result as dbeta", { result <- dbetaMix( - x = 0.3, par = rbind(c(0.2, 0.4), c(1, 1)), - weights = c(0.6, 0.4) - ) - result2 <- 0.6 * dbeta( - x = 0.3, - shape1 = 0.2, - shape2 = 0.4 - ) + 0.4 * dbeta( x = 0.3, - shape1 = 1, - shape2 = 1, + par = rbind(c(0.2, 0.4), c(1, 1)), + weights = c(0.6, 0.4) ) + result2 <- 0.6 * + dbeta( + x = 0.3, + shape1 = 0.2, + shape2 = 0.4 + ) + + 0.4 * + dbeta( + x = 0.3, + shape1 = 1, + shape2 = 1, + ) expect_equal(result, result2, tolerance = 1e-4) }) test_that("dbetaMix handles edge cases", { result_inf <- dbetaMix( - x = c(0, 1), par = rbind(c(0.2, 0.4), c(1, 1)), + x = c(0, 1), + par = rbind(c(0.2, 0.4), c(1, 1)), weights = c(0.6, 0.4) ) expect_equal(result_inf, c(Inf, Inf)) result_finite <- dbetaMix( - x = c(0, 1), par = rbind(c(2, 4), c(1, 1)), + x = c(0, 1), + par = rbind(c(2, 4), c(1, 1)), weights = c(0.6, 0.4) ) expect_equal(result_finite, c(0.4, 0.4)) result_right <- dbetaMix( - x = c(0, 1), par = rbind(c(0, 4), c(1, 1)), + x = c(0, 1), + par = rbind(c(0, 4), c(1, 1)), weights = c(0.6, 0.4) ) expect_equal(result_right, c(Inf, 0.4)) result_right <- dbetaMix( - x = c(NA, 1), par = rbind(c(0, 4), c(1, 1)), + x = c(NA, 1), + par = rbind(c(0, 4), c(1, 1)), weights = c(0.6, 0.4) ) expect_equal(result_right, c(NA, 0.4)) @@ -244,11 +257,14 @@ test_that("h_getBetamixPost gives the correct beta-mixture parameters", { }) test_that("h_getBetamixPost gives weight of 1 for a single beta distribution", { - results <- h_getBetamixPost( - x = 16, - n = 23, - par = rbind(c(1, 2)), - weights = 0.1 + expect_warning( + results <- h_getBetamixPost( + x = 16, + n = 23, + par = rbind(c(1, 2)), + weights = 0.1 + ), + "Weights have been corrected" ) expected <- list( par = rbind(c(17, 9)), @@ -269,13 +285,20 @@ test_that("h_getBetamixPost gives correct weights with 2 beta-mixture component" }) test_that("h_getBetamixPost gives the correct weights when sum of weights is not 1 in beta-mixture", { - result <- h_getBetamixPost( - x = 16, - n = 23, - par = rbind(c(1, 2), c(3, 4), c(10, 10)), - weights = c(0.6, 0.4, 0.5) + expect_warning( + result <- h_getBetamixPost( + x = 16, + n = 23, + par = rbind(c(1, 2), c(3, 4), c(10, 10)), + weights = c(0.6, 0.4, 0.5) + ), + "Weights have been corrected" + ) + expect_equal( + result$weights, + c(.2776991, 0.2683337, 0.4539671), + tolerance = 1e-4 ) - expect_equal(result$weights, c(.2776991, 0.2683337, 0.4539671), tolerance = 1e-4) expect_identical(result$par, rbind(c(17, 9), c(19, 11), c(26, 17))) }) @@ -286,6 +309,7 @@ test_that("h_getBetamixPost gives error when K rows of weights exceed length of n = 23, par = rbind(c(1, 2)), weights = c(0.6, 0.4) - ), "Must have length 1, but has length 2." + ), + "Must have length 1, but has length 2." ) }) diff --git a/tests/testthat/test-postprob.R b/tests/testthat/test-postprob.R index 7990db9b..74bac6cc 100644 --- a/tests/testthat/test-postprob.R +++ b/tests/testthat/test-postprob.R @@ -57,14 +57,42 @@ test_that("postprob gives incrementally higher values with increased x", { test_that("postprob works with vector x", { result <- postprob(x = 0:23, n = 23, p = 0.60, par = c(0.6, 0.4)) expected <- c( - 1.12066620085448e-10, 6.73786529927603e-09, 1.45879637562279e-07, - 1.86374536434781e-06, 1.64656040420248e-05, 0.000108838231763851, - 0.000564103325535708, 0.00236446983272442, 0.00819197194809839, - 0.0238449136766029, 0.0590640325657381, 0.125847456119664, - 0.232931221473374, 0.378259188739121, 0.54495891589689, - 0.705949748288983, 0.835980805221058, 0.922929283049132, - 0.970355725500809, 0.991009176245894, 0.997963909660055, - 0.999685712592687, 0.999972679748126, 0.99999934483779 + 1.12066620085448e-10, + 6.73786529927603e-09, + 1.45879637562279e-07, + 1.86374536434781e-06, + 1.64656040420248e-05, + 0.000108838231763851, + 0.000564103325535708, + 0.00236446983272442, + 0.00819197194809839, + 0.0238449136766029, + 0.0590640325657381, + 0.125847456119664, + 0.232931221473374, + 0.378259188739121, + 0.54495891589689, + 0.705949748288983, + 0.835980805221058, + 0.922929283049132, + 0.970355725500809, + 0.991009176245894, + 0.997963909660055, + 0.999685712592687, + 0.999972679748126, + 0.99999934483779 ) expect_equal(result, expected, tolerance = 1e-5) }) + +test_that("postprob from beta mixture priors utilise updated weights", { + result <- postprob(x = 16, n = 23, p = 0.60, parE = c(2, 4), weights = 1) + expected <- postprob( + x = 16, + n = 23, + p = 0.60, + par = rbind(c(0.6, 0.4), c(2, 4)), + weights = c(0, 1) + ) + expect_equal(result, expected, tolerance = 1e-7) +}) diff --git a/tests/testthat/test-predprob.R b/tests/testthat/test-predprob.R index 413ba6ba..6137a9dd 100644 --- a/tests/testthat/test-predprob.R +++ b/tests/testthat/test-predprob.R @@ -20,21 +20,28 @@ test_that("Higher thetaT decreases predprob", { parE = c(0.6, 0.4) ) is_higher <- predprob( - x = 16, n = 23, Nmax = 40, p = 0.6, thetaT = 0.8, + x = 16, + n = 23, + Nmax = 40, + p = 0.6, + thetaT = 0.8, parE = c(0.6, 0.4) ) expect_true(is_lower$result < is_higher$result) }) test_that("predprob gives an error when x is a numeric", { - expect_error(predprob( - x = 16:20, - n = 23, - Nmax = 40, - p = 0.6, - thetaT = 0.9, - parE = c(0.6, 0.4) - ), "Must have length 1") + expect_error( + predprob( + x = 16:20, + n = 23, + Nmax = 40, + p = 0.6, + thetaT = 0.9, + parE = c(0.6, 0.4) + ), + "Must have length 1" + ) }) test_that("predprob gives an error when K columns of weights exceed rows parE", { @@ -61,7 +68,8 @@ test_that("predprob gives an error when K columns of weights is less than rows p Nmax = 40, p = 0.6, thetaT = 0.9, - parE = rbind(c(0.6, 0.4), c(0.5, 0.5)), weights = 1 + parE = rbind(c(0.6, 0.4), c(0.5, 0.5)), + weights = 1 ), "Assertion on 'weights' failed: Must have length 2", fixed = TRUE @@ -69,14 +77,17 @@ test_that("predprob gives an error when K columns of weights is less than rows p }) test_that("predprob gives an error when thetaT exceeds 1", { - expect_error(predprob( - x = 16, - n = 23, - Nmax = 40, - p = 0.6, - thetaT = 2, - parE = c(0.6, 0.4) - ), "failed") + expect_error( + predprob( + x = 16, + n = 23, + Nmax = 40, + p = 0.6, + thetaT = 2, + parE = c(0.6, 0.4) + ), + "failed" + ) }) test_that("predprob gives the correct numeric result with a beta-mixture", { @@ -87,69 +98,65 @@ test_that("predprob gives the correct numeric result with a beta-mixture", { p = 0.6, thetaT = 0.9, parE = rbind(c(1, 1), c(25, 15)), - weights = c(3, 1) + weights = c(0.6, 0.4) ) - expect_equal(result$result, 0.9874431, tolerance = 1e-6) + expect_equal(result$result, 0.9831967, tolerance = 1e-6) }) test_that("predprob gives an error when x exceeds interim n", { - expect_error(predprob( - x = 24, - n = 23, - Nmax = 40, - p = 0.6, - thetaT = 0.9, - parE = rbind(c(1, 1), c(25, 15)), - weights = c(3, 1) - ), "failed") + expect_error( + predprob( + x = 24, + n = 23, + Nmax = 40, + p = 0.6, + thetaT = 0.9, + parE = rbind(c(1, 1), c(25, 15)), + weights = c(3, 1) + ), + "failed" + ) }) -test_that("predprob gives an error when Nmax is less than n", { - expect_error(predprob( - x = 16, - n = 23, - Nmax = 22, - p = 0.6, - thetaT = 0.9, - parE = rbind(c(1, 1), c(25, 15)), - weights = c(3, 1) - ), "failed") +test_that("Warning length equal to length of x", { + captured_warnings <- capture_warnings({ + result <- predprob( + x = 16, + n = 20, + Nmax = 22, + p = 0.6, + thetaT = 0.9, + parE = rbind(c(1, 1), c(25, 15)), + weights = c(3, 1) + ) + }) + expected <- data.frame( + counts = 0:2, + cumul_counts = c(20, 21, 22), + density = c(0.0738, 0.3651, 0.5611), + posterior = c(0.862329474749815, 0.928990451842824, 0.969938389163636), + success = c(FALSE, TRUE, TRUE) + ) + expect_equal( + length(captured_warnings), + 4, + info = "Should have generated one warning for each element in 'x' in the loop." + ) + expect_equal(result$result, 0.9262315, tolerance = 1e-4) + expect_equal(result$table, expected, tolerance = 1e-4) }) -test_that("predprob gives correct list", { - result <- predprob( - x = 20, - n = 23, - Nmax = 40, - p = 0.6, - thetaT = 0.9, - parE = rbind(c(1, 1), c(25, 15)), - weights = c(3, 1) - ) - expected <- list( - result = 0.987443066689065, - table = data.frame( - counts = 0:17, - cumul_counts = c( - 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, - 33, 34, 35, 36, 37, 38, 39, 40 - ), - density = c( - 0, 0, 0, 0, - 1e-04, 3e-04, 0.0011, 0.0031, 0.008, 0.0177, 0.0348, 0.0613, - 0.0973, 0.1394, 0.1783, 0.1964, 0.1708, 0.0913 - ), - posterior = c( - 0.156, - 0.2352, 0.3303, 0.4359, 0.5449, 0.65, 0.7447, 0.8244, 0.8871, - 0.9327, 0.9634, 0.9821, 0.9923, 0.9972, 0.9991, 0.9998, 1, - 1 - ), - success = c( - FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, - TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE - ) - ) +test_that("predprob gives an error when Nmax is less than n", { + expect_error( + predprob( + x = 16, + n = 23, + Nmax = 22, + p = 0.6, + thetaT = 0.9, + parE = rbind(c(1, 1), c(25, 15)), + weights = c(0.6, 0.4) + ), + "failed" ) - expect_identical(result, expected, tolerance = 1e-4) })