R: error when printing character() in assert_that message - r

I would like to assert some expressions involving certain variable that is potentially evaluated to character(). I would like to print an assertation message showing the value of the variable which can be character().
assertthat::assert_that(<expr containing variable [x]>, msg = sprintf("Test for x failed on value %s" , x))
## Variable [x] is a character variable that can be character() or other conventional characters like `"a"`, `"b"` or ``"c"``.
Below is a simplified example to show the error I obtained when the variable is evaluated to character():
assertthat::assert_that(FALSE, msg = sprintf("%s", character()))
gives me error
## > Error in stop(assertError(attr(res, "msg"))) : bad error message
However the sprintf itself works well
sprintf("%s", character())
## character(0)
sprintf("%s", character()) %>% typeof
## [1] "character"
What I have tried:
Wrap %s with backticks assert_that(FALSE, msg = sprintf("`%s`", character()))
Load the assertthat package first with library(assertthat) then call assert_that(...)
Assign first character() to a variable and call that variable in assert_that which is actually more similar to my real life use case
v <- character()
assert_that(FALSE, msg = sprintf("`%s`", v))
Assign the return value of sprintf call to a variable and then feed it into the call of assert_that
v <- character()
errmsg <- sprintf("`%s`", v)
assert_that(FALSE, msg = errmsg)
All gave same error. It seems something inside assert_that affects.
What would be the reason of the error and how to fix it?

I think the problem here is that your sprintf statements results in a character with length 0:
length(sprintf("%s", character()))
#> [1] 0
This means there is not really a message for assert_that to print. So you have to ensure the length of the sprintf output has a length of 1:
require(assertthat)
#> Loading required package: assertthat
assert_that(F, msg=character())
#> Error in stop(assertError(attr(res, "msg"))) : bad error message
assert_that(F, msg=letters[1:2])
#> Error in stop(assertError(attr(res, "msg"))) : bad error message
assert_that(F, msg=letters[1])
#> Error: a

Related

Skipping errors in a loop in R

I have a list of file to read. I wrote a loop, like this (it is only a example). However, some of these elements are missing and I got a message error interrupting the loop.I would like to skip the error and finish the loop, with the other elements of the list.
list<- c("ciao",
"miao",
"bau")
for (symbols in list){
a <- symbols
b <- as.Date(symbols)
c <- as.numeric(symbols)
d<- cbind(a,b,c)
write.csv(d)
}
As, far as I know, I could use the function try. I have already read some examples here, but they don't fit my need... or I don't know how to implement it.
Any idea to solve the problem?
Thank you
You can use tryCatch() for this and use the error = part to indicate what you want in the case of an error.
For example, in the below we can continue checking if the symbol == "bau" even after "miao" caused an error:
# error before it checks all args of list
list <- c("ciao", "miao", "bau")
for (symbols in list) {
if (symbols == "miao") stop("Er!")
print(symbols == "bau")
}
#> [1] FALSE
#> Error in eval(expr, envir, enclos): Er!
# now it will check all args even with error
for (symbols in list) {
tryCatch(
expr = {
if (symbols == "miao") stop("Er!")
print(symbols == "bau")
},
error = function(e) NA
)
}
#> [1] FALSE
#> [1] TRUE
Created on 2022-08-03 by the reprex package (v2.0.1)

R return object invisibly together with normal error when error is thrown

I have a function that may throw an error. When an error is thrown, I'd like to show the error message, as if the error actually occurred, and further return an object invisibly.
I looked at this thread, which uses withCallingHandlers and logs the error message somewhere. This comes close, but I do not want to log the message as text and then print a text message, the function should show the error message as if it would have exited on error.
The functions workflow looks like the following:
foo <- function(x){
y <- x + 1
if(y == 2) {
stop("oops")
# also return y invisibly when error is thrown,
}
z <- y + 1
z
}
Based on input x an intermediate y is calculated. y is used for the error check. If an error occurs y should be returned invisibly and an normal error message should be thrown. Otherwise z is calculated an returned.
foo(1) should return error message and y invisibly.
I thought about using on.exit but in this case, always y is return invisibly.
Any help is appreciated.
Add: Maybe what I have in mind is not possible. In this case, would it be possible to show a logged error message in a way that comes close to a real error message?
Add2: I thought about issuing a warning, but in my actual use case a warning would be misleading, since the function does not produce desired result z, but just some intermediate result y and I want to return y so that the user can inspect it further, and reason about why it wasn't processed correctly by foo. Thinking about it, other must have encountered the same problem, there should be some kind of solution.
Add3: Maybe it is possible to use on.exit together with a flag which is triggered, so that on.exit will return y invisibly in case of an error and do nothing otherwise.
func <- function(x) { a <- simpleError("quux"); attr(a,"abc") <- 7; stop(a); }
func()
# Error: quux
So far so good, we see an error. If we catch this and look at the contents of the error message we can see the attribute tucked inside:
dput(tryCatch(func(), error=function(e) e))
# structure(list(message = "quux", call = NULL), class = c("simpleError",
# "error", "condition"), abc = 7)
and even extract it easily
dput(tryCatch(func(), error=function(e) attr(e,"abc")))
# 7
Below I post my final solution to my question. Basically it is based on r2evans answer combined with user20650's comment.
As r2evans showed we can use simpleError to create an error and also attach an object in the attributes. The big plus of this approach is that it returns a real error that we can program with. If we would only show the error message and then return a vector invisibly, tryCatch wouldn't recognize it as an error. The downside is that simpleError doesn't look like a normal error to an interactive user when printed in the console. It will show something like <simpleError: x must not be 1>. However, the message is not printed in red like normal errors.
Here user20650's comment shows a nice way out. We can first print the message with message(conditionMessage(e)) and then we can return the simpleError invisibly.
foo <- function(x) {
y <- x + 1
if(y == 2) {
foo_internal <- function(val) {
a <- simpleError("x must not be 1")
attr(a,".data") <- y
stop(a)
}
return(
tryCatch(foo_internal(y),
error = function(e) {
message(conditionMessage(e))
invisible(e)
})
)
}
z <- y + 1
z
}
# this is a special function to inspect the object attached to `simpleError`
inspect <- function(x = NULL) {
if(is.null(x)) {
x <- .Last.value
}
attr(x,".data")
}
# returns error message
foo(1)
#> x must not be 1
# we can get y's value with a special inspect function
inspect()
#> [1] 2
# foo(1) returns a "real" error
class(foo(1))
#> x must not be 1
#> [1] "simpleError" "error" "condition"
Created on 2021-03-13 by the reprex package (v0.3.0)

How can I make a loop skip over inputs that generate warnings?

I'm running a complicated function (multiple imputation with Amelia) over a list of datasets. Every so often, a dataset will trigger a long list of warnings that eventually result in an error. I would like R to give up as soon as the first warning is issued and move on to the next dataset. Here is a minimal working example:
df.list <- list(
data.frame(1:4),
data.frame(-1, -2, -4),
data.frame(10:15)
)
for(df in df.list){
ans <- sum(sapply(df, sqrt))
print(ans)
}
The script issues three warnings about NaNs and then prints:
[1] 6.146264
[1] NaN
[1] 21.1632
I would like it to produce 1 message input 2 failed and then output only the valid results:
[1] 6.146264
[1] 21.1632
(The function I'm actually running, amelia(), issues warnings for 10 minutes before finally throwing an error, so I would like to cut it off at the first warning.)
What about this: the sqrt function cannot return -1 so I make tryCatch return -1 when a warning occurs. The nested lapply is required to loop through the list elements to calculate the square root, returned as a list, and then to loop through those list elements to sum. The -1 value in the result indicates a failed calculation and I can test that.
result <- unlist(
lapply(
lapply(df.list, function(x) tryCatch(sqrt(x), warning = function(w) -1)), sum))
failed <- which(result == -1)
result <- result[-failed]
print(paste0("input ", failed, " failed"))
result
> print(paste0("input ", failed, " failed"))
[1] "input 2 failed"
> result
[1] 6.146264 21.163196

how to skip blank files in read.table() in R

I want to read a lot of text files in a folder using read.table in R, but there are some blank file among those text files, errors coms when I using the following code.
filenames<-list.files("M:/files/test1",pattern=".txt");
datalist<-lapply(filenames,function(name){
read.table(paste("M:/files/test1/",name,sep=""),head=FALSE,stringsAsFactors=FALSE,sep="\t")
})
The easiest way to do this is to add a simple error-catching mechanism using try:
datalist<-lapply(filenames,function(name){
x <- try(read.table(paste("M:/files/test1/",name,sep=""),head=FALSE,stringsAsFactors=FALSE,sep="\t"))
if(inherits(x, "try-error"))
return(NULL)
else
return(x)
})
To see this in action, try a toy example. What try does is return the object, or in the event of an error a special object class containing details of the error:
x <- try(stop("Test error"))
inherits(x, "try-error")
x
# [1] "Error in try(stop(\"Test error\")) : Test error\n"
# attr(,"class")
# [1] "try-error"
# attr(,"condition")
# <simpleError in doTryCatch(return(expr), name, parentenv, handler): Test error>
Versus if you simply introduce an error without try the program will stop and x will be undefined:
rm(x)
x <- stop("Test error")
# Error: Test error
x
# Error: object 'x' not found
If the operation succeeds inside try(), it simply returns the correct object:
x <- try(1)
x
# [1] 1
skip empty files
test the size of each fileļ¼Œ and skip the file size of 0
for (file in list.files(,"*.txt")){
if (file.size(file) == 0) next
print(file)
}

How to get warnings() output to string

When I type warnings() to console, I get back
Warning message:
In fread(my_directory, ... :
C function strtod() returned ERANGE for one or more fields. The first was string input '4.40589099726375E-309'. It was read using (double)strtold() as numeric
However when I type as.character(warnings()), I get:
[1] "fread(my_directory)"
My objective is to get the actual message displayed in warning() into a character string, so that I can pass it to the logwarn function in the logging package. Currently, I am doing logwarn(warnings(),logger="some_log_file.log") to record my warnings, but it gives the incorrect coercion to character that I displayed above.
Note that I can just use sink but I want to stick with logging package, so I require the ability to correct coerce to character.
This may not be the exact answer you're looking for, but I think it's worth a mention.
R has a global variable, last.warning, which holds just that, the last warning. Calling names on it will return the last warning as a character string. Here's a little example
First, purposely trigger a warning:
x <- 1:5
if(x == 1) "yes" else "no"
# [1] "yes"
# Warning message:
# In if (x == 1) "yes" else "no" :
# the condition has length > 1 and only the first element will be used
Look at the variable last.warning:
last.warning
# $`the condition has length > 1 and only the first element will be used`
# if (x == 1) "yes" else "no"
Now look at the names(last.warning). This returns the warning as a character string:
names(last.warning)
# [1] "the condition has length > 1 and only the first element will be used"
warnings() returns a list.
The list values are the language elements which produced the warning; that is what you are seeing with as.character().
The names of the list values are the warning messages. You can get those with names(warnings()).
Use a calling handler along with the 'restart' (see ?warning and ?withCallingHandlers) that warning() creates
f = function() { warning("oops"); 1 }
withCallingHandlers({
f()
}, warning=function(cond) {
txt <- conditionMessage(cond)
## do with txt what you will, e.g.,
## logwarn(txt, logger="some_log_file.log")
message("captured warning: ", txt, "; now continuing")
## signal that the warning has been handled
invokeRestart("muffleWarning")
})

Resources