Disregarding simple warnings/errors in tryCatch()
I think you're looking for the difference between tryCatch
, which catches a condition and continues evaluation from the environment where the tryCatch was defined, versus withCallingHandlers
, which allows you to 'handle' a condition and then continue on from the location where the condition occurred. Take a look at warning
(or the help page for warning, but that's less fun), especially the lines
withRestarts({
.Internal(.signalCondition(cond, message, call))
.Internal(.dfltWarn(message, call))
}, muffleWarning = function() NULL)
This says -- signal a condtion, but insert a 'restart' where the condition was signaled from. Then you'd
withCallingHandlers({
warning("curves ahead")
2
}, warning = function(w) {
## what are you going to do with the warning?
message("warning occurred: ", conditionMessage(w))
invokeRestart("muffleWarning")
})
Although withCallingHandlers
is often used with warnings and tryCatch
with errors, there is nothing to stop one from 'handling' an error or catching a warning if that is the appropriate action.
Store errors and warnings with tryCatch() in a list
Here's a version that puts the tryCatch
within the loop, and saves the errors and warnings that were generated:
hospitals_url <- "https://services1.arcgis.com/Hp6G80Pky0om7QvQ/arcgis/rest/services/Hospitals_1/FeatureServer/0/query?where=1%3D1&outFields=*&outSR=4326&f=json"
hospitals_num <- c(0, 2000, 4000, 6000)
hosp_e <- list()
hosp_w <- list()
hosget <- lapply(hospitals_num, function(num) {
tryCatch({
hospitals_url_loop <- paste(hospitals_url, "&resultOffset=", num)
hospitals_json <- fromJSON(hospitals_url_loop)
hospitals_df <- as.data.frame(hospitals_json$features$attributes)},
error = function(e){
hosp_e <<- c(hosp_e, list(e))
print(e)},
warning = function(w){
hosp_w <<- c(hosp_w, list(w))
print(w)}
) })
hospitals_df <- do.call(rbind, hosget)
I didn't adapt your finally
code to this rearrangement; I'll leave that to you. But at the end, hosp_e
will be a list holding all the errors, and hosp_w
will be a list holding all the warnings.
Catch specific warning and ignore others
I believe withCallingHandlers
is what you want: Disregarding simple warnings/errors in tryCatch()
withCallingHandlers(myfun(),
warning = function(w){
if(grepl("right", w$message)){
stop("I have you now")
} else {
message(w$message)
}
})
Handling errors before warnings in tryCatch
This relates to the "is it possible to process the warning and then have the function continue while still catching errors?" question.
I had a similar issue where I wanted to bind my logging functions for warnings and errors to the try catch and always continue after warnings and also be able to perform multiple attempts at try catch, e.g. for accessing a flimsy network drive. This is what I ended up using. This or a more simplified version could help with what your after.
MyLibrary.Sys.Try <- function(
expr, # Expression that will be evaluated by the call to Try
errorMessage="", # Optional prepended string to add to error output
warningMessage="", # Optional prepended string to add to warning output
failureMessage="", # Optional prepended string to add to failing all attempts
finally=NULL, # Optional expression to be executed at the end of tryCatch
attempts=1, # Number of attempts to try evaluating the expression
suppressError=TRUE, # Should the call just log the error or raise it if all attempts fail
quiet=FALSE # Return expression normally or as invisible
) {
tryNumber <- 0
while(tryNumber<attempts) {
tryNumber <- tryNumber + 1
# If not suppressing the error and this is the last
# attempt then just evaluate the expression straight out
if(tryNumber == attempts && !suppressError){
# NOTE: I think some warnings might be lost here when
# running in non-interactive mode. But I think it should be okay
# because even nested dispatch seems to pick them up:
# MyLibrary.Sys.Try(MyLibrary.Sys.Try(function(),suppressError=F))
return(expr)
}
tryCatch({
# Set the warning handler to an empty function
# so it won't be raised by tryCatch but will
# be executed by withCallingHandlers
options(warning.expression=quote(function(){}))
withCallingHandlers({
if(quiet) {
return(invisible(expr))
} else {
return(expr)
}
},error=function(ex){
MyLibrary.Sys.Log.Error(errorMessage,ex)
}, warning=function(warn){
# Had issues with identical warning messages being
# issued here so to avoid that only log it the first
# time it happens in a given minute.
warnStamp <- paste(Sys.time(),warn,collapse="_",sep="")
if(!identical(warnStamp,getOption("MyLibrary.LAST.WARNING"))) {
if(!(interactive() && is.null(getOption("warning.expression")))){
MyLibrary.Sys.Log.Warning(warningMessage,warn)
}
options(MyLibrary.LAST.WARNING=warnStamp)
}
})
},error=function(ex){
# Suppressing the error since it's been logged
# by the handler above. Needs to be suppressed
# to not invoke the stop directly since the
# handler above passes it through.
},finally={
# Set the warning handler to the default value
# of NULL which will cause it to revert to it's
# normal behaviour. If a custom handler is normally
# attached it would make sense to store it above
# and then restore it here. But don't need that now
options(warning.expression=NULL)
if(!is.null(finally)){
if(quiet) {
return(invisible(finally))
} else {
return(finally)
}
}
})
}
msg <- paste(ifelse(nchar(failureMessage)>0," - ",""),"Failed to call expression after ",attempts," attempt(s)",sep="")
MyLibrary.Sys.Log.Error(failureMessage,msg)
}
tryCatch() apparently ignoring a warning
If you look in binning
I think you'll find that the "warning" you see is not generated via warning()
but with cat()
, which is why tryCatch
isn't picking it up. The author of binning
probably deserves a few lashings with a wet noodle for this oversight. ;) (Or it could be on purpose due to the particular way that rattle works, I'm not sure.)
It appears to return NULL
when this happens, so you could simply handle it manually. Not ideal, but possibly the only way to go.
How do I save warnings and errors as output from a function?
Maybe this is the same as your solution, but I wrote a factory
to convert plain old functions into functions that capture their values, errors, and warnings, so I can
test <- function(i)
switch(i, "1"=stop("oops"), "2"={ warning("hmm"); i }, i)
res <- lapply(1:3, factory(test))
with each element of the result containing the value, error, and / or warnings. This would work with user functions, system functions, or anonymous functions (factory(function(i) ...)
). Here's the factory
factory <- function(fun)
function(...) {
warn <- err <- NULL
res <- withCallingHandlers(
tryCatch(fun(...), error=function(e) {
err <<- conditionMessage(e)
NULL
}), warning=function(w) {
warn <<- append(warn, conditionMessage(w))
invokeRestart("muffleWarning")
})
list(res, warn=warn, err=err)
}
and some helpers for dealing with the result list
.has <- function(x, what)
!sapply(lapply(x, "[[", what), is.null)
hasWarning <- function(x) .has(x, "warn")
hasError <- function(x) .has(x, "err")
isClean <- function(x) !(hasError(x) | hasWarning(x))
value <- function(x) sapply(x, "[[", 1)
cleanv <- function(x) sapply(x[isClean(x)], "[[", 1)
tryCatch within for loop
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
Related Topics
Installing R 3.5.0 with --Enable-R-Shlib
How to Extract All the Rows If a Level in One Column Contains All the Levels of Another Column in R
Dplyr: Put Count Occurrences into New Variable
How to Use Outlier Tests in R Code
How to Transpose a Dataframe in Tidyverse
Visualizing R Function Dependencies
Include Data Examples in Developing R Packages
Function for Retrieving Own Ip Address from Within R
Using Lapply with Changing Arguments
How to Get a Warning on "Shiny App Will Not Work If the Same Output Is Used Twice"
Plot Causes "Error: Incorrect Number of Dimensions"
Error in Fetch(Key):Lazy-Load Database
Monitoring for Changes in File(S) in Real Time
Find Indices of Non Zero Elements in Matrix
Get Map with Specified Boundary Coordinates
Package Dependencies When Installing from Source in R
What's the Real Meaning About 'Everything That Exists Is an Object' in R