tests/testthat/test-errors.R

context("Test vglmer robustness to certain situations")

if (isTRUE(as.logical(Sys.getenv("CI")))){
  # If on CI
  NITER <- 2
  env_test <- "CI"
}else if (!identical(Sys.getenv("NOT_CRAN"), "true")){
  # If on CRAN
  NITER <- 2
  env_test <- "CRAN"
  set.seed(131)
}else{
  # If on local machine
  NITER <- 2000
  env_test <- 'local'
}

test_that("vglmer can run with objects in environment", {
  N <- 100
  G <- 5
  G_names <- paste(sample(letters, G, replace = T), 1:G)
  x <- rnorm(N)
  g <- sample(G_names, N, replace = T)
  alpha <- rnorm(G)

  y <- rbinom(n = N, size = 1, prob = plogis(-1 + x + alpha[match(g, G_names)]))

  test_nodata <- tryCatch(suppressMessages(vglmer(y ~ x + (1 | g),
    data = NULL,
    control = vglmer_control(
      init = "zero",
      iterations = 1, print_prog = 10
    ),
    family = "binomial"
  )),
  error = function(e) {
    NULL
  }
  )
  expect_false(is.null(test_nodata))

  dta <- data.frame(Y = y, X = x, G = g)
  # Inject missingness into
  dta$Y[38] <- NA
  dta$X[39] <- NA
  dta$G[84] <- NA
  dta[3, ] <- NA
  test_missing <- tryCatch(suppressMessages(vglmer(Y ~ X + (1 | G),
    data = dta,
    control = vglmer_control(
      init = "zero", return_data = T,
      iterations = 1, print_prog = 10
    ),
    family = "binomial"
  )),
  error = function(e) {
    NULL
  }
  )
  # Confirm runs
  expect_false(is.null(test_missing))
  # Confirms deletion "works"
  expect_equivalent(dta$X[-c(3, 38, 39, 84)], test_missing$data$X[, 2])
  expect_equivalent(dta$Y[-c(3, 38, 39, 84)], test_missing$data$y)
})

test_that('vglmer runs with timing and "quiet=F"', {
  N <- 25
  G <- 2
  G_names <- paste(sample(letters, G, replace = T), 1:G)
  x <- rnorm(N)
  g <- sample(G_names, N, replace = T)
  alpha <- rnorm(G)

  y <- rbinom(n = N, size = 1, prob = plogis(-1 + x + alpha[match(g, G_names)]))
  
  if (all(y == 0)){
    y[1] <- 1
  }
  if (all(y == 1)){
    y[1] <- 0
  }
  
  est_simple <- suppressMessages(vglmer(y ~ x + (1 | g),
    data = NULL,
    control = vglmer_control(do_timing = T, quiet = F, iteration = 5),
    family = "binomial"
  ))
  expect_true(inherits(est_simple$timing, "data.frame"))
  expect_gte(min(diff(est_simple$ELBO_trajectory$ELBO)), 0)
})

test_that('vglmer parses environment correctly', {
  rm(list=ls())  
  N <- 25
  G <- 2
  G_names <- paste(sample(letters, G, replace = T), 1:G)
  
  dta <- data.frame(x = rnorm(N), g = sample(G_names, N, replace = T))
  alpha <- rnorm(G)
  
  dta$y <- rbinom(n = N, size = 1, prob = plogis(-1 + dta$x + alpha[match(dta$g, G_names)]))
  dta$size <- rpois(n = N, lambda = 2) + 1
  dta$y_b <- rbinom(n = N, size = dta$size, prob = plogis(-1 + dta$x + alpha[match(dta$g, G_names)]))
  #runs with clean environment
  est_simple <- suppressMessages(vglmer(y ~ x + (1 | g), data = dta, 
    control = vglmer_control(iterations = 5),
    family = 'binomial'))
  expect_true(inherits(est_simple, 'vglmer'))
  
  est_simple <- suppressMessages(vglmer(cbind(y_b, size) ~ x + (1 | g), 
    control = vglmer_control(iterations = 5),                                        
    data = dta, family = 'binomial'))
  expect_true(inherits(est_simple, 'vglmer'))
})

test_that("vglmer can run with 'debug' settings", {
  N <- 20
  G <- 5
  G_names <- paste(sample(letters, G, replace = T), 1:G)
  x <- rnorm(N)
  g <- sample(G_names, N, replace = T)
  alpha <- rnorm(G)
  
  y <- rbinom(n = N, size = 1, prob = plogis(-1 + x + alpha[match(g, G_names)]))
  
  # Avoid perfect separation
  if (all(y == 0)){
    y[1] <- 1
  }
  if (all(y == 1)){
    y[1] <- 0
  }
  
  # Debug to collect parameters
  est_vglmer <- vglmer(y ~ x + (1 | g), data = data.frame(y = y, x = x, g = g),
         family = 'binomial',
         control = vglmer_control(debug_param = TRUE, iterations = 5))  
  
  expect_true(all(c('beta', 'alpha') %in% names(est_vglmer$parameter_trajectory)))

  est_vglmer <- vglmer(y ~ x + (1 | g), 
      data = data.frame(y = y, x = x, g = g),
      family = 'binomial',
      control = vglmer_control(debug_ELBO = TRUE))
  expect_true(!is.null(est_vglmer$ELBO_trajectory$step))
  
})

test_that("vglmer can run with exactly balanced classes", {
  N <- 50
  G <- 5
  G_names <- paste(sample(letters, G, replace = T), 1:G)
  x <- rnorm(N)
  g <- sample(G_names, N, replace = T)
  alpha <- rnorm(G)
  
  y <- c(rep(0, N/2), rep(1, N/2))
  
  # Debug to collect parameters
  est_vglmer <- vglmer(y ~ x + (1 | g), data = data.frame(y = y, x = x, g = g),
      family = 'binomial',
      control = vglmer_control(iterations = 1))  
  
  expect_s3_class(est_vglmer, 'vglmer')
})

test_that("Run without FE for corresponding random slope", {

  N <- 25
  G <- 2
  G_names <- paste(sample(letters, G, replace = T), 1:G)
  x <- rnorm(N)
  g <- sample(G_names, N, replace = T)
  alpha <- rnorm(G)
  
  y <- rbinom(n = N, size = 1, prob = plogis(-1 + x + alpha[match(g, G_names)]))
  # Avoid perfect separation
  if (all(y == 0)){
    y[1] <- 1
  }
  if (all(y == 1)){
    y[1] <- 0
  }
  fit_noFE_for_RE <- vglmer(
    formula = y ~ 1 + (1 + x | g),
    family = 'linear', control = vglmer_control(iterations = 4),
    data = NULL)
  expect_s3_class(fit_noFE_for_RE, 'vglmer')
  
})

test_that("predict works with N=1", {
  
  N <- 25
  G <- 2
  G_names <- paste(sample(letters, G, replace = T), 1:G)
  x <- rnorm(N)
  g <- sample(G_names, N, replace = T)
  alpha <- rnorm(G)
  
  y <- rbinom(n = N, size = 1, prob = plogis(-1 + x + alpha[match(g, G_names)]))
  
  est_simple <- suppressMessages(vglmer(y ~ x + (1 | g),
      data = NULL,
      control = vglmer_control(iterations = 1),
      family = "linear"
  ))
  pred_single <- predict(est_simple, newdata = data.frame(x = x[1], g = 'NEW'), 
     allow_missing_levels = TRUE)
  term_single <- predict(est_simple, newdata = data.frame(x = x[1], g = 'NEW'),
     type = 'terms', allow_missing_levels = TRUE)
  expect_equal(pred_single, sum(coef(est_simple) * c(1, x[1])))
  expect_equivalent(c(pred_single, 0), term_single)
  
  est_spline <- suppressMessages(vglmer(y ~ v_s(x) + (1 | g),
      data = NULL,
      control = vglmer_control(iterations = 1),
      family = "linear"
  ))
  pred_spline <- predict(est_spline, 
    newdata = data.frame(x = x[1], g = 'NEW'), 
    allow_missing_levels = TRUE)
  term_spline <- predict(est_spline, type = 'terms',
    newdata = data.frame(x = x[1], g = 'NEW'), 
    allow_missing_levels = TRUE)
  expect_equal(pred_spline, rowSums(term_spline))
  expect_equivalent(term_spline[, 'FE'], sum(c(1, x[1]) * coef(est_spline)))
  
})
mgoplerud/vglmer documentation built on Jan. 22, 2025, 6:43 p.m.