使用列表列或嵌套的data.frame

时间:2019-05-20 10:06:45

标签: r equality testthat

小标题(来自tidyverse)可以包含列表列,这对于包含例如嵌套的数据框或传统上在data.frame中找不到的对象。

这里是一个例子:

library("dplyr")

nested_df <-
      iris %>%
      group_by(Species) %>%
      tidyr::nest() %>%
      mutate(model = purrr::map(data, lm, formula = Sepal.Length ~ .))

nested_df
#  # A tibble: 3 x 3
#   Species    data              model   
#   <fct>      <list>            <list>  
# 1 setosa     <tibble [50 × 4]> <S3: lm>
# 2 versicolor <tibble [50 × 4]> <S3: lm>
# 3 virginica  <tibble [50 × 4]> <S3: lm>

我正在使用testthat编写一些测试:如何测试这些data.frame之间的相等性?

testthat::expect_equal不起作用,因为all.equaldplyr::all_equal均失败:

all.equal(nested_df, nested_df)
# Error in equal_data_frame(target, current, ignore_col_order = ignore_col_order,  : 
#  Can't join on 'data' x 'data' because of incompatible types (list / list)

我考虑过使用testthat::expect_true(identical(...)),但这通常太严格了。例如,定义完全相同的nested_df2不足以传递identical,因为嵌入在.Environment模型中的terms的{​​{1}}属性{模型相等,并通过lm

all.equal

如何使用identical(nested_df, nested_df2) # [1] FALSE identical(nested_df$model, nested_df2$model, ignore.environment = TRUE) # [1] FALSE all.equal(nested_df$model, nested_df2$model, tolerance = 0) # [1] TRUE 之类的列表列来检验小词的相等性?

2 个答案:

答案 0 :(得分:1)

Kinda直率的方法,但似乎可以在您的示例中使用:

all.equal.list(nested_df, nested_df)

# [1] TRUE

all.equal.list(nested_df, mutate(nested_df, Species = sample(Species)))

# [1] "Component “Species”: 2 string mismatches"

答案 1 :(得分:1)

要扩展@utubun的答案,您可以将all.equal.list包装在类似于testthat的expect_*函数中:

expect_equal_tbl <- function(object, expected, ..., info = NULL) {
  act <- testthat::quasi_label(rlang::enquo(object), arg = "object")
  exp <- testthat::quasi_label(rlang::enquo(expected), arg = "expected")

  # all.equal.list is slightly problematic: it returns TRUE for match, and
  # returns a character vector when differences are observed. We extract
  # both a match-indicator and a failure message

  diffs <- all.equal.list(object, expected, ...)
  has_diff <- if (is.logical(diffs)) diffs else FALSE
  diff_msg <- paste(diffs, collapse = "\n")

  testthat::expect(
    has_diff,
    failure_message = sprintf(
      "%s not equal to %s.\n%s", act$lab, exp$lab, diff_msg
    ),
    info = info
  )

  invisible(act$val)
}
expect_equal_tbl(nested_df, nested_df, info = "YAY!")
expect_equal_tbl(nested_df, nested_df[1, ], info = "FAIL!")
 Error: `nested_df` not equal to nested_df[1, ].
Attributes: < Component “row.names”: Numeric: lengths (3, 1) differ >
Component “Species”: Lengths: 3, 1
Component “Species”: Lengths (3, 1) differ (string compare on first 1)
Component “data”: Length mismatch: comparison on first 1 components
Component “model”: Length mismatch: comparison on first 1 components
FAIL!