tests/testthat/test-generate_models.R

# Helpers -------------------------------------------------------------------------------------

test_formula <- y ~ a * b * c
test_terms <- c("Full Model", "a", "b", "c", "a:b", "a:c", "b:c", "a:b:c")

remove_attributes <- function(obj) {
  attr(obj, "type") <- NULL
  attr(obj, "model") <- NULL
  attr(obj, "class") <- NULL
  obj
}


# Tests ---------------------------------------------------------------------------------------

test_that("model has attributes for type and model specifications", {
  purrr::walk(1:3, function(type) {
    actual <- generate_models(test_formula, type)
    expect_equal(attr(actual, "type"), paste0(rep("I", type), collapse = ""))
    expect_equal(attr(actual, "model"), test_formula)
  })
})

test_that("null models return empty list", {
  model <- lm(mpg ~ NULL, data = mtcars)
  expect_length(generate_models(model, type = 1), 0)
})

test_that("generated models are organized with the key as the term", {
  generate_models(test_formula, 1) %>%
    expect_length(length(test_terms)) %>%
    expect_named(test_terms)
})

test_that("each term has a key for each of its complex and simple models", {
  generate_models(test_formula, 1) %>%
    purrr::flatten() %>%
    expect_named(rep(c("complex", "simple"), times = length(test_terms)))
})

test_that("the returned formulae are in the same environment as the given formulae", {
  expect_identical(
    environment(generate_models(test_formula, 1)$`Full Model`$complex),
    environment(test_formula)
  )
  expect_identical(
    environment(generate_models(test_formula, 2)$`Full Model`$complex),
    environment(test_formula)
  )
  expect_identical(
    environment(generate_models(test_formula, 3)$`Full Model`$complex),
    environment(test_formula)
  )
})

test_that("sequential models are returned for Type 1", {
  expected <- list(
    `Full Model` = list(
      complex = y ~ a + b + c + a:b + a:c + b:c + a:b:c,
      simple = y ~ NULL
    ),
    `a` = list(
      complex = y ~ a,
      simple = y ~ NULL
    ),
    `b` = list(
      complex = y ~ a + b,
      simple = y ~ a
    ),
    `c` = list(
      complex = y ~ a + b + c,
      simple = y ~ a + b
    ),
    `a:b` = list(
      complex = y ~ a + b + c + a:b,
      simple = y ~ a + b + c
    ),
    `a:c` = list(
      complex = y ~ a + b + c + a:b + a:c,
      simple = y ~ a + b + c + a:b
    ),
    `b:c` = list(
      complex = y ~ a + b + c + a:b + a:c + b:c,
      simple = y ~ a + b + c + a:b + a:c
    ),
    `a:b:c` = list(
      complex = y ~ a + b + c + a:b + a:c + b:c + a:b:c,
      simple = y ~ a + b + c + a:b + a:c + b:c
    )
  )

  actual <- generate_models(test_formula, type = 1)
  expect_equal(actual, expected, ignore_attr = TRUE)
})

test_that("hierarchical models are returned for Type 2", {
  expected <- list(
    `Full Model` = list(
      complex = y ~ a + b + c + a:b + a:c + b:c + a:b:c,
      simple = y ~ NULL
    ),
    `a` = list(
      complex = y ~ a + b + c + b:c,
      simple = y ~ b + c + b:c
    ),
    `b` = list(
      complex = y ~ a + b + c + a:c,
      simple = y ~ a + c + a:c
    ),
    `c` = list(
      complex = y ~ a + b + c + a:b,
      simple = y ~ a + b + a:b
    ),
    `a:b` = list(
      complex = y ~ a + b + c + a:b + a:c + b:c,
      simple = y ~ a + b + c + a:c + b:c
    ),
    `a:c` = list(
      complex = y ~ a + b + c + a:b + a:c + b:c,
      simple = y ~ a + b + c + a:b + b:c
    ),
    `b:c` = list(
      complex = y ~ a + b + c + a:b + a:c + b:c,
      simple = y ~ a + b + c + a:b + a:c
    ),
    `a:b:c` = list(
      complex = y ~ a + b + c + a:b + a:c + b:c + a:b:c,
      simple = y ~ a + b + c + a:b + a:c + b:c
    )
  )

  actual <- generate_models(test_formula, type = 2)
  expect_equal(actual, expected, ignore_attr = TRUE)
})

test_that("orthogonal models are returned for Type 3", {
  expected <- list(
    `Full Model` = list(
      complex = y ~ a + b + c + a:b + a:c + b:c + a:b:c,
      simple = y ~ NULL
    ),
    `a` = list(
      complex = y ~ a + b + c + a:b + a:c + b:c + a:b:c,
      simple = y ~ b + c + a:b + a:c + b:c + a:b:c
    ),
    `b` = list(
      complex = y ~ a + b + c + a:b + a:c + b:c + a:b:c,
      simple = y ~ a + c + a:b + a:c + b:c + a:b:c
    ),
    `c` = list(
      complex = y ~ a + b + c + a:b + a:c + b:c + a:b:c,
      simple = y ~ a + b + a:b + a:c + b:c + a:b:c
    ),
    `a:b` = list(
      complex = y ~ a + b + c + a:b + a:c + b:c + a:b:c,
      simple = y ~ a + b + c + a:c + b:c + a:b:c
    ),
    `a:c` = list(
      complex = y ~ a + b + c + a:b + a:c + b:c + a:b:c,
      simple = y ~ a + b + c + a:b + b:c + a:b:c
    ),
    `b:c` = list(
      complex = y ~ a + b + c + a:b + a:c + b:c + a:b:c,
      simple = y ~ a + b + c + a:b + a:c + a:b:c
    ),
    `a:b:c` = list(
      complex = y ~ a + b + c + a:b + a:c + b:c + a:b:c,
      simple = y ~ a + b + c + a:b + a:c + b:c
    )
  )

  actual <- generate_models(test_formula, type = 3)
  expect_equal(actual, expected, ignore_attr = TRUE)
})

test_that("linear models are refit with the resulting formulae", {
  # this test depends on data being complete (no missing data)
  # missing data is tested next
  model <- lm(mpg ~ hp + disp, data = mtcars)
  expected <- list(
    `Full Model` = list(
      complex = lm(mpg ~ hp + disp, data = mtcars),
      simple = lm(mpg ~ 1, data = mtcars)
    ),
    `hp` = list(
      complex = lm(mpg ~ hp, data = mtcars),
      simple = lm(mpg ~ 1, data = mtcars)
    ),
    `disp` = list(
      complex = lm(mpg ~ hp + disp, data = mtcars),
      simple = lm(mpg ~ hp, data = mtcars)
    )
  )

  actual <- generate_models(model, type = 1)
  expect_equal(actual, expected, ignore_attr = TRUE)
})

test_that("type 3 models properly exclude lower-level terms", {
  model <- lm(mpg ~ hp * disp, data = mtcars)
  models <- generate_models(model, type = 3)
  expect_false("hp" %in% names(coefficients(models$hp$simple)))
})

test_that("the updated models remove missing cases according to the largest model", {
  df_missing <- mtcars
  df_missing[1, ]$hp <- NA_real_
  df_missing[2:3, ]$disp <- NA_real_
  model <- lm(mpg ~ hp * disp, data = df_missing)

  models <- suppressMessages(generate_models(model, type = 1))
  most_complex <- models$`Full Model`$complex
  least_complex <- models$`Full Model`$simple
  middle_complex <- models$disp$complex

  expect_equal(nrow(most_complex$model), nrow(model$model))
  expect_equal(nrow(least_complex$model), nrow(model$model))
  expect_equal(nrow(middle_complex$model), nrow(model$model))
})

test_that("it prints nicely for supported models", {
  expect_snapshot(generate_models(test_formula, type = 1))
  expect_snapshot(generate_models(test_formula, type = 2))
  expect_snapshot(generate_models(test_formula, type = 3))

  test_lm <- lm(mpg ~ hp * disp, data = mtcars)
  expect_snapshot(generate_models(test_lm, type = 1))
  expect_snapshot(generate_models(test_lm, type = 2))
  expect_snapshot(generate_models(test_lm, type = 3))
})
UCLATALL/supernova documentation built on Feb. 13, 2024, 6:57 a.m.