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.
If you want to use the try constructs, you can set the options for warn. See also ?options. Better is to use tryCatch() :
x <- function(i){
if (i < 10) warning("A warning")
i
}
tt <- tryCatch(x(5),error=function(e) e, warning=function(w) w)
tt2 <- tryCatch(x(15),error=function(e) e, warning=function(w) w)
tt
## <simpleWarning in x(5): A warning>
tt2
## [1] 15
if(is(tt,"warning")) print("KOOKOO")
## [1] "KOOKOO"
if(is(tt2,"warning")) print("KOOKOO")
To get both the result and the warning :
tryCatch(x(5),warning=function(w) return(list(x(5),w)))
## [[1]]
## [1] 5
##
## [[2]]
## <simpleWarning in x(5): A warning>
Using try
op <- options(warn=2)
tt <- try(x())
ifelse(is(tt,"try-error"),"There was a warning or an error","OK")
options(op)
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.
here is an example:
testit <- function() warning("testit") # function that generates warning.
assign("last.warning", NULL, envir = baseenv()) # clear the previous warning
testit() # run it
if(length(warnings())>0){ # or !is.null(warnings())
print("something happened")
}
maybe this is somehow indirect, but i don't know the more straightforward way.
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
It's complicated to explain my use case but I am working on a project that requires parsing text that may throw some errors. I would like to use tryCatch() so that as much of the script can run as possible and alert the user that some code failed. I can use a loop for this but I would like to know why this behaviour exists and if there is an apply function that does do the trick.
When I run the loop or use do.call() on this parsed object, I get just the expected single error mesage. When I use lapply() I get the same error message followed by the ouput of the assignments. I've tried throwing suppress functions around lapply() which, perhaps obviously, did not work. and I get similar output for sapply() and map(). Curious if someone can explain it to me.
test_text <- parse(text = "x <- pi; y <- x; z <- stop()")
eval_try <- function(x) {
tryCatch(
eval(x, envir = .GlobalEnv),
error = function(cond) message("there was an error"),
warning = function(cond) message("there was a warning")
)
}
for (i in seq_along(test_text)) {
eval_try(test_text[i])
}
#> there was an error
do.call("eval_try", list(test_text))
#> there was an error
lapply(test_text, eval_try)
# there was an error
# [[1]]
# [1] 3.141593
#
# [[2]]
# [1] 3.141593
#
# [[3]]
# NULL
The printing you see is the output of the lapply function. You can suppress it by assigning the result to a variable or if you really don't care about storing the output, use the below trick with invisible.
> myfun <- function(x) x
>
> lapply(1:3, FUN = myfun)
[[1]]
[1] 1
[[2]]
[1] 2
[[3]]
[1] 3
> a <- lapply(1:3, FUN = myfun)
> invisible(lapply(1:3, FUN = myfun))
The following function gives all the structures of an object in R. This function (plus dput()/str() for object attributes) fully qualifies everything about an arbitrary R object.
ObjectStructure <- function(x, ShowAll=FALSE) {
op <- options("warn")
options(warn=-1) # Assign "-1" to warn option to temporarily close warnings
on.exit(options(op)) # Reset the settings to initial ones after exit from the ObjectStructure function
is.Functions <- grep(methods(is), pattern="<-", invert=TRUE, value=TRUE) # 55 (is.X) functions
isDotlessFunctions <- character()
packs <- c('base', 'utils', 'methods') # include more packages if needed
for (pkg in packs) {
library(pkg, character.only = TRUE)
objects <- grep("^is.+\\w$", ls(envir=as.environment(paste('package', pkg, sep=':'))), value=TRUE)
objects <- grep("<-", objects, invert=TRUE, value=TRUE)
if (length(objects) > 0)
isDotlessFunctions <- append(isDotlessFunctions, objects[sapply(objects, function(x) class(eval(parse(text=x))) == "function")])
}
FunctionsList <- union(is.Functions, isDotlessFunctions)
result <- data.frame(test=character(), value=character(), warn=character(), stringsAsFactors=FALSE)
# Loop all the "is.(...)" functions and save the results
for(islev in FunctionsList) {
res <- try(eval(call(islev,x)),silent=TRUE) # In cases of error, let error be processed
# in errored cases, try produces try-error object that contains error message
if(class(res)=="try-error") { next() # In case of error, ignore current iteration and pass to the next iteration in the loop
} else if (length(res)>1) {
warn <- "*Applies only to the first element of the provided object"
value <- paste(res,"*",sep="")
} else {
warn <- ""
value <- res
}
result[nrow(result)+1,] <- list(islev, value, warn)
}
result <- result[order(result$value, decreasing=TRUE),] # Order the results
rownames(result) <- NULL # to arrange row numbers in a way that they start from 1 and ordered
if(ShowAll) return(result) # Show only the structures that give TRUE
else return(result[which(result$value=="TRUE"),]) # All the function results that give a TRUE/FALSE value
}
ObjectStructure(1L, TRUE) # See how the function works
As the user #dominic-comtois emphasize here, more packages can be included in packs variable. So,
What are the packages that include internal is.X (is.vector, is.numeric, etc.) and all isX (isS4, isOpen etc.) functions in general?
Based on this solution, I have formulated the below code to perform chisq.test for 33 variables.
sapply(Indices,function(i){
chisq.test(data.cleaned[,i],data.cleaned$Out)$p.value })
This code produces 9 warnings, hopefully due to the violation of the assumptions made for chisq.test. I would like to identify for which instances of i the warnings are issued ?
I assume that there is no need of a reproducible example for this simple question from a beginner.
I generate this example to reproduce the problem:
df <- data.frame(x=rep(c("a","b"), 22))
options(warn=1)
sapply(1:nrow(df), function(i){
df[i,"x"] <- letters[round(rnorm(1,mean=2, sd = .5),0)]
print (i)
})
with options(warn=1) warning is printed when it occurs. (from Andrie answer)
And print(i) tells me on which iteration it is produced.
You could use tryCatch and return warning messages from your anonymous function together with the chisq.test result in a list.
Example:
fun <- function(x) {
if (x == 2) warning("It's a two!")
return(x^2)
}
lapply(1:3, function(i) tryCatch(list(result = fun(i), warning = "no warning"),
warning = function(w) list(result = fun(i),
warning = as.character(w))))
#[[1]]
#[[1]]$result
#[1] 1
#
#[[1]]$warning
#[1] "no warning"
#
#
#[[2]]
#[[2]]$result
#[1] 4
#
#[[2]]$warning
#[1] "simpleWarning in fun(i): It's a two!\n"
#
#
#[[3]]
#[[3]]$result
#[1] 9
#
#[[3]]$warning
#[1] "no warning"
#
#
#Warning message:
#In fun(i) : It's a two!
Given the following R knitr document:
\documentclass{article}
\begin{document}
<<data>>=
opts_chunk$set(comment = NA) # omits "##" at beginning of error message
x <- data.frame(x1 = 1:10)
y <- data.frame()
#
<<output_x>>=
if (nrow(x) == 0) stop("x is an empty data frame.") else summary(x)
#
<<output_y>>=
if (nrow(y) == 0) stop("y is an empty data frame.") else summary(y)
#
\end{document}
As expected, the last chunk returns an error with the custom message. The compiled PDF looks a little different:
Error: y is an empty data frame.
I want this text to just be
y is an empty data frame.
Without the Error: part or the red color. Can I achieve this? How?
Edit: I was able to make it in the mock data through the following workaround:
<<output_y>>=
if (nrow(y) == 0) cat("y is an empty data frame.") else summary(y)
#
However, that doesn't work with my real data, because I need the function to be stopped at that point.
Although I do not understand why an error should not be called Error, you are free to customize the output hook error to remove Error: from the message:
library(knitr)
knit_hooks$set(error = function(x, options) {
knitr:::escape_latex(sub('^Error: ', '', x))
})
You could do something like this. options("show.error.messages" = FALSE) turns off error messages, so you could temporarily employ that once the if statement is entered and use on.exit to reset it.
This way, stop stops the function, Error: is avoided, and the desired message is printed in red.
> f <- function(x) {
if(x > 5) {
g <- getOption("show.error.messages")
options(show.error.messages = FALSE)
on.exit(options(show.error.messages = g))
message("x is greater than 5.")
stop()
}
x
}
> f(2)
# [1] 2
> f(7)
# x is greater than 5.
Note: I'm not exactly sure how safe this is and I'm not a big supporter of changing options settings inside functions.
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.
If you want to use the try constructs, you can set the options for warn. See also ?options. Better is to use tryCatch() :
x <- function(i){
if (i < 10) warning("A warning")
i
}
tt <- tryCatch(x(5),error=function(e) e, warning=function(w) w)
tt2 <- tryCatch(x(15),error=function(e) e, warning=function(w) w)
tt
## <simpleWarning in x(5): A warning>
tt2
## [1] 15
if(is(tt,"warning")) print("KOOKOO")
## [1] "KOOKOO"
if(is(tt2,"warning")) print("KOOKOO")
To get both the result and the warning :
tryCatch(x(5),warning=function(w) return(list(x(5),w)))
## [[1]]
## [1] 5
##
## [[2]]
## <simpleWarning in x(5): A warning>
Using try
op <- options(warn=2)
tt <- try(x())
ifelse(is(tt,"try-error"),"There was a warning or an error","OK")
options(op)
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.
here is an example:
testit <- function() warning("testit") # function that generates warning.
assign("last.warning", NULL, envir = baseenv()) # clear the previous warning
testit() # run it
if(length(warnings())>0){ # or !is.null(warnings())
print("something happened")
}
maybe this is somehow indirect, but i don't know the more straightforward way.
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.