/assert

Lightweight validation tool for checking function arguments and data analysis scripts.

Primary LanguageR

✅ assert

CRAN_Status_Badge Build Status R-CMD-check

Lightweight validation tool for checking function arguments and data analysis scripts. This is an alternative to stopifnot() from the ‘base’ package and to assert_that() from the ‘assertthat’ package. It provides more informative error messages and facilitates debugging.

Installation

You can install the released version of assert from CRAN with:

install.packages("assert")

And the development version from GitHub with:

# install.packages("devtools")
devtools::install_github("OlivierBinette/assert")

Examples

Assertions throughout a data analysis workflow:

library(assert)
attach(ChickWeight)

# Passing assertions
assert(is.numeric(weight),
       all(weight > 0))
# Failing assertions
assert(all(Diet > 0),
       is.numeric(Times))
#> Error in assert(all(Diet > 0), is.numeric(Times)) : 
#> Failed checks: 
#>      all(Diet > 0)   (NA)
#>      is.numeric(Times)   (object 'Times' not found)

Validate function arguments:

# Sample from a multivariate normal distribution
rmultinorm <- function(k, mu, sigma) {
  assert(is.numeric(k),
         length(k) == 1,
         k > 0,
         msg = "Number of samples `k` should be a positive integer")
  assert(is.numeric(mu),
         is.matrix(sigma),
         all(length(mu) == dim(sigma)),
         msg = "Mean vector should match the covariance matrix dimensions.")

  p <- length(mu)
  t(chol(sigma)) %*% matrix(rnorm(p*k, 0, 1), nrow=p) + mu
}

mu <- c(0,10)
sigma <- matrix(c(2,1,1,2), nrow=2)
rmultinorm(3, mu, sigma)
#>           [,1]      [,2]       [,3]
#> [1,] -2.246757 -1.424890 -0.9528532
#> [2,]  7.941552  9.283195 10.4848747
rmultinorm(mu, 3, sigma)
#> Error: in rmultinorm(k = mu, mu = 3, sigma = sigma)
#> Failed checks: 
#>      length(k) == 1
#>      k > 0   (c(FALSE, TRUE))
#>
#> Number of samples `k` should be a positive integer 

Philosophy

Function argument checks should throw errors as early as possible and at the function level. When assert is used within a function, all assertions are executed within tryCatch statements, error messages are recovered, and a single error is thrown from assert. This ensures that “object not found” errors and assertion execution errors are also caught as part of argument checks. The function signature and call are also included as part of error messages to facilitate debugging.

Performance

Because assert executes each assertion inside of a tryCatch() statement and recovers error messages, it is not quite as efficient as stopifnot (which sequentially executes assertions without catching potential errors). assertthat::assert_that has the most overhead.

library(assertthat)

bench::mark(assert(TRUE),
            assert_that(TRUE),
            stopifnot(TRUE),
            check=FALSE)
#> # A tibble: 3 x 6
#>   expression             min   median `itr/sec` mem_alloc `gc/sec`
#>   <bch:expr>        <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#> 1 assert(TRUE)       14.88µs   17.5µs    55463.        0B    55.5 
#> 2 assert_that(TRUE)  29.05µs     32µs    29553.    26.9KB     8.87
#> 3 stopifnot(TRUE)     2.72µs      3µs   269977.        0B     0