Matt Bannert
Matt Bannert

Reputation: 28264

Better error message for stopifnot?

I am using stopifnot and I understand it just returns the first value that was not TRUE. I f that is some freaky dynamic expression someone who is not into the custom function cannot really make something out of that. So I would love to add a custom error message. Any suggestions?

Error: length(unique(nchar(check))) == 1 is not TRUE

Basically states that the elements of the vector check do not have the same length. Is there a way of saying: Error: Elements of your input vector do not have the same length!?

Upvotes: 45

Views: 26356

Answers (8)

kin-b
kin-b

Reputation: 21

Try this:

same_length <- FALSE
stopifnot("Elements of your input vector do not have the same length!" = same_length)
#> Error : Elements of your input vector do not have the same length!

Upvotes: 2

Brian Montgomery
Brian Montgomery

Reputation: 2414

A custom message can be added as a label to your expression:

stopifnot("Elements of your input vector do not have the same length!" =
  length(unique(nchar(check))) == 1)

# Error: Elements of your input vector do not have the same length!

Upvotes: 41

tomas
tomas

Reputation: 395

What about embedding the stopifnot into tryCatch and then recasting the exception with stop using customized message?

Something like:

tryCatch(stopifnot(...,error=stop("Your customized error message"))

Unlike some other solutions this does not require additional packages. Compared to using if statement combined with stop you retain the performance advantages of stopifnot, when you use new R versions. Since R version 3.5.0 stopifnot evaluates expressions sequentially and stops on first failure.

Upvotes: 8

chao
chao

Reputation: 2024

The answers already provided are quite good, and mine is just an addition to that collection. For some people it could be more convenient to use one-liner in form of the following function:

stopifnotinform <- function(..., message = "err") {
  args <- list(...)
  if (length(args) == 0) {
    return(NULL)
  }
  for (i in 1:length(args)) {
    result <- args[[i]]
    if (is.atomic(result) && result == FALSE) {
      stop(message)
    }
  }
}

# throws an error
stopifnotinform(is.integer(1L), is.integer(2), message = "Some number(s) provided is not an integer")
# continues with execution
stopifnotinform(is.integer(1L), is.integer(2L), message = "Some number(s) provided is not an integer")

Bear in mind that this solution provides you with only one (common) error message for all parameters in ....

Upvotes: 1

Richie Cotton
Richie Cotton

Reputation: 121077

The assertive and assertthat packages have more readable check functions.

library(assertthat)
assert_that(length(unique(nchar(check))) == 1)
## Error: length(unique(nchar(check))) == 1 are not all true.

library(assertive)
assert_is_scalar(unique(nchar(check)))
## Error: unique(nchar(check)) does not have length one.

if(!is_scalar(unique(nchar(check)))) 
{
  stop("Elements of check have different numbers of characters.")
}
## Error: Elements of check have different numbers of characters.

Upvotes: 17

Xu Wang
Xu Wang

Reputation: 10597

I would recommend you check out Hadley's testthat package. It allows for intuitive testing: the names of the functions are great and the way you write them is like a sentence -- "I expect that length(unique(nchar(check))) is [exactly|approximately] 1". The errors produced are informative.

See here: http://journal.r-project.org/archive/2011-1/RJournal_2011-1_Wickham.pdf

In your case,

> library(testthat)
> check = c("x", "xx", "xxx")
> expect_that(length(unique(nchar(check))), equals(1))
Error: length(unique(nchar(check))) not equal to 1
Mean relative difference: 2

Also note that you don't have the problem that @Andrie referenced with sometimes having to think about double negatives with stopifnot. I know it seems simple, but it caused me many headaches!

Upvotes: 1

Max Gasner
Max Gasner

Reputation: 1256

Or you could package it up.

assert <- function (expr, error) {
  if (! expr) stop(error, call. = FALSE)
}

So you have:

> check = c("x", "xx", "xxx")
> assert(length(unique(nchar(check))) == 1, "Elements of your input vector do not have the same length!")

Error: Elements of your input vector do not have the same length!

Upvotes: 7

Andrie
Andrie

Reputation: 179428

Use stop and an if statement:

if(length(unique(nchar(check))) != 1) 
  stop("Error: Elements of your input vector do not have the same length!")

Just remember that stopifnot has the convenience of stating the negative, so your condition in the if needs to be the negation of your stop condition.


This is what the error message looks like:

> check = c("x", "xx", "xxx")
> if(length(unique(nchar(check))) != 1) 
+   stop("Error: Elements of your input vector do not have the same length!")

Error in eval(expr, envir, enclos) : 
  Error: Elements of your input vector do not have the same length!

Upvotes: 56

Related Questions