test_that("matsindf_apply() fails with an unexpected argument", { example_fun <- function(a, b) { return(list(c = matsbyname::sum_byname(a, b), d = matsbyname::difference_byname(a, b))) } expect_error(matsindf_apply(.dat = "a string", FUN = example_fun, a = 2, b = 2), ".dat must be NULL, a data frame, or a list in matsindf_apply\\(\\), was character") }) test_that("matsindf_apply() works as expected for single values", { example_fun <- function(a, b) { return(list(c = matsbyname::sum_byname(a, b), d = matsbyname::difference_byname(a, b))) } expect_equal(example_fun(a = 2, b = 2), list(c = 4, d = 0)) expect_equal(matsindf_apply(FUN = example_fun, a = 2, b = 2), list(c = 4, d = 0)) }) test_that("matsindf_apply() works as expected for single matrices", { example_fun <- function(a, b) { return(list(c = matsbyname::sum_byname(a, b), d = matsbyname::difference_byname(a, b))) } a <- matrix(c(1,2,3,4), nrow = 2, ncol = 2, byrow = TRUE, dimnames = list(c("r1", "r2"), c("c1", "c2"))) b <- a c <- matsbyname::sum_byname(a, b) d <- matsbyname::difference_byname(a, b) expected_list <- list(c = c, d = d) expect_equal(example_fun(a, b), expected_list) expect_equal(matsindf_apply(FUN = example_fun, a = a, b = b), expected_list) }) test_that("matsindf_apply() works as expected for single Matrix objects", { example_fun <- function(a, b) { return(list(c = matsbyname::sum_byname(a, b), d = matsbyname::difference_byname(a, b))) } a <- matsbyname::Matrix(c(1,2,3,4), nrow = 2, ncol = 2, byrow = TRUE, dimnames = list(c("r1", "r2"), c("c1", "c2"))) b <- a c <- matsbyname::sum_byname(a, b) d <- matsbyname::difference_byname(a, b) expected_list <- list(c = a + b, d = a - b) expect_equal(example_fun(a, b), expected_list) expect_equal(matsindf_apply(FUN = example_fun, a = a, b = b), expected_list) }) test_that("matsindf_apply() works as expected for lists of single values", { example_fun <- function(a, b) { return(list(c = matsbyname::sum_byname(a, b), d = matsbyname::difference_byname(a, b))) } expect_equal(example_fun(a = list(2, 2), b = list(2, 2)), list(c = list(4, 4), d = list(0, 0))) expect_equal(matsindf_apply(FUN = example_fun, a = list(2, 2, 2), b = list(2, 2, 2)), data.frame(c = I(list(4, 4, 4)), d = I(list(0, 0, 0))), ignore_attr = TRUE) expect_equal(matsindf_apply(FUN = example_fun, a = list(2, 2), b = list(1, 2)), data.frame(c = I(list(3, 4)), d = I(list(1, 0))), ignore_attr = TRUE) }) test_that("matsindf_apply() works as expected for lists of matrices", { example_fun <- function(a, b) { return(list(c = matsbyname::sum_byname(a, b), d = matsbyname::difference_byname(a, b))) } a <- matrix(c(1,2,3,4), nrow = 2, ncol = 2, byrow = TRUE, dimnames = list(c("r1", "r2"), c("c1", "c2"))) b <- a c <- a + b d <- a - b a <- list(a, a) b <- list(b, b) list_expected <- list(c = list(c, c), d = list(d, d)) # Because DF_expected$c and DF_expected$d are created with I(list()), their class is "AsIs". # Need to set the class of DF_expected$c and DF_expected$d to NULL to get a match. attr(list_expected$c, which = "class") <- NULL attr(list_expected$d, which = "class") <- NULL expect_equal(matsindf_apply(FUN = example_fun, a = a, b = b), list_expected) }) test_that("matsindf_apply() works as expected for lists of Matrix objects", { example_fun <- function(a, b) { return(list(c = matsbyname::sum_byname(a, b), d = matsbyname::difference_byname(a, b))) } a <- matsbyname::Matrix(c(1,2,3,4), nrow = 2, ncol = 2, byrow = TRUE, dimnames = list(c("r1", "r2"), c("c1", "c2")), rowtype = "rows", coltype = "cols") b <- a c <- matsbyname::sum_byname(a, b) d <- matsbyname::difference_byname(a, b) a <- list(a, a) b <- list(b, b) list_expected <- list(c = list(c, c), d = list(d, d)) # Because DF_expected$c and DF_expected$d are created with I(list()), their class is "AsIs". # Need to set the class of DF_expected$c and DF_expected$d to NULL to get a match. attr(list_expected$c, which = "class") <- NULL attr(list_expected$d, which = "class") <- NULL expect_equal(matsindf_apply(FUN = example_fun, a = a, b = b), list_expected) }) test_that("matsindf_apply() works as expected using .dat with single numbers", { example_fun <- function(a, b) { return(list(c = matsbyname::sum_byname(a, b), d = matsbyname::difference_byname(a, b))) } DF <- tibble::tibble(a = c(4, 4, 5), b = c(4, 4, 4)) expect_equal(matsindf_apply(DF, FUN = example_fun, a = "a", b = "b"), tibble::tibble(a = c(4, 4, 5), b = c(4, 4, 4), c = c(8, 8, 9), d = c(0, 0, 1))) }) test_that("matsindf_apply() works as expected using .DF with matrices", { example_fun <- function(a, b) { return(list(c = matsbyname::sum_byname(a, b), d = matsbyname::difference_byname(a, b))) } a <- matrix(c(1,2,3,4), nrow = 2, ncol = 2, byrow = TRUE, dimnames = list(c("r1", "r2"), c("c1", "c2"))) b <- a c <- a + b d <- a - b DF <- data.frame(a = I(list(a, a)), b = I(list(b,b))) result <- matsindf_apply(DF, FUN = example_fun, a = "a", b = "b") expected <- dplyr::bind_cols(DF, data.frame(c = I(list(c, c)), d = I(list(d, d)))) expect_equal(result, expected, ignore_attr = TRUE) # Try with piped .DF argument result <- DF |> matsindf_apply(FUN = example_fun, a = "a", b = "b") expect_equal(result, expected, ignore_attr = TRUE) }) test_that("matsindf_apply() works as expected using .DF with Matrix objects", { example_fun <- function(a, b) { return(list(c = matsbyname::sum_byname(a, b), d = matsbyname::difference_byname(a, b))) } a <- matsbyname::Matrix(c(1,2,3,4), nrow = 2, ncol = 2, byrow = TRUE, dimnames = list(c("r1", "r2"), c("c1", "c2")), rowtype = "rows", coltype = "cols") b <- a c <- matsbyname::sum_byname(a, b) d <- matsbyname::difference_byname(a, b) DF <- data.frame(a = I(list(a, a)), b = I(list(b,b)), stringsAsFactors = FALSE) result <- matsindf_apply(DF, FUN = example_fun, a = "a", b = "b") expected <- dplyr::bind_cols(DF, data.frame(c = I(list(c, c)), d = I(list(d, d)), stringsAsFactors = FALSE)) expect_equal(result, expected, ignore_attr = TRUE) # Try with piped .DF argument result <- DF |> matsindf_apply(FUN = example_fun, a = "a", b = "b") expect_equal(result, expected, ignore_attr = TRUE) }) test_that("matsindf_apply() works with .DF containing lists of Matrix objects, only some of which are named", { # This is a failure mode for one set of calculations in the PFUAGgDatabase pipeline, # namely when doing chops. # See if we can reproduce the error here. example_fun <- function(a, b) { return(list(c = matsbyname::sum_byname(a, b), d = matsbyname::difference_byname(a, b))) } a <- matsbyname::Matrix(c(1,2,3,4), nrow = 2, ncol = 2, byrow = TRUE, dimnames = list(c("r1", "r2"), c("c1", "c2")), rowtype = "rows", coltype = "cols") b <- a c <- matsbyname::sum_byname(a, b) d <- matsbyname::difference_byname(a, b) DF <- data.frame(a = I(list(a, named_a = a)), b = I(list(b, named_b = b)), stringsAsFactors = FALSE) result <- matsindf_apply(DF, FUN = example_fun, a = "a", b = "b") expected <- dplyr::bind_cols(DF, data.frame(c = I(list(c, c)), d = I(list(d, d)), stringsAsFactors = FALSE)) expect_equal(result, expected, ignore_attr = TRUE) }) test_that("matsindf_apply() fails as expected when not all same type for ...", { example_fun <- function(a, b) { return(list(c = matsbyname::sum_byname(a, b), d = matsbyname::difference_byname(a, b))) } matsindf_apply(FUN = example_fun, a = "a", b = 2) |> expect_warning("In matsindf::matsindf_apply\\(\\), the following named arguments to FUN were not found in any of .dat, ..., or defaults to FUN: a") |> expect_error('argument "a" is missing, with no default') }) test_that("matsindf_apply() fails as expected when wrong type of data is sent in ...", { example_fun <- function(a, b) { return(list(c = matsbyname::sum_byname(a, b), d = matsbyname::difference_byname(a, b))) } expect_error(matsindf_apply(FUN = example_fun, a = list("a"), b = list(2)), "non-numeric argument to binary operator") }) test_that("matsindf_apply() fails gracefully when some of ... are NULL", { example_fun <- function(a, b) { return(list(c = matsbyname::sum_byname(a, b), d = matsbyname::difference_byname(a, b))) } expect_error(matsindf_apply(FUN = example_fun, a = 1, b = 2, c = NULL), "In matsindf::matsindf_apply\\(\\), the following unused arguments appeared in ...: c") }) test_that("matsindf_apply() fails as expected when .DF argument is missing from a data frame", { example_fun <- function(a, b) { return(list(c = matsbyname::sum_byname(a, b), d = matsbyname::difference_byname(a, b))) } matsindf_apply(FUN = example_fun, a = "a", b = "b") |> expect_warning("In matsindf::matsindf_apply\\(\\), the following named arguments to FUN were not found in any of .dat, ..., or defaults to FUN: a, b") }) test_that("matsindf_apply() fails as expected when .DF argument is not a data frame or a list", { example_fun <- function(a, b) { return(list(c = matsbyname::sum_byname(a, b), d = matsbyname::difference_byname(a, b))) } matsindf_apply(.DF = "string", FUN = example_fun, a = "a", b = "b") |> expect_warning("In matsindf::matsindf_apply\\(\\), the following named arguments to FUN were not found in any of .dat, ..., or defaults to FUN: a, b") |> expect_error("In matsindf::matsindf_apply\\(\\), the following unused arguments appeared in ...: .DF") }) test_that("matsindf_apply() works with a NULL argument", { example_fun <- function(a, b) { return(list(c = matsbyname::sum_byname(a, b), d = matsbyname::difference_byname(a, b))) } a <- matrix(c(1,2,3,4), nrow = 2, ncol = 2, byrow = TRUE, dimnames = list(c("r1", "r2"), c("c1", "c2"))) b <- a c <- a + b d <- a - b DF <- data.frame(a = I(list(a, a)), b = I(list(b,b))) # Here is where the NULL is given as an argument to matsindf_apply. # This attempt fails, because z is an extra argument in ... . expect_error(matsindf_apply(DF, FUN = example_fun, a = "a", b = "b", z = NULL), "In matsindf::matsindf_apply\\(\\), the following unused arguments appeared in ...: z") # Try with piped .dat argument expect_error(matsindf_apply(DF, FUN = example_fun, a = "a", b = "b", z = NULL), "In matsindf::matsindf_apply\\(\\), the following unused arguments appeared in ...: z") }) test_that("matsindf_apply() works with a NULL argument and Matrix objects", { example_fun <- function(a, b) { return(list(c = matsbyname::sum_byname(a, b), d = matsbyname::difference_byname(a, b))) } a <- matsbyname::Matrix(c(1,2,3,4), nrow = 2, ncol = 2, byrow = TRUE, dimnames = list(c("r1", "r2"), c("c1", "c2"))) b <- a c <- matsbyname::sum_byname(a, b) d <- matsbyname::difference_byname(a, b) DF <- data.frame(a = I(list(a, a)), b = I(list(b,b)), stringsAsFactors = FALSE) # Here is where the NULL is given as an argument to matsindp_apply. # This attempt fails, because z is an extra argument in ... . expect_error(matsindf_apply(DF, FUN = example_fun, a = "a", b = "b", z = NULL), "In matsindf::matsindf_apply\\(\\), the following unused arguments appeared in ...: z") # Try with piped .DF argument expect_error(matsindf_apply(DF, FUN = example_fun, a = "a", b = "b", z = NULL), "In matsindf::matsindf_apply\\(\\), the following unused arguments appeared in ...: z") }) test_that("matsindf_apply() works when FUN returns NULL", { example_fun <- function(a, b) { return(list(c = matsbyname::sum_byname(a, b), d = matsbyname::difference_byname(a, b))) } df <- tibble::tibble(a = list(NULL, 20), b = list(NULL, 10)) res <- matsindf_apply(df, FUN = example_fun, a = "a", b = "b") expect_equal(res, df |> dplyr::mutate(c = c(0, 30), d = c(0, 10))) example_fun2 <- function(a, b) { return(list(c = NULL, d = NULL)) } df2 <- tibble::tibble(a = list(NULL, 20), b = list(NULL, 10)) res2 <- matsindf_apply(df2, FUN = example_fun2, a = "a", b = "b") expect_equal(res2, df2 |> dplyr::mutate(c = list(NULL, NULL), d = list(NULL, NULL))) }) test_that("matsindf_apply() works when all named arguments are NULL", { example_fun <- function(a, b) { return(list(c = matsbyname::sum_byname(a, b), d = matsbyname::difference_byname(a, b))) } expect_equal(matsindf_apply(FUN = example_fun, a = NULL, b = NULL), list(a = list(), b = list())) }) test_that("matsindf_apply() works when .dat is a list", { example_fun <- function(a, b) { return(list(c = matsbyname::sum_byname(a, b), d = matsbyname::difference_byname(a, b))) } expect_equal(matsindf_apply(list(a = 1, b = 2), FUN = example_fun, a = "a", b = "b"), list(a = 1, b = 2, c = 3, d = -1)) }) test_that("matsindf_apply() works when .dat supplies some or all argument names", { example_fun <- function(a, b) { return(list(c = matsbyname::sum_byname(a, b), d = matsbyname::difference_byname(a, b))) } # All arguments to FUN are supplied by named items in .dat (Case 11) expect_equal(matsindf_apply(list(a = 1, b = 2), FUN = example_fun), list(a = 1, b = 2, c = 3, d = -1)) # All arguments are supplied by named arguments in ... (Case 3) expect_equal(matsindf_apply(list(a = 1, b = 2), FUN = example_fun, a = "a", b = "b"), list(a = 1, b = 2, c = 3, d = -1)) # All arguments are supplied by named arguments in ..., but mix them up. (Case 3) # Note that the named arguments override the items in .DF expect_equal(matsindf_apply(list(a = 1, b = 2, z = 10), FUN = example_fun, a = "z", b = "b"), list(a = 1, b = 2, z = 10, c = 12, d = 8)) # Try when one of the output names is same as an input name (Case 3) expect_warning(res <- matsindf_apply(list(a = 1, b = 2, c = 10), FUN = example_fun, a = "c", b = "b"), "Name collision in matsindf::matsindf_apply\\(\\). The following arguments appear both in .dat and in the output of `FUN`: c") expect_equal(res, c(list(a = 1, b = 2, c = 10), list(c = 12, d = 8))) }) test_that("matsindf_apply() works for single numbers in data frame columns", { example_fun <- function(a, b) { return(list(c = matsbyname::sum_byname(a, b), d = matsbyname::difference_byname(a, b))) } DF <- tibble::tibble(a = c(4, 4, 5), b = c(4, 4, 4)) expected <- DF expected$c <- c(8, 8, 9) expected$d <- c(0, 0, 1) expect_equal(matsindf_apply(DF, FUN = example_fun, a = "a", b = "b"), expected) expect_equal(matsindf_apply(DF, FUN = example_fun), expected) }) test_that("override works() for single numbers supplied in a list", { example_fun <- function(a, b) { return(list(c = matsbyname::sum_byname(a, b), d = matsbyname::difference_byname(a, b))) } expect_equal(matsindf_apply(list(a = 2, b = 1), FUN = example_fun, a = 10), list(a = 10, b = 1, c = 11, d = 9)) }) test_that("matsindf_apply() works when an argument is missing", { outer_fun <- function(.DF = NULL, a = "a", b = "b") { inner_fun <- function(a_num, b_num = NULL) { return(list(c = matsbyname::sum_byname(a_num, b_num), d = matsbyname::difference_byname(a_num, b_num))) } matsindf_apply(.DF, FUN = inner_fun, a_num = a, b_num = b) } # Make sure it works when all arguments are present. expect_equal(outer_fun(a = 2, b = 2), list(c = 4, d = 0)) # Now try when an argument is missing and the inner function can handle it. expect_equal(outer_fun(a = 2), list(c = 2, d = 2)) # Try when an argument is missing and the inner function can't handle it. outer_fun(b = 2) |> expect_warning("In matsindf::matsindf_apply\\(\\), the following named arguments to FUN were not found in any of .dat, ..., or defaults to FUN: a_num") |> expect_error('argument "a_num" is missing, with no default') }) test_that("matsindf_apply() works with functions similar in form to those in `Recca`", { # This function is similar in form to functions in `Recca`. find_matching_rownames <- function(.DF = NULL, prefixes, m = "m", out_colname = "matching_rows") { rowmatch_fun <- function(prefixes_arg, m_arg) { out <- m_arg %>% matsbyname::select_rows_byname(retain_pattern = RCLabels::make_or_pattern(strings = prefixes_arg, pattern_type = "leading")) |> matsbyname::getrownames_byname() # If we don't have a list, make a list. if (!is.list(out)) { out <- list(out) } # At this point, we already have a list. # All that remains is to set the name of the list. out %>% magrittr::set_names(out_colname) } matsindf_apply(.DF, FUN = rowmatch_fun, prefixes_arg = prefixes, m_arg = m) } # Set up a matrix mat <- matrix(c(0, 1, 2, 3, 4, 5), nrow = 3, ncol = 2, byrow = TRUE, dimnames = list(c("ra1", "ra2", "b3"), c("c1", "c2"))) expect_equal(find_matching_rownames(prefixes = list("ra"), m = mat), list(matching_rows = c("ra1", "ra2"))) # Now make lists and try again. This works fine. res2 <- find_matching_rownames(prefixes = list("ra"), m = list(mat)) expect_type(res2, type = "list") expect_equal(res2[["matching_rows"]][[1]], "ra1") expect_equal(res2[["matching_rows"]][[2]], "ra2") # Use with lists and 2 items in each list. res3 <- find_matching_rownames(prefixes = list("ra", "ra"), m = list(mat, mat)) expect_type(res3, type = "list") expect_equal(res3[["matching_rows"]][[1]], c("ra1", "ra2")) expect_equal(res3[["matching_rows"]][[2]], c("ra1", "ra2")) expect_equal(length(res3), 1) # Try in a data frame. df <- tibble::tibble(prefixes = list("ra", c("r", "b"), "b"), m = list(mat, mat, mat)) |> find_matching_rownames(prefixes = "prefixes") expect_equal(df$matching_rows[[1]], c("ra1", "ra2")) expect_equal(df$matching_rows[[2]], c("ra1", "ra2", "b3")) expect_equal(df$matching_rows[[3]], "b3") }) test_that("matsindf_apply() works with functions similar in form to those in `Recca` with Matrix objects", { # This function is similar in form to functions in `Recca`. find_matching_rownames <- function(.DF = NULL, prefixes, m = "m", out_colname = "matching_rows") { rowmatch_fun <- function(prefixes_arg, m_arg) { out <- m_arg %>% matsbyname::select_rows_byname(retain_pattern = RCLabels::make_or_pattern(strings = prefixes_arg, pattern_type = "leading")) %>% matsbyname::getrownames_byname() # If we don't have a list, make a list. if (!is.list(out)) { out <- list(out) } # At this point, we already have a list. # All that remains is to set the name of the list. out %>% magrittr::set_names(out_colname) } matsindf_apply(.DF, FUN = rowmatch_fun, prefixes_arg = prefixes, m_arg = m) } # Set up a matrix mat <- matsbyname::Matrix(c(0, 1, 2, 3, 4, 5), nrow = 3, ncol = 2, byrow = TRUE, dimnames = list(c("ra1", "ra2", "b3"), c("c1", "c2"))) # This fails, because we go into find_matching_rownames without the string prefixes_arg argument. expect_equal(find_matching_rownames(prefixes = list("ra"), m = mat), list(matching_rows = c("ra1", "ra2"))) res2 <- find_matching_rownames(prefixes = list("ra"), m = list(mat)) expect_type(res2, type = "list") expect_equal(res2[["matching_rows"]][[1]], "ra1") expect_equal(res2[["matching_rows"]][[2]], "ra2") # Use with lists and 2 items in each list. (Case 15) res3 <- find_matching_rownames(prefixes = list("ra", "ra"), m = list(mat, mat)) expect_type(res3, type = "list") expect_equal(res3[["matching_rows"]][[1]], c("ra1", "ra2")) expect_equal(res3[["matching_rows"]][[2]], c("ra1", "ra2")) expect_equal(length(res3), 1) # Try in a data frame. (Case 13) df <- tibble::tibble(prefixes = list("ra", c("r", "b"), "b"), m = list(mat, mat, mat)) |> find_matching_rownames(prefixes = "prefixes") expect_equal(df$matching_rows[[1]], c("ra1", "ra2")) expect_equal(df$matching_rows[[2]], c("ra1", "ra2", "b3")) expect_equal(df$matching_rows[[3]], "b3") }) test_that("matsindf_apply() issues a warning when replacing a column", { # Create a data frame with a matrix in a column. a <- matrix(c(1,2,3,4), nrow = 2, ncol = 2, byrow = TRUE, dimnames = list(c("r1", "r2"), c("c1", "c2"))) b <- 2 * a DF <- data.frame(a = I(list(a, a)), b = I(list(b, b)), stringsAsFactors = FALSE) replace_func <- function(a_mat, b_mat) { a <- matsbyname::difference_byname(a_mat, b_mat) d <- matsbyname::sum_byname(a_mat, b_mat) list(a, d) %>% magrittr::set_names(c(a_name, d_name)) } a_name <- "a" d_name <- "d" expect_warning( suppressMessages( # The next call gives a warning but also a "New names:" message. # The message clutters the output of the tests, # so suppress it. # We already know of the problem and are testing for it, anyway. matsindf_apply(DF, FUN = replace_func, a_mat = "a", b_mat = "b") ), "Name collision in matsindf::matsindf_apply\\(\\). The following arguments appear both in .dat and in the output of `FUN`: a" ) }) test_that("matsindf_apply() issues a warning when replacing a column with Matrix objects", { # Create a data frame with a matrix in a column. a <- matsbyname::Matrix(c(1,2,3,4), nrow = 2, ncol = 2, byrow = TRUE, dimnames = list(c("r1", "r2"), c("c1", "c2"))) b <- matsbyname::hadamardproduct_byname(2, a) DF <- data.frame(a = I(list(a, a)), b = I(list(b, b)), stringsAsFactors = FALSE) replace_func <- function(a_mat, b_mat) { a <- matsbyname::difference_byname(a_mat, b_mat) d <- matsbyname::sum_byname(a_mat, b_mat) list(a, d) %>% magrittr::set_names(c(a_name, d_name)) } a_name <- "a" d_name <- "d" expect_warning( suppressMessages( # The next call gives a warning but also a "New names:" message. # The message clutters the output of the tests, # so suppress it. # We already know of the problem and are testing for it, anyway. matsindf_apply(DF, FUN = replace_func, a_mat = "a", b_mat = "b") ), "Name collision in matsindf::matsindf_apply\\(\\). The following arguments appear both in .dat and in the output of `FUN`: a" ) }) test_that("matsindf_apply() works for a string and numbers", { example_fun <- function(str_a, b) { a <- as.numeric(str_a) list(c = matsbyname::sum_byname(a, b), d = matsbyname::difference_byname(a, b)) } res <- matsindf_apply(FUN = example_fun, str_a = list("1"), b = list(2)) expect_equal(res$c[[1]], 3) expect_equal(res$d[[1]], -1) }) test_that("matsindf_apply() works as desired in degenerate case", { sum_fun <- function(a, b) { paste0(a, b) |> magrittr::set_names("term") } expected <- list(term = list("string142", "string243")) expect_equal( matsindf_apply(FUN = sum_fun, a = c("string1", "string2"), b = list("42", "43")), expected ) }) test_that("matsindf_apply() works as desired with zero-row data frames", { example_fun <- function(a_var, b_var) { return(list(c = matsbyname::sum_byname(a_var, b_var), d = matsbyname::difference_byname(a_var, b_var))) } a <- matsbyname::Matrix(c(1,2,3,4), nrow = 2, ncol = 2, byrow = TRUE, dimnames = list(c("r1", "r2"), c("c1", "c2"))) b <- a + 1 # Make a data frame df <- tibble::tribble(~acol, ~bcol, a, b, a, b) expected_df <- df |> dplyr::mutate( c = matsbyname::sum_byname(acol, bcol), d = matsbyname::difference_byname(acol, bcol) ) res <- matsindf_apply(df, FUN = example_fun, a_var = "acol", b_var = "bcol") expect_equal(res, expected_df) # Now try with no rows in df dfzero <- df[0, ] res <- matsindf_apply(dfzero, FUN = example_fun, a_var = "acol", b_var = "bcol") # Should return the input data frame unmodified. expect_equal(res, dfzero) }) test_that("matsindf_apply() works as desired with zero-length lists and matrix objects", { example_fun <- function(a_var, b_var) { return(list(c = matsbyname::sum_byname(a_var, b_var), d = matsbyname::difference_byname(a_var, b_var))) } a <- matrix(c(1,2,3,4), nrow = 2, ncol = 2, byrow = TRUE, dimnames = list(c("r1", "r2"), c("c1", "c2"))) b <- a + 1 c <- a + b d <- a - b expected_list <- list(c = list(c, c), d = list(d, d)) res <- matsindf_apply(FUN = example_fun, a_var = list(a, a), b_var = list(b, b)) expect_equal(res, expected_list) # Now try with zero-length lists res_zero <- matsindf_apply(FUN = example_fun, a_var = list(), b_var = list()) expect_equal(res_zero, list(a_var = list(), b_var = list())) }) test_that("matsindf_apply() works as desired with zero-length lists and Matrix objects", { example_fun <- function(a_var, b_var) { return(list(c = matsbyname::sum_byname(a_var, b_var), d = matsbyname::difference_byname(a_var, b_var))) } a <- matsbyname::Matrix(c(1,2,3,4), nrow = 2, ncol = 2, byrow = TRUE, dimnames = list(c("r1", "r2"), c("c1", "c2"))) b <- a + 1 c <- a + b d <- a - b expected_list <- list(c = list(c, c), d = list(d, d)) res <- matsindf_apply(FUN = example_fun, a_var = list(a, a), b_var = list(b, b)) expect_equal(res, expected_list) # Now try with zero-length lists res_zero <- matsindf_apply(FUN = example_fun, a_var = list(), b_var = list()) expect_equal(res_zero, list(a_var = list(), b_var = list())) }) test_that("matsindf_apply() works with empty lists", { example_fun <- function(a_var, b_var) { return(list(c = matsbyname::sum_byname(a_var, b_var), d = matsbyname::difference_byname(a_var, b_var))) } a <- matsbyname::Matrix(c(1,2,3,4), nrow = 2, ncol = 2, byrow = TRUE, dimnames = list(c("r1", "r2"), c("c1", "c2"))) b <- a + 1 a_list <- list(a = a) b_list <- list(b = b) a_list_0 <- a_list[0] b_list_0 <- b_list[0] # Try with zero-length lists (Case 5) res_zero <- matsindf_apply(FUN = example_fun, a_var = a_list_0, b_var = b_list_0) expect_equal(res_zero, list(a_var = a_list[0], b_var = b_list[0])) # Try with a zero-length variable store (Case 15) var_store <- list(a_var = a_list_0, b_var = b_list_0) res_zero_2 <- matsindf_apply(var_store, FUN = example_fun) expect_equal(res_zero_2, list(a_var = a_list[0], b_var = b_list[0])) }) test_that("matsindf_apply() works with a no-argument function", { example_fun <- function() {42} expect_equal(matsindf_apply(FUN = example_fun), 42) }) test_that("matsindf_apply_types() works as expected", { example_fun <- function(a, b) { c(a, b) } matsindf_apply_types(.dat = NULL, FUN = example_fun) |> expect_warning("In matsindf::matsindf_apply\\(\\), the following named arguments to FUN were not found in any of .dat, ..., or defaults to FUN: a, b") # Set the warning flag to FALSE to suppress the warning no_warn <- matsindf_apply_types(.dat = NULL, FUN = example_fun, .warn_missing_FUN_args = FALSE) expect_equal(no_warn$keep_args, list(.dat = NULL, FUN = NULL, dots = NULL)) matsindf_apply_types(.dat = NULL, FUN = example_fun, a = 1, b = 2) |> expect_equal(list(.dat_null = TRUE, .dat_df = FALSE, .dat_list = FALSE, .dat_names = NULL, FUN_arg_all_names = c("a", "b"), FUN_arg_default_names = NULL, FUN_arg_default_values = NULL, dots_present = TRUE, all_dots_num = TRUE, all_dots_mats = FALSE, all_dots_list = FALSE, all_dots_vect = TRUE, all_dots_char = FALSE, all_dots_longer_than_1 = FALSE, dots_names = c("a", "b"), keep_args = list(.dat = NULL, FUN = NULL, dots = c(a = "a", b = "b")))) matsindf_apply_types(.dat = data.frame(a = 42), FUN = example_fun, a = matrix(c(1, 2)), b = matrix(c(2, 3))) |> expect_equal(list(.dat_null = FALSE, .dat_df = TRUE, .dat_list = TRUE, .dat_names = "a", FUN_arg_all_names = c("a", "b"), FUN_arg_default_names = NULL, FUN_arg_default_values = NULL, dots_present = TRUE, all_dots_num = FALSE, all_dots_mats = TRUE, all_dots_list = FALSE, all_dots_vect = FALSE, all_dots_char = FALSE, all_dots_longer_than_1 = FALSE, dots_names = c("a", "b"), keep_args = list(.dat = NULL, FUN = NULL, dots = c(a = "a", b = "b")))) matsindf_apply_types(.dat = list(a = 1, b = 2), FUN = example_fun, a = list(1, 2), b = list(3, 4)) |> expect_equal(list(.dat_null = FALSE, .dat_df = FALSE, .dat_list = TRUE, .dat_names = c("a", "b"), FUN_arg_all_names = c("a", "b"), FUN_arg_default_names = NULL, FUN_arg_default_values = NULL, dots_present = TRUE, all_dots_num = FALSE, all_dots_mats = FALSE, all_dots_list = TRUE, all_dots_vect = TRUE, all_dots_char = FALSE, all_dots_longer_than_1 = TRUE, dots_names = c("a", "b"), keep_args = list(.dat = NULL, FUN = NULL, dots = c(a = "a", b = "b")))) matsindf_apply_types(.dat = NULL, FUN = example_fun, a = "a", b = "b") |> expect_warning("In matsindf::matsindf_apply\\(\\), the following named arguments to FUN were not found in any of .dat, ..., or defaults to FUN: a, b") # Try with Matrix objects matsindf_apply_types(.dat = NULL, FUN = example_fun, a = matsbyname::Matrix(c(1, 2)), b = matsbyname::Matrix(c(2, 3))) |> expect_equal(list(.dat_null = TRUE, .dat_df = FALSE, .dat_list = FALSE, .dat_names = NULL, FUN_arg_all_names = c("a", "b"), FUN_arg_default_names = NULL, FUN_arg_default_values = NULL, dots_present = TRUE, all_dots_num = FALSE, all_dots_mats = TRUE, all_dots_list = FALSE, all_dots_vect = FALSE, all_dots_char = FALSE, all_dots_longer_than_1 = FALSE, dots_names = c("a", "b"), keep_args = list(.dat = NULL, FUN = NULL, dots = c(a = "a", b = "b")))) # Try with data coming from one but not the other source. matsindf_apply_types(.dat = data.frame(a = 42), FUN = example_fun, b = matrix(c(2, 3))) |> expect_equal(list(.dat_null = FALSE, .dat_df = TRUE, .dat_list = TRUE, .dat_names = "a", FUN_arg_all_names = c("a", "b"), FUN_arg_default_names = NULL, FUN_arg_default_values = NULL, dots_present = TRUE, all_dots_num = FALSE, all_dots_mats = TRUE, all_dots_list = FALSE, all_dots_vect = FALSE, all_dots_char = FALSE, all_dots_longer_than_1 = FALSE, dots_names = "b", keep_args = list(.dat = c(a = "a"), FUN = NULL, dots = c(b = "b")))) }) test_that("matsindf_apply_types() works with functions that have default values", { example_fun <- function(a = 2, b, c = "string") { list(a = a, b = b, c = c) } expect_equal(matsindf_apply_types(.dat = NULL, FUN = example_fun, a = 1, b = 2), list(.dat_null = TRUE, .dat_df = FALSE, .dat_list = FALSE, .dat_names = NULL, FUN_arg_all_names = c("a", "b", "c"), FUN_arg_default_names = c("a", "c"), FUN_arg_default_values = list(a = 2, c = "string"), dots_present = TRUE, all_dots_num = TRUE, all_dots_mats = FALSE, all_dots_list = FALSE, all_dots_vect = TRUE, all_dots_char = FALSE, all_dots_longer_than_1 = FALSE, dots_names = c("a", "b"), keep_args = list(.dat = NULL, FUN = c(c = "c"), dots = c(a = "a", b = "b")))) }) test_that("matsindf_apply_types() works with a degenerate case with simple FUN and no parameters", { example_fun <- function() {42} expect_equal(matsindf_apply_types(FUN = example_fun), list(.dat_null = TRUE, .dat_df = FALSE, .dat_list = FALSE, .dat_names = NULL, FUN_arg_all_names = NULL, FUN_arg_default_names = NULL, FUN_arg_default_values = NULL, dots_present = FALSE, all_dots_num = FALSE, all_dots_mats = FALSE, all_dots_list = FALSE, all_dots_vect = FALSE, all_dots_char = FALSE, all_dots_longer_than_1 = FALSE, dots_names = NULL, keep_args = list(.dat = NULL, FUN = NULL, dots = NULL))) }) test_that("matsindf_apply_types() works for examples", { identity_fun <- function(a, b) {list(a = a, b = b)} types <- matsindf_apply_types(.dat = data.frame(), FUN = identity_fun, a = matrix(c(1, 2)), b = matrix(c(2, 3))) expect_null(types$keep_args$.dat) }) test_that("build_matsindf_apply_data_frame() works as expected", { example_fun <- function(a_var, b_var = c(42, 43)) { c(a_var, b_var) } DF <- tibble::tribble(~a, ~b, ~z, 1, 2, 3, 4, 5, NULL) expected_df <- DF |> dplyr::select("a", "b") |> dplyr::rename( a_var = "a", b_var = "b" ) res_df <- build_matsindf_apply_data_frame(.dat = DF, FUN = example_fun, a_var = "a", b_var = "b") expect_equal(names(res_df), c("a_var", "b_var")) expect_equal(res_df, expected_df) DF_2 <- DF |> dplyr::select(-b) expected_df_2 <- DF |> dplyr::mutate( b_var = c(42, 43), b = NULL) |> dplyr::rename(a_var = "a") |> dplyr::select(a_var, b_var) res_df_2 <- build_matsindf_apply_data_frame(.dat = DF_2, FUN = example_fun, a_var = "a") expect_equal(res_df_2, expected_df_2) }) test_that("build_matsindf_apply_data_frame() works with NULL args in ...", { example_fun <- function(R_mat, U_mat) { return(matsbyname::sum_byname(R_mat, matsbyname::transpose_byname(U_mat))) } expected <- tibble::tribble(~R_mat, ~U_mat, NULL, matrix(c(42, 43))) res <- build_matsindf_apply_data_frame(FUN = example_fun, R_mat = NULL, U_mat = matrix(c(42, 43))) expect_equal(res, expected) }) test_that("matsindf_apply() works when FUN can handle zero-row DF's", { example_fun <- function(a, b) { if (length(a) == 0 & length(b) == 0) { return(list(c = numeric(), d = numeric())) } return(list(c = matsbyname::sum_byname(a, b), d = matsbyname::difference_byname(a, b))) } expect_equal(example_fun(a = 2, b = 2), list(c = 4, d = 0)) expect_equal(matsindf_apply(FUN = example_fun, a = 2, b = 2), list(c = 4, d = 0)) expect_equal(matsindf_apply(list(a = 2, b = 2), FUN = example_fun), list(a = 2, b = 2, c = 4, d = 0)) df <- data.frame(a = 2, b = 2) df_expected <- tibble::tibble(a = 2, b = 2, c = 4, d = 0) expect_equal(matsindf_apply(df, example_fun), df_expected) dfzero <- df[0, ] dfzero_expected <- df_expected[0, ] expect_equal(matsindf_apply(dfzero, example_fun), dfzero_expected) }) test_that("matsindf_apply() works when FUN can handle zero-length lists", { example_fun <- function(a, b) { if (length(a) == 0 & length(b) == 0) { return(list(c = numeric(), d = numeric())) } return(list(c = matsbyname::sum_byname(a, b), d = matsbyname::difference_byname(a, b))) } expect_equal(matsindf_apply(FUN = example_fun, a = numeric(), b = numeric()), list(c = numeric(), d = numeric())) input <- list(a = numeric(), b = numeric()) output_expected <- list(a = numeric(), b = numeric(), c = numeric(), d = numeric()) expect_equal(matsindf_apply(input, example_fun), output_expected) }) test_that("matsindf_apply() works for an edge case", { # This example comes from the matsindf_apply primer. calc_W <- function(.DF = NULL, U = "U", V = "V", W = "W") { # The inner function does all the work. W_func <- function(U_mat, V_mat){ # When we get here, U_mat and V_mat will be single matrices or single numbers, # not a column in a data frame or an item in a list. if (length(U_mat) == 0 & length(V_mat == 0)) { # Tolerate zero-length arguments by returning a zero-length # a list with the correct name and return type. return(list(numeric()) |> magrittr::setnames(W)) } # Calculate W_mat from the inputs U_mat and V_mat. W_mat <- matsbyname::difference_byname(matsbyname::transpose_byname(V_mat), U_mat) # Return a named list. list(W_mat) |> magrittr::set_names(W) } # The body of the main function consists of a call to matsindf_apply # that specifies the inner function in the FUN argument. matsindf_apply(.DF, FUN = W_func, U_mat = U, V_mat = V) } expect_equal(calc_W(list(U = c(2, 2, 2, 2), V = c(3, 4, 5, 6))), list(U = c(2, 2, 2, 2), V = c(3, 4, 5, 6), W = c(1, 2, 3, 4))) }) test_that("should_unlist() works as expected", { DF <- tibble::tibble(a = list(1, 2, 3), b = c("a", "b", "c"), c = list(matrix(c(42, 43)), matrix(c(44, 45)), matrix(c(46, 47)))) expect_true(matsindf:::should_unlist(DF$a)) expect_false(matsindf:::should_unlist(DF$b)) expect_false(matsindf:::should_unlist(DF$c)) expect_equal(sapply(DF, FUN = function(this_col) {matsindf:::should_unlist(this_col)}), c(a = TRUE, b = FALSE, c = FALSE)) expect_equal(matsindf:::should_unlist(DF), c(a = TRUE, b = FALSE, c = FALSE)) }) test_that("should_unlist() correctly says 'FALSE' for a list of NULL items", { # Unlist when length is 1 expect_true(matsindf:::should_unlist(list(NULL))) # Do not unlist when the length is > 1 expect_false(matsindf:::should_unlist(list(NULL, NULL, NULL))) }) test_that("should_unlist() correctly says 'FALSE' for a list of data frames", { my_col <- list(data.frame(a = 42), tibble::tibble(b = 43) ) expect_false(matsindf:::should_unlist(my_col)) }) test_that("matsindf_apply() works for missing arg in .dat", { example_fun <- function(a_val = 2, b_val = 1) { list(c = matsbyname::sum_byname(a_val, b_val), d = matsbyname::difference_byname(a_val, b_val)) } # Try when everything is available. df <- tibble::tibble(a = c(10, 11, 12), b = c(5, 6, 7)) expected_df <- dplyr::bind_cols(df, data.frame(c = c(15, 17, 19), d = c(5, 5, 5))) res <- df |> matsindf_apply(FUN = example_fun, a_val = "a", b_val = "b") expect_equal(res, expected_df) # Try when df lacks a column. # matsindf_apply() should use the default value in the signature of example_fun. # But as of 4 April 2023, it does not. # This test exposes that bug. res <- df |> dplyr::select("a") |> matsindf_apply(FUN = example_fun, a_val = "a") expect_equal(res, tibble::tribble(~a, ~c, ~d, 10, 11, 9, 11, 12, 10, 12, 13, 11)) }) test_that("where_to_get_args() works as intended", { example_fun <- function(a = 1, b) { list(c = a + b, d = a - b) } matsindf:::where_to_get_args(FUN = example_fun) |> expect_equal(list(a = c(source = "FUN", arg_name = "a"), b = NULL)) matsindf:::where_to_get_args(FUN = example_fun, b = 2) |> expect_equal(list(a = c(source = "FUN", arg_name = "a"), b = c(source = "...", arg_name = "b"))) matsindf:::where_to_get_args(list(a = 2), FUN = example_fun, b = 2) |> expect_equal(list(a = c(source = ".dat", arg_name = "a"), b = c(source = "...", arg_name = "b"))) matsindf:::where_to_get_args(list(a = 2, b = 2), FUN = example_fun) |> expect_equal(list(a = c(source = ".dat", arg_name = "a"), b = c(source = ".dat", arg_name = "b"))) # Map to self matsindf:::where_to_get_args(list(a = 2, b = 2, c = 2), FUN = example_fun, a = "a", b = "b") |> expect_equal(list(a = c(source = ".dat", arg_name = "a"), b = c(source = ".dat", arg_name = "b"))) # Check redirection matsindf:::where_to_get_args(list(a = 2, b = 2, c = 2), FUN = example_fun, b = "c") |> expect_equal(list(a = c(source = ".dat", arg_name = "a"), b = c(source = ".dat", arg_name = "c"))) # Leave an argument out matsindf:::where_to_get_args(list(a = 2), FUN = example_fun) |> expect_equal(list(a = c(source = ".dat", arg_name = "a"), b = NULL)) # Try redirecting to an item that doesn't exist in .dat matsindf:::where_to_get_args(list(a = 2, b = 2, c = 2), FUN = example_fun, b = "d") |> expect_equal(list(a = c(source = ".dat", arg_name = "a"), b = NULL)) # In this case a comes from .dat and b comes from .dat as well. matsindf:::where_to_get_args(list(a = 2, c = 2), FUN = example_fun, b = "a") |> expect_equal(list(a = c(source = ".dat", arg_name = "a"), b = c(source = ".dat", arg_name = "a"))) # In this case, both a and b should be found in defaults to FUN. matsindf:::where_to_get_args(list(c = 2), FUN = example_fun, b = "a") |> expect_equal(list(a = c(source = "FUN", arg_name = "a"), b = c(source = "FUN", arg_name = "a"))) # In this case, both a and b should be found in .dat. matsindf:::where_to_get_args(list(a = 2, c = 2), FUN = example_fun, b = "a") |> expect_equal(list(a = c(source = ".dat", arg_name = "a"), b = c(source = ".dat", arg_name = "a"))) # Cross the mappings matsindf:::where_to_get_args(list(a = 2, b = 2), FUN = example_fun, a = "b", b = "a") |> expect_equal(list(a = c(source = ".dat", arg_name = "b"), b = c(source = ".dat", arg_name = "a"))) }) test_that("matsindf_apply() handles different lengths correctly (i.e., with an error)", { example_fun <- function(a = 1, b) { list(c = a + b, d = a - b) } expect_error(matsindf_apply(FUN = example_fun, a = c(1, 2, 3), b = c(4, 5)), "Different lengths in handle_null_args\\(\\)") }) test_that("matsindf_apply() correctly handles NULL default args on FUN", { example_fun <- function(a = 1, b = NULL) { list(c = a, d = b) } expect_equal(matsindf:::where_to_get_args(list(a = 1), FUN = example_fun), list(a = c(source = ".dat", arg_name = "a"), b = c(source = "FUN", arg_name = "b"))) expect_equal(matsindf_apply(list(a = 1), FUN = example_fun), list(a = 1, b = NULL, c = 1, d = NULL)) }) test_that("matsindf_apply() works correctly for a list of matrices each with length 1", { example_fun <- function(a, b) { list(c = matsbyname::sum_byname(a, b), d = matsbyname::difference_byname(a, b)) } m <- matrix(c(1, 2, 3, 4), nrow = 2, ncol = 2, byrow = TRUE) my_mat_list <- list(a = m, b = m+1) expected_c <- my_mat_list$a + my_mat_list$b expected_d <- my_mat_list$a - my_mat_list$b expect_equal(matsindf_apply(my_mat_list, example_fun), list(a = list(my_mat_list$a), b = list(my_mat_list$b), c = expected_c, d = expected_d)) expect_equal(matsindf_apply(FUN = example_fun, a = m, b = m+1), list(c = expected_c, d = expected_d)) })