diff --git a/R/llcont.R b/R/llcont.R index d8e496a..7c191a3 100644 --- a/R/llcont.R +++ b/R/llcont.R @@ -53,12 +53,16 @@ llcont.glm <- function(x, ...){ if(is.matrix(y)) { ## Bolt: replaced apply(..., 1, sum) with optimized rowSums() for performance n <- rowSums(y) - y <- ifelse(n == 0, 0, y[, 1]/n) + ## Bolt: optimized ifelse for performance + y <- y[, 1] / n + y[n == 0 & !is.na(n)] <- 0 } else { n <- rep.int(1, length(y)) } m <- if (any(n > 1)) n else wt - wt <- ifelse(m > 0, (wt/m), 0) + ## Bolt: optimized ifelse for performance + wt <- wt / m + wt[m <= 0 & !is.na(m)] <- 0 dbinom(round(m * y), round(m), mpreds, log = TRUE) * wt }, quasibinomial = { diff --git a/tests/testthat/test_llcont_binomial_ifelse_contract.R b/tests/testthat/test_llcont_binomial_ifelse_contract.R new file mode 100644 index 0000000..6763964 --- /dev/null +++ b/tests/testthat/test_llcont_binomial_ifelse_contract.R @@ -0,0 +1,33 @@ +context("llcont binomial normalization contract") + +test_that("matrix-response zero totals preserve glm log-likelihood", { + successes <- c(0, 1, 2, 3, 1) + failures <- c(0, 2, 1, 0, 3) + predictor <- seq_along(successes) + response <- cbind(successes, failures) + + fit <- glm(response ~ predictor, family = binomial()) + fit$y <- response + + contributions <- llcont(fit) + + expect_false(any(is.nan(contributions))) + expect_equal(sum(contributions), as.numeric(logLik(fit))) +}) + +test_that("zero prior weights remain zero-contribution observations", { + response <- c(0, 1, 0, 1, 1, 0) + predictor <- seq_along(response) + prior_weight <- c(0, 1, 1, 1, 1, 1) + + fit <- glm( + response ~ predictor, + family = binomial(), + weights = prior_weight + ) + + contributions <- llcont(fit) + + expect_identical(unname(contributions[1]), 0) + expect_equal(sum(contributions), as.numeric(logLik(fit))) +})