tests/testthat/test-make-afun.R

context("make_afun and related machinery")

value_labels <- rtables:::value_labels
test_that("afun internals coverage", {
  ## use existing funcs to ensure coverage numbers are correct
  res_tafun1 <- rtables:::test_afun(1:10, 5)
  expect_identical(
    value_labels(res_tafun1),
    c(
      "Min." = "Minimum",
      "1st Qu." = "1st Quartile",
      Median = "Median",
      Mean = "Mean",
      "3rd Qu." = "Third Quartile",
      "Max." = "Maximum",
      grp = "grp"
    )
  )

  res_tafun2 <- rtables:::test_afun_grp(1:10, 5)
  expect_identical(
    lapply(res_tafun2, obj_format),
    list(
      "Min." = "xx.x",
      "1st Qu." = "xx.xx",
      Median = NULL,
      Mean = NULL,
      "3rd Qu." = "xx.xx",
      "Max." = "xx.x",
      range = "xx - xx",
      n_unique = "xx - xx"
    )
  )


  foo <- function(x, .N_col, ...) list(a = character(0))
  afoo <- make_afun(foo)
  expect_silent(afoo(factor(character(0)), .N_col = 100))
})


test_that("make_afun works for x arg", {
  s_summary <- function(x) {
    stopifnot(is.numeric(x))

    list(
      n = sum(!is.na(x)),
      mean_sd = c(mean = mean(x), sd = sd(x)),
      min_max = range(x)
    )
  }

  a_summary <- make_afun(
    fun = s_summary,
    .formats = c(n = "xx", mean_sd = "xx.xx (xx.xx)", min_max = "xx.xx - xx.xx"),
    .labels = c(n = "n", mean_sd = "Mean (sd)", min_max = "min - max")
  )
  expect_identical(
    formals(a_summary),
    formals(s_summary)
  )

  asres1 <- a_summary(x = iris$Sepal.Length)
  expect_identical(
    c(n = "n", mean_sd = "Mean (sd)", min_max = "min - max"),
    value_labels(asres1)
  )

  a_summary2 <- make_afun(a_summary, .stats = c("n", "mean_sd"))
  expect_identical(
    formals(a_summary2),
    formals(s_summary)
  )
  asres1 <- a_summary2(x = iris$Sepal.Length)
  expect_identical(names(asres1), c("n", "mean_sd"))

  a_summary3 <- make_afun(a_summary, .formats = c(mean_sd = "(xx.xxx, xx.xxx)"))

  asres3 <- a_summary3(iris$Sepal.Length)
  expect_equal(
    lapply(asres3, obj_format),
    list(
      n = "xx",
      mean_sd = "(xx.xxx, xx.xxx)",
      min_max = "xx.xx - xx.xx"
    )
  )
})


## also used for .indent_mod test, by happenstance
s_foo <- function(df, .N_col, a = 1, b = 2) {
  list(
    nrow_df = nrow(df),
    .N_col = .N_col,
    a = a,
    b = b
  )
}
test_that("make_afun works for df functions", {
  a_foo <- make_afun(s_foo,
    b = 4,
    .formats = c(nrow_df = "xx.xx", ".N_col" = "xx.", a = "xx", b = "xx.x"),
    .labels = c(
      nrow_df = "Nrow df",
      ".N_col" = "n in cols",
      a = "a value",
      b = "b value"
    )
  )

  expect_identical(
    formals(a_foo),
    formals(s_foo)
  )
  ares1 <- a_foo(iris, .N_col = 40)
  expect_identical(
    unlist(unname(value_labels(ares1))),
    c("Nrow df", "n in cols", "a value", "b value")
  )

  expect_equal(unlist(ares1$b), 4)
  expect_equal(unlist(ares1$a), 1)

  ares1b <- a_foo(iris, .N_col = 40, b = 8)
  expect_identical(
    unlist(unname(value_labels(ares1))),
    c("Nrow df", "n in cols", "a value", "b value")
  )

  expect_equal(unlist(ares1b$b), 8)

  a_foo2 <- make_afun(a_foo, .labels = c(nrow_df = "Number of Rows"))
  ares2 <- a_foo2(iris, .N_col = 40, b = 6)
  expect(unlist(ares2$b), 6)
  expect_identical(
    unlist(unname(value_labels(ares2))),
    c("Number of Rows", "n in cols", "a value", "b value")
  )
})


test_that("make_afun works for funs with ...", {
  ## with dots


  sfun3 <- function(x, .ref_group = NULL, ...) "hi"
  afun3 <- make_afun(sfun3)
  expect_identical(
    formals(sfun3),
    formals(afun3)
  )
})


test_that("ungrouping .ungroup_stats works", {
  ## ungrouping
  sfun4 <- function(x) {
    list(
      single1 = 5,
      single2 = 10,
      grouped1 = list(g1 = 11, g2 = 15, g3 = with_label(17, "sneaky label")),
      grouped2 = list(g4 = c(2, 3), g5 = c(6, 10))
    )
  }
  afun4 <- make_afun(sfun4,
    .labels = c(
      single1 = "first single val",
      single2 = "second single val"
    ),
    .formats = c(grouped2 = "xx - xx"),
    .ungroup_stats = c("grouped1", "grouped2")
  )
  expect_identical(formals(sfun4), formals(afun4))
  ares4 <- afun4(5)
  expect_identical(
    names(ares4),
    c("single1", "single2", "g1", "g2", "g3", "g4", "g5")
  )
  expect_identical(
    value_labels(ares4),
    c(
      single1 = "first single val",
      single2 = "second single val",
      g1 = "g1",
      g2 = "g2",
      g3 = "sneaky label",
      g4 = "g4",
      g5 = "g5"
    )
  )
})

get_imods <- function(obj) {
  setNames(rtables:::indent_mod(obj), names(obj))
}

test_that("make_afun .indent_mods argument works", {
  ## .indent_mod
  a_imod <- make_afun(s_foo, .indent_mods = c(
    nrow_df = -1L,
    a = 1L,
    b = 2L
  ))
  imodres1 <- a_imod(iris, 5)
  expect_identical(
    get_imods(imodres1),
    c(nrow_df = -1L, .N_col = 0L, a = 1L, b = 2L)
  )

  a_imod2 <- make_afun(a_imod, .indent_mods = c(
    nrow_df = 2L,
    .N_col = 1L
  ))
  imodres2 <- a_imod2(iris, 5)
  expect_identical(
    get_imods(imodres2), # vapply(imodres2, rtables:::indent_mod, 1L),
    c(nrow_df = 2L, .N_col = 1L, a = 1L, b = 2L)
  )

  tbl <- basic_table() %>%
    analyze("Sepal.Length", a_imod) %>%
    build_table(iris)
  rows <- tree_children(tbl)
  expect_identical(
    vapply(rows, rtables:::indent_mod, 1L),
    c(nrow_df = -1L, .N_col = 0L, a = 1L, b = 2L)
  )
})

test_that("make_afun override arg that has no default", {
  s_nodflt <- function(df, .N_col, a = 1, b) {
    list(
      nrow_df = nrow(df),
      .N_col = .N_col,
      a = a,
      b = b
    )
  }

  a_nodflt <- make_afun(s_nodflt,
    b = 4,
    .formats = c(
      nrow_df = "xx.xx",
      ".N_col" = "xx.",
      a = "xx",
      b = "xx.x"
    ),
    .labels = c(
      nrow_df = "Nrow df",
      ".N_col" = "n in cols",
      a = "a value",
      b = "b value"
    )
  )
  nodfltres <- a_nodflt(iris, 5)
  expect_equal(4, rtables:::rawvalues(nodfltres[["b"]]))
})




test_that("make_afun+build_table integration tests", {
  ## summary function that uses with_label
  s_summary <- function(x) {
    stopifnot(is.numeric(x))

    list(
      n = with_label(sum(!is.na(x)), "N subjects"),
      mean_sd = with_label(
        c(
          mean = mean(x),
          sd = sd(x)
        ),
        "My mean and SD"
      ),
      min_max = with_label(range(x), "Range")
    )
  }
  a_summary <- make_afun(
    s_summary,
    .labels = c(n = "n subjects"), # only overwrite the label of the `n` statistics here.
    .formats = c(
      n = "xx",
      mean_sd = "xx.xx (xx.xx)",
      min_max = "xx.xx - xx.xx"
    )
  )
  tbl <- basic_table() %>%
    analyze(
      "Sepal.Length",
      afun = a_summary
    ) %>%
    build_table(iris)

  expect_identical(
    row.names(tbl),
    c(
      "n subjects",
      "My mean and SD",
      "Range"
    )
  )

  tbl2 <- basic_table() %>%
    split_cols_by("Species") %>%
    analyze(
      "Sepal.Length",
      afun = a_summary
    ) %>%
    build_table(iris)

  expect_identical(
    row.names(tbl2),
    c(
      "n subjects",
      "My mean and SD",
      "Range"
    )
  )

  ## summary function that does not use with_label
  s_summary2 <- function(x) {
    stopifnot(is.numeric(x))

    list(
      n = sum(!is.na(x)),
      mean_sd = c(mean = mean(x), sd = sd(x)),
      min_max = range(x)
    )
  }

  a_summary2 <- make_afun(
    fun = s_summary2,
    .formats = c(
      n = "xx",
      mean_sd = "xx.xx (xx.xx)",
      min_max = "xx.xx - xx.xx"
    ),
    .labels = c(
      n = "n subjects",
      mean_sd = "My mean and SD",
      min_max = "Range"
    )
  )
  tbl3 <- basic_table() %>%
    analyze(
      "Sepal.Length",
      afun = a_summary2
    ) %>%
    build_table(iris)

  expect_identical(
    row.names(tbl3),
    c(
      "n subjects",
      "My mean and SD",
      "Range"
    )
  )

  tbl4 <- basic_table() %>%
    split_cols_by("Species") %>%
    analyze(
      "Sepal.Length",
      afun = a_summary2
    ) %>%
    build_table(iris)

  expect_identical(
    row.names(tbl4),
    c(
      "n subjects",
      "My mean and SD",
      "Range"
    )
  )

  ## recursive make_afun applications
  ## with with_label

  a_function3 <- make_afun(
    a_summary,
    .labels = c(min_max = "New Range")
  )
  tbl5 <- basic_table() %>%
    split_cols_by("Species") %>%
    analyze("Sepal.Length",
      afun = a_function3
    ) %>%
    build_table(iris)

  expect_identical(
    row.names(tbl5),
    c(
      "n subjects",
      "My mean and SD",
      "New Range"
    )
  )

  a_function4 <- make_afun(
    a_summary2,
    .labels = c(min_max = "New Range")
  )
  tbl5 <- basic_table() %>%
    split_cols_by("Species") %>%
    analyze("Sepal.Length",
      afun = a_function4
    ) %>%
    build_table(iris)

  expect_identical(
    row.names(tbl5),
    c(
      "n subjects",
      "My mean and SD",
      "New Range"
    )
  )

  ## .indent_mod appears in .indent_mod unit test section
  ## currently
})

## regression for bug #102

test_that("call-time ... passed down correctly by funs constructed by make_afun", {
  f <- function(x, a, ...) {
    list(a = a, ...)
  }

  af <- make_afun(f, .stats = "b")
  res1 <- af(5, a = 6, b = 7)
  expect_identical(list(b = 7), rtables:::rawvalues(res1))


  f2 <- function(df, a, ...) {
    list(a = a, ...)
  }

  af2 <- make_afun(f2, .stats = "b")
  res2 <- af2(iris, a = 5, b = 7)
  expect_identical(list(b = 7), rtables:::rawvalues(res2))
})


test_that(".format_na_strs works in make_afun", {
  s_fun <- function(x, ...) list(stuff = NA)

  afun <- make_afun(s_fun,
    .stats = "stuff",
    .formats = list(stuff = "xx.x"),
    .format_na_strs = list(stuff = "wat")
  )
  res <- afun(1:10)
  expect_identical(
    format_rcell(res[[1]]),
    "wat"
  )
})

test_that("list_wrap functions work", {
  f1 <- list_wrap_x(summary)

  expect_identical(
    f1(1:10),
    as.list(summary(1:10))
  )


  infun <- function(df) summary(df[[1]])
  f2 <- list_wrap_df(infun)
  expect_identical(
    f2(mtcars),
    f1(mtcars[[1]])
  )
})

Try the rtables package in your browser

Any scripts or data that you put into this service are public.

rtables documentation built on June 27, 2024, 9:06 a.m.