tests/testthat/test-across.R

# arrange -----------------------------------------------------
test_df <- tidytable(a = c("a", "b", "a"), b = 3:1)

test_that("ascending order works", {
  across_df <- arrange(test_df, across())

  check_df <- arrange(test_df, a, b)

  expect_equal(across_df, check_df)
})

test_that("descending order works with desc.", {
  across_df <- arrange(test_df, across(everything(), desc.))

  check_df <- arrange(test_df, -a, -b)

  expect_equal(across_df, check_df)
})

test_that("descending order works with desc", {
  across_df <- arrange(test_df, across(everything(), desc))

  check_df <- arrange(test_df, -a, -b)

  expect_equal(across_df, check_df)
})

test_that("descending order works with rlang lambda", {
  across_df <- arrange(test_df, across(everything(), ~ desc(.x)))

  check_df <- arrange(test_df, -a, -b)

  expect_equal(across_df, check_df)
})

test_that("works with dot", {
  across_df <- arrange(test_df, across.())

  check_df <- arrange(test_df, a, b)

  expect_equal(across_df, check_df)
})

# mutate -----------------------------------------------------
test_that(".cols selection works", {
  df <- data.table(x_start = c(1,1,1), end_x = c(2,2,2), z = c("a", "a", "b"))
  df <- df %>%
    mutate(across(where(is.numeric), function(.x) .x + 1))

  expect_equal(df$x_start, c(2,2,2))
  expect_equal(df$end_x, c(3,3,3))
})

test_that("works with newly named columns & allows other transforms", {
  df <- data.table(x = c(1,1,1), y = c(2,2,2), z = c("a", "a", "b"))
  df <- df %>%
    mutate(
      across(c(x:y), list(new = ~ .x + 1)),
      double_x = x * 2
    )

  expect_named(df, c("x","y","z","x_new","y_new", "double_x"))
  expect_equal(df$x_new, c(2,2,2))
  expect_equal(df$y_new, c(3,3,3))
})

test_that("works with newly named columns using .names with single .fn", {
  df <- data.table(x = c(1,1,1), y = c(2,2,2), z = c("a", "a", "b"))
  df <- df %>%
    mutate(across(c(x:y), ~ .x + 1, .names = "new_{.col}"))

  expect_named(df, c("x","y","z","new_x","new_y"))
  expect_equal(df$new_x, c(2,2,2))
  expect_equal(df$new_y, c(3,3,3))
})

test_that("works with newly named columns using .names", {
  df <- data.table(x = c(1,1,1), y = c(2,2,2), z = c("a", "a", "b"))
  df <- df %>%
    mutate(across(c(x:y), list(new = function(.x) .x + 1), .names = "{.fn}_{.col}"))

  expect_named(df, c("x","y","z","new_x","new_y"))
  expect_equal(df$new_x, c(2,2,2))
  expect_equal(df$new_y, c(3,3,3))
})

test_that("works with newly named columns using .names w/ autonaming", {
  df <- data.table(x = c(1,1,1), y = c(2,2,2), z = c("a", "a", "b"))
  df <- df %>%
    mutate(across(c(x:y), list(new = ~ .x + 1, ~ .x + 2), .names = "{.col}_{.fn}_stuff"))

  expect_named(df, c("x","y","z","x_new_stuff", "x_2_stuff","y_new_stuff", "y_2_stuff"))
  expect_equal(df$x_new_stuff, c(2,2,2))
  expect_equal(df$y_new_stuff, c(3,3,3))
  expect_equal(df$x_2_stuff, c(3,3,3))
  expect_equal(df$y_2_stuff, c(4,4,4))
})

test_that(".cols doesn't use .by columns", {
  test_df <- data.table(
    x = c(1,2,3),
    y = c(4,5,6),
    z = c("a","a","b")
  )

  results_df <- test_df %>%
    mutate(across(everything(), ~ mean(.x)), .by = z)

  expect_named(results_df, c("x", "y", "z"))
  expect_equal(results_df$x, c(1.5, 1.5, 3))
  expect_equal(results_df$y, c(4.5, 4.5, 6))
  expect_equal(test_df$x, c(1,2,3))
})

test_that("can refer to variables in the data.table", {
  test_df <- data.table(x = c(1,1,1), y = c(2,2,2), z = c("a", "a", "b"))

  results_df <- test_df %>%
    mutate(across(c(x, y), ~ .x + y))

  expect_equal(results_df$x, c(3,3,3))
  expect_equal(results_df$y, c(4,4,4))
})

test_that("can refer to variables in the data.table w/ list of .fns", {
  test_df <- data.table(x = c(1,1,1), y = c(2,2,2), z = c("a", "a", "b"))

  results_df <- test_df %>%
    mutate(across(x, list(~ .x + 1, new = ~ .x + y)))

  expect_equal(results_df$x_1, c(2,2,2))
  expect_equal(results_df$x_new, c(3,3,3))
})

test_that("can use bare functions", {
  test_df <- tidytable(x = 1:3, y = 2:4, z = c("a", "a", "b"))

  results_df <- test_df %>%
    mutate(across(c(x, y), between, 1, 3))

  expect_equal(results_df$x, c(TRUE, TRUE, TRUE))
  expect_equal(results_df$y, c(TRUE, TRUE, FALSE))
})

test_that("can be used in custom functions", {
  df <- data.table(x_start = c(1,1,1), end_x = c(2,2,2), z = c("a", "a", "b"))

  mutate_across_fn <- function(data, cols, val) {
    data %>%
      mutate(across({{ cols }}, ~ .x + val))
  }

  df <- df %>%
    mutate_across_fn(where(is.numeric), 1)

  expect_equal(df$x_start, c(2,2,2))
  expect_equal(df$end_x, c(3,3,3))
})

test_that("can refer to newly created columns", {
  test_df <- tidytable(x = rep(1, 3), y = rep(2, 3))

  out <- test_df %>%
    mutate(
      double_x = x * 2,
      across(c(x, double_x), as.character)
    )

  expect_equal(out$double_x, c("2", "2", "2"))
})

test_that("tidyselect with no cols works, #280", {
  df <- tidytable(x = 1:3, y = 1:3)

  across_df <- df %>%
    mutate(
      across(starts_with("a"), ~ .x + 1)
    )

  expect_equal(df, across_df)
})

test_that("works with rowSums, #346/352", {
  df <- tidytable(x = 1:3, y = 1:3, z = c("a", "a", "b"))

  across_df <- df %>%
    mutate(row_sum = rowSums(across(where(is.numeric))))
  expect_named(across_df, c("x", "y", "z", "row_sum"))
  expect_equal(across_df$row_sum, c(2, 4, 6))

  across_df2 <- df %>%
    mutate(rowSums(across(where(is.numeric))))

  expect_equal(across_df2[[4]], c(2, 4, 6))
})

test_that(".cols detects environment variables in custom functions, #389", {
  df <- tidytable(x = 1:3, y = 1:3, z = 1:3)

  make_character <- function(data, char_varlist){
    var_names <- char_varlist
    data %>%
      mutate(across(.cols = all_of(var_names), .fns = ~as.character(.)))
  }

  out <- df %>%
    make_character(char_varlist = c("x", "y"))

  expect_equal(out$x, c("1", "2", "3"))
  expect_equal(out$y, c("1", "2", "3"))
})

test_that("can namespace functions, #511", {
  df <- tidytable(x = 1:3, y = 1:3)

  res <- df %>%
    summarize(across(.fns = base::mean))

  expect_equal(res$x, 2)
  expect_equal(res$y, 2)
})

test_that("errors when used in not-first position when `.by` is used", {
  df <- tidytable(x = 1:3, y = c("a", "a", "b"))

  expect_error(
    df %>% mutate(new = 1, across(x, ~ .x), .by = y)
  )
  expect_error(
    df %>% mutate(new = 1, across.(x, ~ .x), .by = y)
  )
})

test_that("base anonymous functions can use n()/etc., #699", {
  df <- data.table(x = c(1,1,1), y = c(2,2,2), z = c("a", "a", "b"))
  res <- df %>%
    mutate(across(where(is.numeric), function(.x) .x + n()))

  expect_equal(res$x, c(4,4,4))
  expect_equal(res$y, c(5,5,5))
})

# summarize -----------------------------------------------------
test_that("single function works", {
  test_df <- tidytable(a = c(1:2, NA), b = 4:6, z = c("a", "a", "b"))

  result_df <- test_df %>%
    summarize(across(c(a, b), mean, na.rm = TRUE))

  expect_named(result_df, c("a", "b"))
  expect_equal(result_df$a, 1.5)
  expect_equal(result_df$b, 5)
})

test_that("n works", {
  test_df <- tidytable(a = 1:3, b = 4:6)

  result_df <- test_df %>%
    summarize(across(c(a, b), ~ n()))

  expect_named(result_df, c("a", "b"))
  expect_equal(result_df$a, 3)
  expect_equal(result_df$b, 3)
})

test_that("can use anonymous functions", {
  test_df <- tidytable(a = 1:3, b = 4:6, z = c("a", "a", "b"))

  result_df <- test_df %>%
    summarize(across(c(a, b), ~ mean(.x, na.rm = TRUE)))

  expect_named(result_df, c("a", "b"))
  expect_equal(result_df$a, 2)
  expect_equal(result_df$b, 5)
})

test_that("can use other columns", {
  test_df <- tidytable(a = 1:3, b = 1:3, z = c("a", "a", "b"))

  result_df <- test_df %>%
    summarize(across(c(a, b), ~ mean(.x)/mean(b)))

  expect_named(result_df, c("a", "b"))
  expect_equal(result_df$a, 1)
  expect_equal(result_df$b, 1)
})

test_that(".cols doesn't use .by columns", {
  test_df <- tidytable(a = 1:3, b = 4:6, z = c("a", "a", "b"))

  result_df <- test_df %>%
    summarize(across(everything(), mean, na.rm = TRUE), .by = z)

  expect_named(result_df, c("z", "a", "b"))
  expect_equal(result_df$a, c(1.5, 3))
  expect_equal(result_df$b, c(4.5, 6))
})

test_that("can pass list of named functions", {
  test_df <- tidytable(a = 1:3, b = 4:6, z = c("a", "a", "b"))

  result_df <- test_df %>%
    summarize(across(c(a, b), list(avg = mean, max = max)))

  expect_named(result_df, c("a_avg", "a_max", "b_avg", "b_max"))
  expect_equal(result_df$a_avg, 2)
  expect_equal(result_df$b_avg, 5)
  expect_equal(result_df$a_max, 3)
  expect_equal(result_df$b_max, 6)

  result_df <- test_df %>%
    summarize(across(c(a, b), list(avg = ~ mean(.x), max = ~ max(.x))))

  expect_named(result_df, c("a_avg", "a_max", "b_avg", "b_max"))
  expect_equal(result_df$a_avg, 2)
  expect_equal(result_df$b_avg, 5)
  expect_equal(result_df$a_max, 3)
  expect_equal(result_df$b_max, 6)
})

test_that("can pass list of functions with no names", {
  test_df <- tidytable(a = 1:3, b = 4:6, z = c("a", "a", "b"))

  result_df <- test_df %>%
    summarize(across(c(a, b), list(mean, max)))

  expect_named(result_df, c("a_1", "a_2", "b_1", "b_2"))
  expect_equal(result_df$a_1, 2)
  expect_equal(result_df$b_1, 5)
  expect_equal(result_df$a_2, 3)
  expect_equal(result_df$b_2, 6)
})

test_that("dots work with list of functions", {
  test_df <- tidytable(a = c(1:2, NA), b = 4:6, z = c("a", "a", "b"))

  result_df <- test_df %>%
    summarize(across(c(a, b), list(mean = mean, max = max), na.rm = TRUE))

  expect_named(result_df, c("a_mean", "a_max", "b_mean", "b_max"))
  expect_equal(result_df$a_mean, 1.5)
  expect_equal(result_df$b_mean, 5)
  expect_equal(result_df$a_max, 2)
  expect_equal(result_df$b_max, 6)
})

test_that("can pass list of functions with some names", {
  test_df <- tidytable(a = 1:3, b = 4:6, z = c("a", "a", "b"))

  result_df <- test_df %>%
    summarize(across(c(a, b), list(avg = mean, max)))

  expect_named(result_df, c("a_avg", "a_2", "b_avg", "b_2"))
  expect_equal(result_df$a_avg, 2)
  expect_equal(result_df$b_avg, 5)
  expect_equal(result_df$a_2, 3)
  expect_equal(result_df$b_2, 6)
})

test_that("can pass list of named functions with .by and .names", {
  test_df <- tidytable(a = 1:3, b = 4:6, z = c("a", "a", "b"))

  result_df <- test_df %>%
    summarize(
      across(c(a, b), list(avg = mean, max = max), .names = "{.fn}_{.col}"),
      .by = z
    )

  expect_named(result_df, c("z", "avg_a", "max_a", "avg_b", "max_b"))
  expect_equal(result_df$avg_a, c(1.5, 3))
  expect_equal(result_df$avg_b, c(4.5, 6))
  expect_equal(result_df$max_a, c(2, 3))
  expect_equal(result_df$max_b, c(5, 6))
})

# distinct -----------------------------------------------------
test_that("throws an error in distinct()", {
  test_df <- tidytable(a = 1:3, b = 4:6, z = c("a", "a", "b"))

  expect_error(distinct(test_df, across(everything())))
})

Try the tidytable package in your browser

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

tidytable documentation built on Oct. 5, 2023, 5:07 p.m.