Reputation: 9050
Code written using lapply and friends is usually easier on the eyes and more Rish than loops. I love lapply just as much as the next guy, but how do I debug it when things go wrong? For example:
> ## a list composed of numeric elements
> x <- as.list(-2:2)
> ## turn one of the elements into characters
> x[[2]] <- "what?!?"
>
> ## using sapply
> sapply(x, function(x) 1/x)
Error in 1/x : non-numeric argument to binary operator
Had I used a for loop:
> y <- rep(NA, length(x))
> for (i in 1:length(x)) {
+ y[i] <- 1/x[[i]]
+ }
Error in 1/x[[i]] : non-numeric argument to binary operator
But I would know where the error happened:
> i
[1] 2
What should I do when using lapply/sapply?
Upvotes: 40
Views: 7897
Reputation: 3234
Using debug or browser isn't a good idea in this case, because it will stop your code so frequently. Use Try or TryCatch instead, and deal with the situation when it arises.
Upvotes: 1
Reputation: 303
Like geoffjentry said:
> sapply(x, function(x) {
res <- tryCatch(1 / x,
error=function(e) {
cat("Failed on x = ", x, "\n", sep="") ## browser()
stop(e)
})
})
Also, your for loop could be rewritten to be much cleaner (possibly a little slower):
> y <- NULL
> for (xi in x)
y <- c(y, 1 / xi)
Error in 1/xi : non-numeric argument to binary operator
For loops are slow in R, but unless you really need the speed I'd go with a simple iterative approach over a confusing list comprehension.
If I need to figure out some code on the fly, I'll always go:
sapply(x, function(x) {
browser()
...
})
And write the code from inside the function so I see what I'm getting.
-- Dan
Upvotes: 3
Reputation: 753
I've faced the same problem and have tended to make my calls with (l)(m)(s)(t)apply to be functions that I can debug().
So, instead of blah<-sapply(x,function(x){ x+1 })
I'd say,
myfn<-function(x){x+1}
blah<-sapply(x,function(x){myfn(x)})
and use debug(myfn) with options(error=recover).
I also like the advice about sticking print() lines here and there to see what is happening.
Even better is to design a test of myfn(x) that it has to pass and to be sure it passes said test before subjecting it to sapply. I only have patience to to this about half the time.
Upvotes: 0
Reputation:
Use the standard R debugging techniques to stop exactly when the error occurs:
options(error = browser)
or
options(error = recover)
When done, revert to standard behaviour:
options(error = NULL)
Upvotes: 30
Reputation: 103898
Use the plyr package, with .inform = TRUE
:
library(plyr)
laply(x, function(x) 1/x, .inform = TRUE)
Upvotes: 9
Reputation: 4754
You can debug() the function, or put a browser() inside the body. This is only particularly useful if you don't have a gajillion iterations to work through.
Also, I've not personally done this, but I suspect you could put a browser() in as part of a tryCatch(), such that when the error is generated you can use the browser() interface.
Upvotes: 0
Reputation: 100174
If you wrap your inner function with a try() statement, you get more information:
> sapply(x, function(x) try(1/x))
Error in 1/x : non-numeric argument to binary operator
[1] "-0.5"
[2] "Error in 1/x : non-numeric argument to binary operator\n"
[3] "Inf"
[4] "1"
[5] "0.5"
In this case, you can see which index fails.
Upvotes: 27