I am trying to write an error exception handling in R using tryCatch.
correct = 1
tryCatch({
sqrt(b)
},
warning = function(w){
print("NaNs")
},
finally = {
correct = 0
}
)
correct
If I set b = -5, the warning is printed and the value of correct is 0. If I set b = 5, the warning is not printed. However the value is still 0. What I would like is that when there is some warning/error to catch, the value of correct is 0. When there is no warning/error, the value of correct is 1. Thanks!
You don't want finally here. Instead just specify return values of 0 when an error or warning occurs.
myfun <- function(b) {
tryCatch({
sqrt(b)
},
error = function(e){
return(0)
},
warning = function(w){
return(0)
}
)
}
> myfun(5)
# [1] 2.236068
> myfun(-5)
# [1] 0
Here is a function:
is.bad <- function(x) {
as.numeric(isTRUE(tryCatch(x,
error = function(c) TRUE,
warning = function(c) TRUE
)))
}
is.bad(stop())
is.bad(warning())
is.bad(message())
is.bad(3)
## > is.bad(stop())
## [1] 1
## > is.bad(warning())
## [1] 1
## > is.bad(message())
##
## [1] 0
## > is.bad(3)
## [1] 0
The finally clause is executed regardless of whether or not a warning is thrown in the square root. That's why you end up with correct == 0 regardless.
The following code will do what you want, although I used a global assignment <<- which might cause problems if you are not careful. This was necessary because otherwise you can't change the value of correct from within the warning function.
correct = 1
tryCatch({
sqrt(b)
},
warning = function(w){
print("NaNs")
correct <<- 0
}
)
Related
I have a function that conditionally prints messages/warnings, e.g. like below:
test <- function(x)
{
for (i in 1:5)
{
if (i == 3)
{
warning("test")
break()
}
if (i != 3)
{
message(i)
}
}
}
The function correctly breaks out of index 3 and prints the warning.
test(1)
1
2
Warning message:
test
However, when I wrap that function into an lapply, the warning message is only printed at the end:
lapply(1:2, test)
1
2
1
2
[[1]]
NULL
[[2]]
NULL
Warning messages:
1: In FUN(X[[i]], ...) : test
2: In FUN(X[[i]], ...) : test
How can I make sure that the message is printed "per loop"? I.e.
1
2
Warning messages:
1: In FUN(X[[i]], ...) : test
1
2
Warning messages:
1: In FUN(X[[i]], ...) : test
[[1]]
NULL
[[2]]
NULL
You can set immediate warnings globally using options(warn = 1). warning() also has an immediate. argument for specific use:
test <- function(x)
{
for (i in 1:5)
{
if (i == 3)
{
warning("test", immediate. = TRUE)
break()
}
if (i != 3)
{
message(i)
}
}
}
lapply(1:2, test)
1
2
Warning in FUN(X[[i]], ...) : test
1
2
Warning in FUN(X[[i]], ...) : test
[[1]]
NULL
[[2]]
NULL
Note from the documentation:
warning signals a warning condition by (effectively) calling
signalCondition. If there are no handlers or if all handlers return,
then the value of warn = getOption("warn") is used to determine the
appropriate action. If warn is negative warnings are ignored; if it is
zero they are stored and printed after the top–level function has
completed; if it is one they are printed as they occur and if it is 2
(or larger) warnings are turned into errors. Calling
warning(immediate. = TRUE) turns warn <= 0 into warn = 1 for this call
only.
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.
I am new to R and I have checked most links online but I have not been able to solve the problem.
Here is a reproducible example of a Monte Carlo simulation I am doing:
rm(list = ls())
x=c(-2,3,-1,4,'A')
y=rep(0,5)
for(i in 1:5){
tryCatch(
expr = {
y[i]=log(x[i])
},
error = function(e){
message('Caught an error!',i)
return(NA)
},
warning = function(w){
message('Caught an warning!',i)
return(NA)
}
)
}
Please how do I fix the code so that at the end of the for loop, R returns the values of y as
y= NA,log(3),NA,log(4),NA
i=1,3,5
and the values of i where there was an error or warning.
That is, error and warnings are replaced with NA and successful evaluations are returned and the values of i where there was an error or warning are also returned.
Thanks
Initialise y with NA and then run the for loop. Also since x is a vector and vector can hold only one class all the numbers in x turn to characters as you have non-numeric elements in x so you need to convert them to numbers before taking log.
x=c(-2,3,-1,4,'A')
y=rep(NA,5)
for(i in 1:5){
tryCatch(
expr = {
y[i]= log(as.numeric(x[i]))
},
error = function(e){
message('Caught an error!',i)
},
warning = function(w){
message('Caught a warning! ',i)
}
)
}
#Caught a warning! 1
#Caught a warning! 3
#Caught a warning! 5
y
#[1] NA 1.098612 NA 1.386294 NA
and then use is.na with which to get the index where error or warning happened.
which(is.na(y))
#[1] 1 3 5
Of course, you can do this without for loop as well
y <- log(as.numeric(x))
which(is.na(y))
#[1] 1 3 5
To return different value based on error or warning, we can make this into a function
run_fun <- function(x) {
tryCatch(
expr = {
return(log(as.numeric(x)))
},
error = function(e){
message('Caught an error!',i)
return(100)
},
warning = function(w){
message('Caught a warning! ',i)
return(200)
}
)
}
and then call it in for loop.
for (i in seq_along(x)) {
y[i] <- run_fun(x[i])
}
y
#[1] 200.0000 1.0986 200.0000 1.3863 200.0000
I want to catch a warning and still run an expression.
Here is an example:
x <- 0
tryCatch({
x <- as.numeric("text") # this is NA and causes warning
}, warning = function(w) {
print("I am a message")
})
x
# x still 0
Previous code catches the warning and print the message, BUT the value of x is not NA afterwards, which means that the expression did not run because of the warning.
I could run the expression with suppressWarnings() and <<- as follows:
x <- 0
tryCatch({
x <- as.numeric("text")
}, warning = function(w) {
print("I am a message")
suppressWarnings(x <<- as.numeric("text"))
})
x
# now x is NA
Is there a more elegant way to do that? maybe one of following examples?
another function other than tryCatch()
or using some parameter of tryCatch()
or maybe another package other than base
...
From this answer follows that this code could work:
x <- 0
withCallingHandlers({
x <- as.numeric("text")
}, warning = function(w) {
print("I am a message")
invokeRestart("muffleWarning")
})
x
(I came across this post looking for a way to catch a warning and alter the return value of a function. I ended up with
which_nondefault_enc <- function(txt) {
ans <- rep(NA, length(txt))
for (i in seq(1, length(txt)))
ans[i] <- tryCatch(stringi::stri_enc_tonative(txt[i]), warning = function(w) return(NA))
return(which(is.na(ans)))
}
which returns those indices of the vector, where warnings like "unable to translate '<U+0001F41F>' to native encoding" are used as a selection criterion.)
I'm using a tryCatch function in which I want another function to be run in case of error/warning. This other function depends on some arguments and for some reason, tryCatch does not recognize them when they are the error and warning functions.
Here is a simplified function where I'm facing the same problem:
essai <- function(x){
y <- 2
result <- tryCatch({
sqrt(x*y)
} , warning = function(cond,x,y) {
message(cond)
sqrt(abs(x*y))
} , error = function(cond,x,y) {
message(cond)
sqrt(abs(x*y))
} , finally = {
message("done")
} )
}
nbs <- c(1,2,3,-1,-2)
lapply(nbs, essai)
But here I have an error message saying Error in value[[3L]](cond) : argument "x" is missing, with no default. R doesn't understand it has to reuse x and y used in the failed function, why?
error and warning take functions of one argument. This works fine:
essai <- function(x, cond = 'problem'){
y <- 2
result <- tryCatch({
sqrt(x*y)
} , warning = function(w) {
message(cond)
sqrt(abs(x*y))
} , error = function(e) {
message(cond)
sqrt(abs(x*y))
} , finally = {
message("done")
} )
}
The rest of the arguments can be specified in your essai function and will be available from both warning and error.
nbs <- c(1,2,3,-1,-2)
lapply(nbs, essai)
#done
#done
#done
#problem
#done
#problem
#done
# [[1]]
# [1] 1.414214
#
# [[2]]
# [1] 2
#
# [[3]]
# [1] 2.44949
#
# [[4]]
# [1] 1.414214
#
# [[5]]
# [1] 2