How can I check whether a function call results in a warning?

Viewed 36736

In R, how can I determine whether a function call results in a warning?

That is, after calling the function I would like to know whether that instance of the call yielded a warning.

6 Answers

On the R-help mailing list (see http://tolstoy.newcastle.edu.au/R/help/04/06/0217.html), Luke Tierney wrote:

"If you want to write a function that computes a value and collects all warning you could do it like this:

withWarnings <- function(expr) {
    myWarnings <- NULL
    wHandler <- function(w) {
        myWarnings <<- c(myWarnings, list(w))
        invokeRestart("muffleWarning")
    }
    val <- withCallingHandlers(expr, warning = wHandler)
    list(value = val, warnings = myWarnings)
} 

2019 update

You can you use 'quietly' from the purrr package, which returns a list of output, result, warning and error. You can then extract each element by name. For instance, if you had a list, which you want to map a function over, and find the elements which returned a warning you could do

library(purrr)
library(lubridate)

datelist <- list(a = "12/12/2002", b = "12-12-2003", c = "24-03-2005")

# get all the everything
quiet_list <- map(datelist, quietly(mdy))

# find the elements which produced warnings
quiet_list %>% map("warnings") %>% keep(~ !is.null(.))

# or 
quiet_list %>% keep(~ length(.$warnings) != 0)

For this example it's quite trivial, but for a long list of dataframes where the NAs might be hard to spot, this is quite useful.

For a simple TRUE/FALSE return on whether a given operation results in a warning (or error), you could use the is.error function from the berryFunctions package, after first setting options(warn = 2) so that warnings are converted to errors.

E.g.,

options(warn = 2)
berryFunctions::is.error(as.numeric("x")) # TRUE
berryFunctions::is.error(as.numeric("3")) # FALSE

If you want to limit the option change to the use of this function, you could just create a new function as follows.

is.warningorerror <- function(x) {
 op <- options()
 on.exit(options(op))
 options(warn = 2)
 berryFunctions::is.error(x)
}

is.warningorerror(as.numeric("x")) # TRUE
options("warn") # still 0 (default)

I personally use the old good sink redirected into a text connection:

# create a new text connection directed into a variable called 'messages'
con <- textConnection("messages","w")
# sink all messages (i.e. warnings and errors) into that connection
sink(con,type = "message")
# a sample warning-generating function
test.fun <- function() {
    warning("Your warning.")
    return("Regular output.")
}
output <- test.fun()
# close the sink
sink(type="message")
# close the connection
close(con)
# if the word 'Warning' appears in messages than there has been a warning
warns <- paste(messages,collapse=" ")
if(grepl("Warning",warns)) {
    print(warns)
}
# [1] "Warning message: In test.fun() : Your warning."
print(output)
# [1] "Regular output."

Possibly more straightforward and cleaner than the other suggested solutions.

Related