library(testthat) library(recipes) skip_if_not_installed("modeldata") data(biomass, package = "modeldata") test_that("default method", { rec <- recipe(x = biomass) exp_res <- tibble( variable = colnames(biomass), type = rep( list(c("string", "unordered", "nominal"), c("double", "numeric")), c(2, 6) ), role = NA_character_, source = "original", required_to_bake = TRUE ) expect_equal(summary(rec, TRUE), exp_res) }) test_that("changing roles", { rec <- recipe(x = biomass) rec <- update_role(rec, sample, new_role = "some other role") exp_res <- tibble( variable = colnames(biomass), type = rep( list(c("string", "unordered", "nominal"), c("double", "numeric")), c(2, 6) ), role = rep(c("some other role", NA), c(1, 7)), source = "original", required_to_bake = TRUE ) expect_equal(summary(rec, TRUE), exp_res) }) test_that("change existing role", { rec <- recipe(x = biomass) expect_snapshot(error = TRUE, add_role(rec, sample, new_role = "some other role") ) rec <- update_role(rec, sample, new_role = "some other role") rec <- update_role(rec, sample, new_role = "other other role") exp_res <- tibble( variable = colnames(biomass), type = rep( list(c("string", "unordered", "nominal"), c("double", "numeric")), c(2, 6) ), role = rep(c("other other role", NA), c(1, 7)), source = "original", required_to_bake = TRUE ) expect_equal(summary(rec, TRUE), exp_res) }) test_that("change only 1 role of variable with multiple roles", { rec <- recipe(x = biomass) rec <- rec %>% update_role(sample, new_role = "role 1") %>% add_role(sample, new_role = "role 2") orig_roles <- rec # changes only 1 rec <- update_role(rec, sample, new_role = "role 3", old_role = "role 1") exp_res <- summary(orig_roles) exp_res$role[exp_res$role == "role 1"] <- "role 3" exp_res$required_to_bake <- TRUE expect_equal(summary(rec, TRUE), exp_res) }) test_that("change every role of 2 variables", { rec <- recipe(x = biomass) rec <- update_role(rec, sample, dataset, new_role = "role 1") orig_roles <- summary(rec) rec <- update_role(rec, sample, dataset, new_role = "role 2") exp_res <- orig_roles exp_res$role[exp_res$role == "role 1"] <- "role 2" exp_res$required_to_bake <- TRUE expect_equal(summary(rec, TRUE), exp_res) }) test_that("update only NA role", { rec <- recipe(x = biomass) orig_rec <- summary(rec) rec <- update_role(rec, sample, dataset, new_role = "some other role") exp_res <- orig_rec %>% arrange(variable) exp_res$role[exp_res$variable %in% c("sample", "dataset")] <- "some other role" exp_res$required_to_bake <- TRUE expect_equal(summary(rec, TRUE) %>% arrange(variable), exp_res) }) test_that("new role for existing NA role", { rec <- recipe(x = biomass) rec <- update_role(rec, sample, new_role = "some other role") exp_res <- tibble( variable = colnames(biomass), type = rep( list(c("string", "unordered", "nominal"), c("double", "numeric")), c(2, 6) ), role = rep(c("some other role", NA), c(1, length(colnames(biomass)) - 1)), source = "original", required_to_bake = TRUE ) expect_equal(summary(rec, TRUE), exp_res) }) test_that("new role with specified type", { rec <- recipe(x = biomass) rec <- update_role(rec, sample, new_role = "blah") rec <- add_role(rec, sample, new_role = "some other role", new_type = "new type") exp_res <- tibble( variable = c("sample", colnames(biomass)), type = c( list(c("string", "unordered", "nominal")), list("new type"), list(c("string", "unordered", "nominal")), rep(list(c("double", "numeric")), 6) ), role = rep(c("blah", "some other role", NA), c(1, 1, 7)), source = "original", required_to_bake = TRUE ) expect_equal(summary(rec, TRUE), exp_res) }) test_that("add new role when two already exist with different types", { # type of the first existing role found is used rec <- recipe(x = biomass) rec <- update_role(rec, sample, new_role = "blah") rec <- add_role(rec, sample, new_role = "some other role", new_type = "new type") rec <- add_role(rec, sample, new_role = "another role") exp_res <- tibble( variable = c("sample", "sample", colnames(biomass)), type = c( list(c("string", "unordered", "nominal")), list("new type"), list(c("string", "unordered", "nominal")), list(c("string", "unordered", "nominal")), rep(list(c("double", "numeric")), 6) ), role = c("blah", "some other role", "another role", rep(NA, 7)), source = "original", required_to_bake = TRUE ) expect_equal(summary(rec, TRUE), exp_res) }) test_that("existing role is skipped", { rec <- recipe(x = biomass) rec <- update_role(rec, sample, new_role = "blah") rec <- add_role(rec, sample, new_role = "some other role") # skip me expect_snapshot( rec <- add_role(rec, sample, new_role = "some other role") ) # also tests the order, new roles come directly after old ones exp_res <- tibble( variable = c("sample", colnames(biomass)), type = rep( list(c("string", "unordered", "nominal"), c("double", "numeric")), c(3, 6) ), role = rep(c("blah", "some other role", NA), c(1, 1, 7)), source = "original", required_to_bake = TRUE ) expect_equal(summary(rec, TRUE), exp_res) }) test_that("existing role is skipped, but new one is added", { rec <- recipe(x = biomass) rec <- update_role(rec, sample, new_role = "blah") rec <- add_role(rec, sample, new_role = "some other role") # partially skip me expect_snapshot( rec <- add_role(rec, sample, dataset, new_role = "some other role") ) exp_res <- tibble( variable = c( rep(c("sample", "dataset"), c(2, 2)), setdiff(colnames(biomass), c("sample", "dataset")) ), type = rep( list(c("string", "unordered", "nominal"), c("double", "numeric")), c(4, 6) ), role = c("blah", "some other role", NA, "some other role", rep(NA, 6)), source = "original", required_to_bake = TRUE ) expect_equal(summary(rec, TRUE), exp_res) }) test_that("cannot add roles if the current one is `NA`", { rec <- recipe(x = biomass) expect_snapshot(error = TRUE, add_role(rec, sample, sulfur) ) }) test_that("`update_role()` cannot be ambiguous", { rec <- recipe(HHV ~ ., data = biomass) rec <- add_role(rec, sample, new_role = "x") expect_snapshot(error = TRUE, update_role(rec, sample, new_role = "y") ) }) test_that("`new_role` cannot be `NA_character_`", { rec <- recipe(x = biomass) expect_snapshot(error = TRUE, add_role(rec, sample, new_role = NA_character_) ) expect_snapshot(error = TRUE, update_role(rec, sample, new_role = NA_character_) ) }) test_that("remove roles", { rec <- recipe(x = biomass) rec <- update_role(rec, sample, new_role = "role1") expect_snapshot(error = TRUE, rec <- remove_role(rec, sample, old_role = NA) ) expect_snapshot(error = TRUE, rec <- remove_role(rec, sample) ) expect_snapshot( remove_role(rec, sample, old_role = "non-existant") ) rec <- remove_role(rec, sample, old_role = "role1") exp_res <- tibble( variable = colnames(biomass), type = rep( list(c("string", "unordered", "nominal"), c("double", "numeric")), c(2, 6) ), role = NA_character_, source = "original", required_to_bake = TRUE ) expect_equal(summary(rec, TRUE), exp_res) }) test_that("New type for an existing role can be added", { rec <- recipe(x = biomass) rec <- update_role(rec, sample, new_role = "role1") rec <- add_role(rec, sample, new_role = "role1", new_type = "text") exp_res <- tibble( variable = c("sample", colnames(biomass)), type = c( list(c("string", "unordered", "nominal")), list("text"), list(c("string", "unordered", "nominal")), rep(list(c("double", "numeric")), 6) ), role = c("role1", "role1", rep(NA, 7)), source = "original", required_to_bake = TRUE ) expect_equal(summary(rec, TRUE), exp_res) }) test_that("can use tidyselect ops in role selection", { rec <- recipe(mpg ~ ., mtcars) %>% step_center(all_predictors()) # Swap "predictor" for "foo" rec <- update_role( rec, starts_with("c") & !carb, new_role = "foo", old_role = "predictor" ) expect_identical( rec$term_info$role[rec$term_info$variable == "cyl"], "foo" ) # Add "predictor" back rec <- add_role( rec, starts_with("c") & !carb, new_role = "predictor" ) expect_identical( rec$term_info$role[rec$term_info$variable == "cyl"], c("foo", "predictor") ) # Remove "foo" rec <- remove_role( rec, starts_with("c") & !carb, old_role = "foo" ) expect_identical( rec$term_info$role[rec$term_info$variable == "cyl"], "predictor" ) }) test_that("empty dots and zero column selections return input with a warning", { rec <- recipe(x = biomass) expect_snapshot( rec2 <- add_role(rec) ) expect_identical(rec2, rec) expect_snapshot( rec2 <- update_role(rec) ) expect_identical(rec2, rec) expect_snapshot( rec2 <- remove_role(rec, old_role = "foo") ) expect_identical(rec2, rec) expect_snapshot( rec2 <- add_role(rec, starts_with("foobar")) ) expect_identical(rec2, rec) expect_snapshot( rec2 <- update_role(rec, starts_with("foobar")) ) expect_identical(rec2, rec) expect_snapshot( rec2 <- remove_role(rec, starts_with("foobar"), old_role = "foo") ) expect_identical(rec2, rec) }) test_that("bad args", { expect_snapshot(error = TRUE, recipe(x = biomass) %>% add_role(carbon, new_role = letters[1:2]) ) expect_snapshot(error = TRUE, recipe(x = biomass) %>% add_role(carbon, new_role = "a", new_type = letters[1:2]) ) expect_snapshot(error = TRUE, recipe(x = biomass) %>% update_role(carbon, new_role = c("a", "b")) ) expect_snapshot(error = TRUE, recipe(x = biomass) %>% update_role(carbon, old_role = c("a", "b")) ) }) # ------------------------------------------------------------------------------ # Multiples roles + Selection testing test_that("adding multiple roles/types does not duplicate prepped columns", { rec <- recipe(HHV ~ ., data = biomass) # second role expect_equal( rec %>% add_role(carbon, new_role = "carb") %>% prep(training = biomass) %>% bake(new_data = NULL) %>% ncol(), 8 ) # second type expect_equal( rec %>% add_role(carbon, new_type = "carb") %>% prep(training = biomass) %>% bake(new_data = NULL) %>% ncol(), 8 ) }) test_that("type selectors can be combined", { rec <- recipe(HHV ~ ., data = biomass) prepped <- rec %>% add_role(carbon, new_role = "predictor", new_type = "carb") %>% step_center(all_numeric(), -has_type("carb")) %>% prep(training = biomass) expect_equal( names(prepped$steps[[1]]$means), c("hydrogen", "oxygen", "nitrogen", "sulfur", "HHV") ) }) test_that("step_rm() removes ALL mention of variables with that role", { rec <- recipe(HHV ~ ., data = biomass) rec_prepped <- rec %>% add_role(carbon, new_role = "predictor", new_type = "carb") %>% step_rm(has_type("carb")) %>% prep(training = biomass) %>% summary() expect_false("carbon" %in% rec_prepped$variable) }) # ------------------------------------------------------------------------------ # Tests related to #296 # https://github.com/tidymodels/recipes/issues/296 test_that("Existing `NA` roles are not modified in prep() when new columns are generated", { rec_dummy <- recipe(x = iris) %>% update_role(Sepal.Length, new_role = "outcome") %>% update_role(Species, new_role = "predictor") %>% step_dummy(Species) prepped_rec_dummy <- prep(rec_dummy, iris) orig <- summary(rec_dummy) new <- summary(prepped_rec_dummy) # These should be identical except for the modified Species term expect_equal( filter(orig, !grepl("Species", variable)), filter(new, !grepl("Species", variable)) ) expect_equal( filter(new, grepl("Species", variable)), tibble( variable = c("Species_versicolor", "Species_virginica"), type = rep(list(c("double", "numeric")), 2), role = rep("predictor", times = 2), source = rep("derived", times = 2) ) ) # Juicing with all predictors should only give these two columns expect_equal( colnames(bake(prepped_rec_dummy, new_data = NULL, all_predictors())), c("Species_versicolor", "Species_virginica") ) }) test_that("Existing `NA` roles are not modified in prep() when multiple new columns are generated", { rec <- recipe(x = iris) %>% update_role(Sepal.Length, new_role = "outcome") %>% update_role(Sepal.Width, new_role = "predictor") %>% update_role(Species, new_role = "predictor") %>% step_dummy(Species) %>% step_bs(Sepal.Width) prepped_rec <- prep(rec, iris) orig <- summary(rec) new <- summary(prepped_rec) # These should be identical except for the # modified Species and Sepal.Width terms expect_equal( filter(orig, !grepl("Species", variable), !grepl("Sepal.Width", variable)), filter(new, !grepl("Species", variable), !grepl("Sepal.Width", variable)) ) }) test_that("Roles are correcly selected in bake", { x <- tibble::tibble( a = runif(10), b = runif(10), c = runif(10) ) rec <- recipe(c ~ ., x) %>% update_role(b, new_role = "id") %>% add_role(a, new_role = "id") %>% prep() o <- recipes::bake(rec, x, recipes::has_role("id")) expect_equal(names(o), c("a", "b")) }) test_that("role functions handle case weights correctly", { expect_snapshot(error = TRUE, recipe(mpg ~ ., data = mtcars) %>% update_role("disp", new_role = "case_weights") ) expect_snapshot(error = TRUE, recipe(mpg ~ ., data = mtcars) %>% add_role("disp", new_role = "case_weights") ) mtcars1 <- mtcars %>% mutate(wt = importance_weights(wt)) expect_snapshot(error = TRUE, recipe(mpg ~ ., data = mtcars1) %>% remove_role(wt, old_role = "case_weights") ) expect_snapshot(error = TRUE, recipe(mpg ~ ., data = mtcars1) %>% update_role(wt) ) expect_snapshot(error = TRUE, recipe(mpg ~ ., data = mtcars1) %>% add_role(wt) ) }) test_that("role information from summary()", { # ---------------------------------------------------------------------------- # non-formula method rec_roles <- recipe(mtcars) %>% update_role(-mpg, new_role = "predictor") %>% update_role(mpg, new_role = "outcome") %>% update_role(gear, new_role = "id") %>% update_role(carb, new_role = "important") %>% prep() req_roles <- rec_roles %>% update_role_requirements("important", bake = FALSE) %>% prep() expect_snapshot(summary(rec_roles, original = TRUE)) expect_snapshot(summary(req_roles, original = TRUE)) # ---------------------------------------------------------------------------- # missing role values na_rec <- mtcars %>% recipe() %>% update_role(mpg, new_role = "outcome") %>% update_role(disp, wt, new_role = "predictor") %>% update_role(carb, new_role = "other") %>% prep() na_req_rec <- na_rec %>% update_role_requirements("NA", bake = FALSE) %>% prep() expect_snapshot(summary(na_rec, original = TRUE)) expect_snapshot(summary(na_req_rec, original = TRUE)) }) test_that("add_roles() error if columns would be both predictor and outcome", { expect_snapshot( error = TRUE, recipe(mpg ~., data = mtcars) %>% add_role(mpg, new_role = "predictor") ) expect_snapshot( error = TRUE, recipe(mpg ~., data = mtcars) %>% add_role(disp, new_role = "outcome") ) })