tests/testthat/test-unnest.R

test_that("can keep empty rows", {
  df <- tibble(x = 1:3, y = list(NULL, tibble(), tibble(a = 1)))
  out1 <- df %>% unnest(y)
  expect_equal(nrow(out1), 1)

  out2 <- df %>% unnest(y, keep_empty = TRUE)
  expect_equal(nrow(out2), 3)
  expect_equal(out2$a, c(NA, NA, 1))
})

test_that("empty rows still affect output type", {
  df <- tibble(
    x = 1:2,
    data = list(
      tibble(y = character(0)),
      tibble(z = integer(0))
    )
  )
  out <- unnest(df, data)
  expect_equal(out, tibble(x = integer(), y = character(), z = integer()))
})

test_that("bad inputs generate errors", {
  df <- tibble(x = 1, y = list(mean))
  expect_snapshot((expect_error(unnest(df, y))))
})

test_that("unesting combines augmented vectors", {
  df <- tibble(x = as.list(as.factor(letters[1:3])))
  expect_equal(unnest(df, x)$x, factor(letters[1:3]))
})

test_that("vector unnest preserves names", {
  df <- tibble(x = list(1, 2:3), y = list("a", c("b", "c")))
  out <- unnest(df, x)
  expect_named(out, c("x", "y"))
})

test_that("rows and cols of nested-dfs are expanded", {
  df <- tibble(x = 1:2, y = list(tibble(a = 1), tibble(b = 1:2)))
  out <- df %>% unnest(y)

  expect_named(out, c("x", "a", "b"))
  expect_equal(nrow(out), 3)
})

test_that("can unnest nested lists", {
  df <- tibble(
    x = 1:2,
    y = list(list("a"), list("b"))
  )
  rs <- unnest(df, y)
  expect_identical(rs, tibble(x = 1:2, y = list("a", "b")))
})

test_that("can unnest mixture of name and unnamed lists of same length", {
  df <- tibble(
    x = c("a"),
    y = list(y = 1:2),
    z = list(1:2)
  )
  expect_identical(
    unnest(df, c(y, z)),
    tibble(x = c("a", "a"), y = c(1:2), z = c(1:2))
  )
})

test_that("can unnest list_of", {
  df <- tibble(
    x = 1:2,
    y = vctrs::list_of(1:3, 4:9)
  )
  expect_equal(
    unnest(df, y),
    tibble(x = rep(1:2, c(3, 6)), y = 1:9)
  )
})

test_that("can combine NULL with vectors or data frames", {
  df1 <- tibble(x = 1:2, y = list(NULL, tibble(z = 1)))
  out <- unnest(df1, y)
  expect_named(out, c("x", "z"))
  expect_equal(out$z, 1)

  df2 <- tibble(x = 1:2, y = list(NULL, 1))
  out <- unnest(df2, y)
  expect_named(out, c("x", "y"))
  expect_equal(out$y, 1)
})

test_that("vectors become columns", {
  df <- tibble(x = 1:2, y = list(1, 1:2))
  out <- unnest(df, y)
  expect_equal(out$y, c(1L, 1:2))
})

test_that("multiple columns must be same length", {
  df <- tibble(x = list(1:2), y = list(1:3))
  expect_snapshot((expect_error(unnest(df, c(x, y)))))

  df <- tibble(x = list(1:2), y = list(tibble(y = 1:3)))
  expect_snapshot((expect_error(unnest(df, c(x, y)))))
})

test_that("can use non-syntactic names", {
  out <- tibble("foo bar" = list(1:2, 3)) %>% unnest(`foo bar`)
  expect_named(out, "foo bar")
})

test_that("unpacks df-cols (#1112)", {
  df <- tibble(x = 1, y = tibble(a = 1, b = 2))
  expect_identical(unnest(df, y), tibble(x = 1, a = 1, b = 2))
})

test_that("unnesting column of mixed vector / data frame input is an error", {
  df <- tibble(x = list(1, tibble(a = 1)))
  expect_snapshot((expect_error(unnest(df, x))))
})

test_that("unnest() advises on outer / inner name duplication", {
  df <- tibble(x = 1, y = list(tibble(x = 2)))

  expect_snapshot(error = TRUE, {
    unnest(df, y)
  })
})

test_that("unnest() advises on inner / inner name duplication", {
  df <- tibble(
    x = list(tibble(a = 1)),
    y = list(tibble(a = 2))
  )

  expect_snapshot(error = TRUE, {
    unnest(df, c(x, y))
  })
})

test_that("unnest() disallows renaming", {
  df <- tibble(x = list(tibble(a = 1)))

  expect_snapshot(error = TRUE, {
    unnest(df, c(y = x))
  })
})

test_that("unnest() works on foreign list types recognized by `vec_is_list()` (#1327)", {
  new_foo <- function(...) {
    structure(list(...), class = c("foo", "list"))
  }

  df <- tibble(x = new_foo(tibble(a = 1L), tibble(a = 2:3)))
  expect_identical(unnest(df, x), tibble(a = 1:3))

  # With empty list
  df <- tibble(x = new_foo())
  expect_identical(unnest(df, x), tibble(x = unspecified()))

  # With empty types
  df <- tibble(x = new_foo(tibble(a = 1L), tibble(a = integer())))
  expect_identical(unnest(df, x), tibble(a = 1L))
  expect_identical(unnest(df, x, keep_empty = TRUE), tibble(a = c(1L, NA)))

  # With `NULL`s
  df <- tibble(x = new_foo(tibble(a = 1L), NULL))
  expect_identical(unnest(df, x), tibble(a = 1L))
  expect_identical(unnest(df, x, keep_empty = TRUE), tibble(a = c(1L, NA)))
})

# other methods -----------------------------------------------------------------

test_that("rowwise_df becomes grouped_df", {
  skip_if_not_installed("dplyr", "0.8.99")

  df <- tibble(g = 1, x = list(1:3)) %>% dplyr::rowwise(g)
  rs <- df %>% unnest(x)

  expect_s3_class(rs, "grouped_df")
  expect_equal(dplyr::group_vars(rs), "g")
})

test_that("grouping is preserved", {
  df <- tibble(g = 1, x = list(1:3)) %>% dplyr::group_by(g)
  rs <- df %>% unnest(x)

  expect_s3_class(rs, "grouped_df")
  expect_equal(dplyr::group_vars(rs), "g")
})

# Empty inputs ------------------------------------------------------------

test_that("can unnest empty data frame", {
  df <- tibble(x = integer(), y = list())
  out <- unnest(df, y)
  expect_equal(out, tibble(x = integer(), y = unspecified()))
})

test_that("unnesting bare lists of NULLs is equivalent to unnesting empty lists", {
  df <- tibble(x = 1L, y = list(NULL))
  out <- unnest(df, y)
  expect_identical(out, tibble(x = integer(), y = unspecified()))
})

test_that("unnest() preserves ptype", {
  tbl <- tibble(x = integer(), y = list_of(ptype = tibble(a = integer())))
  res <- unnest(tbl, y)
  expect_equal(res, tibble(x = integer(), a = integer()))
})

test_that("unnesting typed lists of NULLs retains ptype", {
  df <- tibble(x = 1L, y = list_of(NULL, .ptype = tibble(a = integer())))
  out <- unnest(df, y)
  expect_identical(out, tibble(x = integer(), a = integer()))
})

test_that("ptype can be overriden manually (#1158)", {
  df <- tibble(
    a = list("a", c("b", "c")),
    b = list(1, c(2, 3)),
  )

  ptype <- list(b = integer())

  out <- unnest(df, c(a, b), ptype = ptype)

  expect_type(out$b, "integer")
  expect_identical(out$b, c(1L, 2L, 3L))
})

test_that("ptype works with nested data frames", {
  df <- tibble(
    a = list("a", "b"),
    b = list(tibble(x = 1, y = 2L), tibble(x = 2, y = 3L)),
  )

  # x: double -> integer
  ptype <- list(b = tibble(x = integer(), y = integer()))

  out <- unnest(df, c(a, b), ptype = ptype)

  expect_identical(out$x, c(1L, 2L))
  expect_identical(out$y, c(2L, 3L))
})

test_that("skips over vector columns", {
  df <- tibble(x = integer(), y = list())
  expect_identical(unnest(df, x), df)
})

test_that("unnest keeps list cols", {
  df <- tibble(x = 1:2, y = list(3, 4), z = list(5, 6:7))
  out <- df %>% unnest(y)

  expect_equal(names(out), c("x", "y", "z"))
})

# Deprecated behaviours ---------------------------------------------------

test_that("cols must go in cols", {
  df <- tibble(x = list(3, 4), y = list("a", "b"))
  expect_snapshot(unnest(df, x, y))
})

test_that("need supply column names", {
  df <- tibble(x = 1:2, y = list("a", "b"))
  expect_snapshot(unnest(df))
})

test_that("sep combines column names", {
  local_options(lifecycle_verbosity = "warning")
  df <- tibble(x = list(tibble(x = 1)), y = list(tibble(x = 1)))
  expect_snapshot(out <- df %>% unnest(c(x, y), .sep = "_"))
  expect_named(out, c("x_x", "y_x"))
})

test_that("unnest has mutate semantics", {
  df <- tibble(x = 1:3, y = list(1, 2:3, 4))
  expect_snapshot(out <- df %>% unnest(z = map(y, `+`, 1)))
  expect_equal(out$z, 2:5)
})

test_that(".drop and .preserve are deprecated", {
  local_options(lifecycle_verbosity = "warning")

  df <- tibble(x = list(3, 4), y = list("a", "b"))
  expect_snapshot(df %>% unnest(x, .preserve = y))

  df <- tibble(x = list(3, 4), y = list("a", "b"))
  expect_snapshot(df %>% unnest(x, .drop = FALSE))
})

test_that(".id creates vector of names for vector unnest", {
  local_options(lifecycle_verbosity = "warning")

  df <- tibble(x = 1:2, y = list(a = 1, b = 1:2))
  expect_snapshot(out <- unnest(df, y, .id = "name"))

  expect_equal(out$name, c("a", "b", "b"))
})
tidyverse/tidyr documentation built on Jan. 28, 2024, 12:10 a.m.