Skip to content

Commit cdad777

Browse files
authored
Merge pull request #617 from yjunechoe/col_vals_expr-na_pass
Add `na_pass` argument to `col_vals_expr()`
2 parents 939ede6 + bb004ba commit cdad777

File tree

7 files changed

+169
-17
lines changed

7 files changed

+169
-17
lines changed

NEWS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# pointblank (development version)
22

3+
- Add `na_pass` to `col_vals_expr()` for finer control of `NA` values. Previously, `NA`s were ignored, but now they are caught as failures with the default `na_pass = FALSE`. As a safeguard, if an expression generates `NA` values while `na_pass` is not explicitly supplied, a warning is thrown. (#617)
4+
35
- Bugfix agents auto-generating a table label that was too long. They now get truncated (#614)
46

57
- Bugfix agents not searching the formula environment when materializing `~ tbl` (#599)

R/col_vals_expr.R

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,13 @@
6868
#' formally tested (so be mindful of this when using unsupported backends with
6969
#' **pointblank**).
7070
#'
71+
#' @section Missing Values:
72+
#'
73+
#' This validation function supports special handling of `NA` values. The
74+
#' `na_pass` argument will determine whether an `NA` value appearing in a test
75+
#' unit should be counted as a *pass* or a *fail*. The default of `na_pass =
76+
#' FALSE` means that any `NA`s encountered will accumulate failing test units.
77+
#'
7178
#' @section Preconditions:
7279
#'
7380
#' Providing expressions as `preconditions` means **pointblank** will preprocess
@@ -313,6 +320,7 @@ NULL
313320
col_vals_expr <- function(
314321
x,
315322
expr,
323+
na_pass = FALSE,
316324
preconditions = NULL,
317325
segments = NULL,
318326
actions = NULL,
@@ -322,6 +330,11 @@ col_vals_expr <- function(
322330
active = TRUE
323331
) {
324332

333+
# Mark if unspecified and resolve the default behavior at interrogate
334+
if (missing(na_pass)) {
335+
na_pass <- NA
336+
}
337+
325338
if (!inherits(expr, "call")) {
326339

327340
if (rlang::is_formula(expr)) {
@@ -353,6 +366,7 @@ col_vals_expr <- function(
353366
create_agent(x, label = "::QUIET::") %>%
354367
col_vals_expr(
355368
expr = expr,
369+
na_pass = na_pass,
356370
preconditions = preconditions,
357371
segments = segments,
358372
label = label,
@@ -400,6 +414,7 @@ col_vals_expr <- function(
400414
columns_expr = NA_character_,
401415
column = NA_character_,
402416
values = expr,
417+
na_pass = na_pass,
403418
preconditions = preconditions,
404419
seg_expr = segments,
405420
seg_col = seg_col,
@@ -421,16 +436,23 @@ col_vals_expr <- function(
421436
expect_col_vals_expr <- function(
422437
object,
423438
expr,
439+
na_pass = FALSE,
424440
preconditions = NULL,
425441
threshold = 1
426442
) {
427443

444+
# Mark if unspecified and resolve the default behavior at interrogate
445+
if (missing(na_pass)) {
446+
na_pass <- NA
447+
}
448+
428449
fn_name <- "expect_col_vals_expr"
429450

430451
vs <-
431452
create_agent(tbl = object, label = "::QUIET::") %>%
432453
col_vals_expr(
433454
expr = {{ expr }},
455+
na_pass = na_pass,
434456
preconditions = {{ preconditions }},
435457
actions = action_levels(notify_at = threshold)
436458
) %>%
@@ -476,14 +498,21 @@ expect_col_vals_expr <- function(
476498
test_col_vals_expr <- function(
477499
object,
478500
expr,
501+
na_pass = FALSE,
479502
preconditions = NULL,
480503
threshold = 1
481504
) {
482505

506+
# Mark if unspecified and resolve the default behavior at interrogate
507+
if (missing(na_pass)) {
508+
na_pass <- NA
509+
}
510+
483511
vs <-
484512
create_agent(tbl = object, label = "::QUIET::") %>%
485513
col_vals_expr(
486514
expr = {{ expr }},
515+
na_pass = na_pass,
487516
preconditions = {{ preconditions }},
488517
actions = action_levels(notify_at = threshold)
489518
) %>%

R/interrogate.R

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2100,24 +2100,56 @@ interrogate_expr <- function(
21002100
# Get the expression
21012101
expr <- get_values_at_idx(agent = agent, idx = idx)
21022102

2103+
# Determine whether NAs should be allowed
2104+
na_pass <- get_column_na_pass_at_idx(agent = agent, idx = idx)
2105+
21032106
# Create function for validating the `col_vals_expr()` step function
21042107
tbl_val_expr <- function(
21052108
table,
2106-
expr
2109+
expr,
2110+
na_pass
21072111
) {
21082112

21092113
# Ensure that the input `table` is actually a table object
21102114
tbl_validity_check(table = table)
21112115

21122116
expr <- expr[[1]]
21132117

2114-
table %>%
2115-
dplyr::mutate(pb_is_good_ = !!expr) %>%
2116-
dplyr::filter(!is.na(pb_is_good_))
2118+
tbl <- table %>%
2119+
dplyr::mutate(pb_is_good_ = !!expr)
2120+
2121+
if (anyNA(tbl$pb_is_good_)) {
2122+
2123+
# Throw warning if `expr` results in `NA` values but `na_pass` was unset
2124+
if (is.na(na_pass)) {
2125+
warn(
2126+
paste(
2127+
"Expression generated `NA` value(s).",
2128+
"Edit the `expr` or specify `na_pass` (default is `FALSE`)."
2129+
),
2130+
# "simpleWarning" lets it trickle up to the test/expect functions
2131+
class = "simpleWarning"
2132+
)
2133+
# Re-apply user-facing default of `na_pass = FALSE`
2134+
na_pass <- FALSE
2135+
}
2136+
2137+
tbl$pb_is_good_[is.na(tbl$pb_is_good_)] <- na_pass
2138+
2139+
}
2140+
2141+
tbl
2142+
21172143
}
21182144

21192145
# Perform rowwise validations for the column
2120-
pointblank_try_catch(tbl_val_expr(table = table, expr = expr))
2146+
pointblank_try_catch(
2147+
tbl_val_expr(
2148+
table = table,
2149+
expr = expr,
2150+
na_pass = na_pass
2151+
)
2152+
)
21212153
}
21222154

21232155
interrogate_specially <- function(

man/col_vals_expr.Rd

Lines changed: 32 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/test-expectation_fns.R

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -693,17 +693,17 @@ test_that("pointblank expectation function produce the correct results", {
693693
#
694694

695695
expect_col_vals_expr(tbl, ~ a %% 1 == 0)
696-
expect_col_vals_expr(tbl, ~ c %% 1 == 0)
696+
expect_col_vals_expr(tbl, ~ c %% 1 == 0, na_pass = TRUE)
697697
expect_col_vals_expr(tbl, expr(a %% 1 == 0))
698-
expect_col_vals_expr(tbl, expr(c %% 1 == 0))
698+
expect_col_vals_expr(tbl, expr(c %% 1 == 0), na_pass = TRUE)
699699
expect_col_vals_expr(tbl, ~ case_when(
700700
b == 1 ~ a > 5 & c >= 1
701-
))
701+
), na_pass = TRUE)
702702
expect_col_vals_expr(tbl, expr(
703703
case_when(
704704
b == 1 ~ a > 5 & c >= 1
705705
)
706-
))
706+
), na_pass = TRUE)
707707

708708
expect_success(expect_col_vals_expr(tbl, ~ a %% 1 == 0))
709709
expect_success(expect_col_vals_expr(tbl, ~ a %>% between(0, 10)))

tests/testthat/test-interrogate_with_agent.R

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2016,3 +2016,64 @@ test_that("Select validation steps can be `active` or not", {
20162016
)
20172017
)
20182018
})
2019+
2020+
test_that("col_vals_expr(na_pass) works as expected (#616)", {
2021+
2022+
agent <- small_table %>%
2023+
create_agent() %>%
2024+
col_vals_equal(c, 3) %>%
2025+
col_vals_expr(expr(c == 3), na_pass = FALSE) %>%
2026+
interrogate()
2027+
2028+
expect_identical(
2029+
agent$validation_set$tbl_checked[[1]][[1]],
2030+
agent$validation_set$tbl_checked[[2]][[1]]
2031+
)
2032+
2033+
agent <- small_table %>%
2034+
create_agent() %>%
2035+
col_vals_equal(c, 3, na_pass = TRUE) %>%
2036+
col_vals_expr(expr(c == 3), na_pass = TRUE) %>%
2037+
interrogate()
2038+
2039+
expect_identical(
2040+
agent$validation_set$tbl_checked[[1]][[1]],
2041+
agent$validation_set$tbl_checked[[2]][[1]]
2042+
)
2043+
2044+
})
2045+
2046+
test_that("col_vals_expr(na_pass) NA value handling (#617)", {
2047+
2048+
agent1 <- small_table %>%
2049+
create_agent() %>%
2050+
col_vals_expr(expr(c == 3)) %>%
2051+
interrogate()
2052+
expect_true(agent1$validation_set$eval_warning)
2053+
2054+
agent2 <- small_table %>%
2055+
create_agent() %>%
2056+
col_vals_expr(expr(c == 3), na_pass = FALSE) %>%
2057+
interrogate()
2058+
expect_false(agent2$validation_set$eval_warning)
2059+
expect_identical(
2060+
agent1$validation_set$tbl_checked,
2061+
agent2$validation_set$tbl_checked
2062+
)
2063+
2064+
# Warnings trickle up for test/expect functions
2065+
tbl <- data.frame(x = c(NA, 1))
2066+
expect_warning(
2067+
expect_false(
2068+
tbl %>%
2069+
test_col_vals_expr(expr(x == 1))
2070+
)
2071+
)
2072+
expect_warning(
2073+
expect_error(
2074+
tbl %>%
2075+
expect_col_vals_expr(expr(x == 1))
2076+
)
2077+
)
2078+
2079+
})

tests/testthat/test-test_fns.R

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -275,21 +275,21 @@ test_that("pointblank expectation functions produce the correct results", {
275275
#
276276

277277
expect_true(test_col_vals_expr(tbl, ~ a %% 1 == 0))
278-
expect_true(test_col_vals_expr(tbl, ~ c %% 1 == 0))
278+
expect_true(test_col_vals_expr(tbl, ~ c %% 1 == 0, na_pass = TRUE))
279279
expect_true(test_col_vals_expr(tbl, expr(a %% 1 == 0)))
280-
expect_true(test_col_vals_expr(tbl, expr(c %% 1 == 0)))
280+
expect_true(test_col_vals_expr(tbl, expr(c %% 1 == 0), na_pass = TRUE))
281281

282282
expect_true(
283283
test_col_vals_expr(tbl, ~ case_when(
284284
b == 1 ~ a > 5 & c >= 1
285-
))
285+
), na_pass = TRUE)
286286
)
287287
expect_true(
288288
test_col_vals_expr(tbl, expr(
289289
case_when(
290290
b == 1 ~ a > 5 & c >= 1
291291
)
292-
))
292+
), na_pass = TRUE)
293293
)
294294

295295
expect_true(test_col_vals_expr(tbl, ~ a < 0, threshold = 1000))

0 commit comments

Comments
 (0)