47

I have an example function below that reads in a date as a string and returns it as a date object. If it reads a string that it cannot convert to a date, it returns an error.

testFunction <- function (date_in) {
    return(as.Date(date_in))
    }

testFunction("2010-04-06")  # this works fine
testFunction("foo")  # this returns an error

Now, I want to use lapply and apply this function over a list of dates:

dates1 = c("2010-04-06", "2010-04-07", "2010-04-08")
lapply(dates1, testFunction)  # this works fine

But if I want to apply the function over a list when one string in the middle of two good dates returns an error, what is the best way to deal with this?

dates2 = c("2010-04-06", "foo", "2010-04-08")
lapply(dates2, testFunction)

I presume that I want a try catch in there, but is there a way to catch the error for the "foo" string whilst asking lapply to continue and read the third date?

zx8754
  • 52,746
  • 12
  • 114
  • 209
John
  • 5,139
  • 19
  • 57
  • 62
  • 1
    Very closely related: http://stackoverflow.com/questions/1395622/debugging-lapply-sapply-calls – Shane Apr 07 '10 at 02:12

4 Answers4

76

Use a tryCatch expression around the function that can throw the error message:

testFunction <- function (date_in) {
  return(tryCatch(as.Date(date_in), error=function(e) NULL))
}

The nice thing about the tryCatch function is that you can decide what to do in the case of an error (in this case, return NULL).

> lapply(dates2, testFunction)
[[1]]
[1] "2010-04-06"

[[2]]
NULL

[[3]]
[1] "2010-04-08"
Shane
  • 98,550
  • 35
  • 224
  • 217
  • At the end of the day we're both doing the same thing here -- and John doesn't need exception handling. If it ain't a date, NA is returned. Why make matters more difficult? – Dirk Eddelbuettel Apr 07 '10 at 01:29
  • 1
    Yes, that's certainly true in this case. Although if the question is generalized then using `tryCatch` is probably the best way to continue through an error in `lapply`. I think the date example was just an example? – Shane Apr 07 '10 at 02:10
  • Thanks Dirk and Shane, actually the exception handling is what I was looking for. The dates were just an example of a more complicated function, but it was the easiest thing I could think of that would return an error from a list of things. – John Apr 07 '10 at 03:50
  • 4
    See also the `failwith` function in `plyr` which automates this common task. – hadley Apr 07 '10 at 16:13
  • 1
    Another possibility would be to add tryCatch in the lapply and let the function raise the error. as usual. `lapply(dates2, function(x) tryCatch(testFunctionWihoutTryCatch(x), error=function(e) NULL))`. Of course the comment of Dirk makes perfect sense in this paticular case. – DJJ Oct 17 '16 at 14:21
  • I was playing with the idea and came up with [this](http://stackoverflow.com/questions/40089885/r-allow-error-in-lapply/40090177#40090177) – DJJ Oct 17 '16 at 15:41
  • And what if what I want is REALLY to "SKIP" the element? Is it possible to find a way to "not return nothing" (not sure how to says that), and to have, if keeping the example, just two element in the returned list (without the null on). I know I could just removed it afterward nut I wonder if it's possible – Simon C. Dec 01 '17 at 12:22
8

One could try to keep it simple rather than to make it complicated:

  • Use the vectorised date parsing
R> as.Date( c("2010-04-06", "foo", "2010-04-08") )
[1] "2010-04-06" NA           "2010-04-08"

You can trivially wrap na.omit() or whatever around it. Or find the index of NAs and extract accordingly from the initial vector, or use the complement of the NAs to find the parsed dates, or, or, or. It is all here already.

  • You can make your testFunction() do something. Use the test there -- if the returned (parsed) date is NA, do something.

  • Add a tryCatch() block or a try() to your date parsing.

The whole things is a little odd as you go from a one-type data structure (vector of chars) to something else, but you can't easily mix types unless you keep them in a list type. So maybe you need to rethink this.

Dirk Eddelbuettel
  • 360,940
  • 56
  • 644
  • 725
6

You can also accomplish this kind of task with the purrr helper functions map and possibly. For example

library(purrr)
map(dates2, possibly(testFunction, NA))

Here possibly will return NA (or whatever value you specified if an error occurs.

MrFlick
  • 195,160
  • 17
  • 277
  • 295
0

Assuming the testFunction() is not trivial and/or that one cannot alter it, it can be wrapped in a function of your own, with a tryCatch() block. For example:

> FaultTolerantTestFunction <- function(date_in) {
+    tryCatch({ret <- testFunction(date_in);}, error = function(e) {ret <<- NA});
+    ret
+ }
> FaultTolerantTestFunction('bozo')
[1] NA
> FaultTolerantTestFunction('2010-03-21')
[1] "2010-03-21"
mjv
  • 73,152
  • 14
  • 113
  • 156