tests/testthat/test-stack.R

context("test-stack.R -- Basic stack functionality")
library(origami)
library(SuperLearner)


data(cpp_imputed)
covars <- c(
  "apgar1", "apgar5", "parity", "gagebrth", "mage", "meducyrs",
  "sexn"
)
outcome <- "haz"
task <- sl3_Task$new(cpp_imputed, covariates = covars, outcome = outcome)

glm_learner <- Lrnr_glm$new()
glmnet_learner <- Lrnr_pkg_SuperLearner$new("SL.glmnet")
stack <- Stack$new(glm1 = glm_learner, glm2 = glmnet_learner)
stack2 <- Stack$new(stack)
test_that(
  "Stack$new copies original stack when learners is a Stack",
  expect_equivalent(stack$params$learners, stack2$params$learners)
)

stack_fit <- stack$train(task)
stack_preds <- stack_fit$predict()
test_that(
  "Stack accepts custom names",
  expect_equal(names(stack_preds), c("glm1", "glm2"))
)
# stack3 <- Stack$new(stack,glm_learner)
# stack3$params$learners
#
# test_that("Stack$new combines existing stacks into itself",
#           expect_length(stack3$params$learners,3))

# test that unique names are created when there's repetition
dens_hse_glm <- make_learner(Lrnr_density_semiparametric,
  mean_learner = make_learner(Lrnr_glm)
)

# check that stack gives unique names to input learners
stack_dens <- Stack$new(dens_hse_glm, dens_hse_glm)
stack_lrnr_names <- as.character(stack_dens$print())
test_that("Repetitive names of learners in stack differ after creation", {
  expect_false(stack_lrnr_names[1] == stack_lrnr_names[2])
})


# check that stack does not assume predict length
Lrnr_fixed_pred_length <- R6Class(
  classname = "Lrnr_broken", inherit = Lrnr_base, portable = TRUE,
  public = list(
    initialize = function() {
      invisible(self)
    }
  ),
  private = list(
    .train = function(task) {
      return(list())
    },
    .predict = function(task) {
      return(rep(1, 10))
    }
  )
)

# Stack works with prediction lengths that don't match task length
stack_fixed_len <- Stack$new(Lrnr_fixed_pred_length$new(), Lrnr_fixed_pred_length$new())
fit <- stack_fixed_len$train(task)
preds <- fit$predict()


# check that you can create a stack of one learner
stack_one <- Stack$new(glm_learner)
fit <- stack_one$train(task)
preds <- fit$predict(task)


# check that stacks can be a mix of pretrained and untrained learners
task_old <- task[1:10]
mean_lrnr <- Lrnr_mean$new()
old_fit <- mean_lrnr$train(task_old)
stack_old_and_new <- Stack$new(old_fit, mean_lrnr)
stack_fit <- stack_old_and_new$train(task)
# debug_predict(stack_fit)
preds <- stack_fit$predict()
old_mean <- mean(task_old$Y)
new_mean <- mean(task$Y)

test_that("A stack mixed from learners and fits does not retrain existing fits", {
  expect_equal(unlist(preds[1, 1, with = FALSE], use.names = FALSE), old_mean)
  expect_equal(unlist(preds[1, 2, with = FALSE], use.names = FALSE), new_mean)
})

# Example due to Lars van der Laan
# check that stack can handle learners that return multiple predictions
test_that("Stack will accept multiple predictions from a single learner", {
  lrnr_hal <- Lrnr_hal9001$new(
    max_degree = 1, lambda = c(5, 7, 8), fit_control = list(cv_select = F)
  )
  hal_stack <- make_learner(Stack, mean_lrnr, lrnr_hal)
  hs_fit <- hal_stack$train(task)
  preds <- hs_fit$predict()
  expect_equal(safe_dim(preds), c(task$nrow, 4))
})
jeremyrcoyle/sl3 documentation built on Feb. 3, 2022, 9:12 a.m.