With mclapply() all issued warnings seems get suppressed:
library(multicore)
mclapply(1:3, function(x) warning(x))
[[1]]
[1] "1"
[[2]]
[1] "2"
[[3]]
[1] "3"
while lapply would give:
lapply(1:3, function(x) warning(x))
[[1]]
[1] "1"
[[2]]
[1] "2"
[[3]]
[1] "3"
Warning messages:
1: In FUN(1:3[[3L]], ...) : 1
2: In FUN(1:3[[3L]], ...) : 2
3: In FUN(1:3[[3L]], ...) : 3
Any tips on how to avoid loosing the warnings?
According to mclapply's help page, in my opinion the argument mc.silent should allow you to chose if warnings are to be printed or not. Strangely, it does not do that. Setting it explictly to TRUE or FALSE does not have any effect in your situation.
So that leaves us only with a somewhat dirty hack: forcing R to print warnings as they occur.
options(warn=1)
mclapply(1:3, function(x) warning(x))
# Warning in FUN(1L[[1L]], ...) : 1
# Warning in FUN(2L[[1L]], ...) : 2
# Warning in FUN(3L[[1L]], ...) : 3
# [[1]]
# [1] "1"
#
# [[2]]
# [1] "2"
#
# [[3]]
# [1] "3"
I have this problem too. If I'm reading the code correctly, parallel::mclapply() passes the mc.silent option to parallel:mcparallel(). But mcparallel() has this line:
sendMaster(try(eval(expr, env), silent = TRUE))
I think that's the place where the warnings would be sent back to the master process, but the mc.silent is not respected. That's my best guess about what is going on.
For anyone who will come around the same issue, here is a workaround:
safe_mclapply <- function(X, FUN, mc.cores, stop.on.error=T, ...){
fun <- function(x){
res_inner <- tryCatch({
withCallingHandlers(
expr = {
FUN(x, ...)
},
warning = function(e) {
message_parallel(trimws(paste0("WARNING [element ", x,"]: ", e)))
# this line is required to continue FUN execution after the warning
invokeRestart("muffleWarning")
},
error = function(e) {
message_parallel(trimws(paste0("ERROR [element ", x,"]: ", e)))
}
)},
error = function(e){
# error is returned gracefully; other results of this core won't be affected
return(e)
}
)
return(res_inner)
}
res <- mclapply(X, fun, mc.cores=mc.cores)
failed <- sapply(res, inherits, what = "error")
if (any(failed == T)){
error_indices <- paste0(which(failed == T), collapse=", ")
error_traces <- paste0(lapply(res[which(failed == T)], function(x) x$message), collapse="\n\n")
error_message <- sprintf("Elements with following indices failed with an error: %s. Error messages: \n\n%s",
error_indices,
error_traces)
if (stop.on.error)
stop(error_message)
else
warning(error_message, "\n\n### Errors will be ignored ###")
}
return(res[!failed])
}
#' Function which prints a message using shell echo; useful for printing messages from inside mclapply when running in Rstudio
message_parallel <- function(...){
system(sprintf('echo "\n%s\n"', paste0(..., collapse="")))
}
safe_mclapply above is a wrapper around mclapply. For each iteration it uses withCallingHandlers to catch and print warnings and errors; note that invokeRestart("muffleWarning") is required in order to continue FUN exection and return the result. Printing is done via message_parallel function which uses shell echo to print messages to R console (tested to work in Rstudio).
safe_mclapply provides few more features which you might find optional:
along with the warning and error it prints character representation of x which I find useful because it gives an idea where the message comes from
tryCatch around withCallingHandlers helps to return an error gracefully so that other results of the core are not affected
after mclapply is executed, the indices of error results are printed
stop.on.error provides an option to ignore any results which contain error and continue despite the errors
Side note: I personally prefer pbmclapply function from pbmcapply over mclapply which adds a progress bar. You can change mclapply to pbmclapply in the code above.
Small snippet to test the code:
X <- list(1, 2, 3, 4, 5, 6)
f <- function(x){
if (x == 3){
warning("a warning")
warning("second warning")
}
if (x == 6){
stop("an error")
}
return(x + 1)
}
res <- safe_mclapply(X = X, FUN = f, mc.cores=16)
res_no_stop <- safe_mclapply(X = X, FUN = f, mc.cores=16, stop.on.error = F)
Related
I'm trying to find a place of the warning in the function:
fun <- function(x, y){
z = sum(x,y)
if(z > 15)
warning("Potential problem")
return(z)
}
x = 10; y = 20
fun(x, y)
The result I'd like to get is the line number of the warning (not only warning msgs created by developer) and the function name where it exists. The name of a function I can get with the withCallingHandlers function, however I don't know how to find the line number of the warning.
|function_name | warning_index |
--------------------------------
|fun |4 |
EDITED to use base functions:
You can use sys.calls() to see the call stack, and then look through it for lines with debug info. Here's a demonstration that does it.
# First, some code that will generate a warning
fun <- function(x, y){
z = sum(x,y)
if(z > 15)
warning("Potential problem")
return(z)
}
x = 10; y = 20
# Here's a handler that looks through the call stack
# for locations. Not all calls have recorded locations
# (most packages are installed without debug info)
# but this will find the debug info if it is there,
# and add it to the warning message.
locatedWarnings <- function(e) {
calls <- sys.calls()
locations <- character()
for (i in rev(seq_along(calls)))
if (!is.null(srcref <- getSrcref(calls[[i]])))
locations <- c(locations, sprintf("%s:%d", getSrcFilename(srcref), srcref[1]))
# If we found any locations, redo the warning
# with those locations prepended to the message
if (length(locations)) {
call <- if (!is.null(e$call)) paste("In", deparse(e$call)) else ""
warning(sprintf("%s at %s: %s", call, paste(locations, collapse=","), conditionMessage(e)), call. = FALSE)
invokeRestart("muffleWarning")
}
}
withCallingHandlers(fun(x, y),
warning = locatedWarnings)
#> Warning: In fun(x, y) at <text>:5: Potential problem
#> [1] 30
Created on 2023-02-15 with reprex v2.0.2
If you put this example in a file and source it with the default keep.source = TRUE, you'll get the filename and line for the warning line as well as the withCallingHandlers() line. Not sure why reprex didn't give the second one. If you just execute it by cut and paste to the console you won't get very useful line info, because every statement restarts the line count.
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.
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))
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!
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.