Jacob Nelson
Jacob Nelson

Reputation: 473

Try an expression multiple times until it succeeds in R

I have R code that sometimes returns an NA, which causes errors downstream. However, the only reason that it fails stems from a bad random number. Run the expression again with a different starting point, and it produces results that are not NA.

I've setup a while loop to try the expression multiple times before giving up. Here's an example:

attempts <- 0
x <- NA
while(is.na(x) & attempts < 100) {
     attempts <- attempts + 1
     rand <- runif(1)
     x <- ifelse(rand > 0.3, rand, NA)
}
if(attempts == 100) stop("My R code failed")
x

I don't like how clunky this is.

Is there a function, package, or method that can help simplify this try-repeat-try-again expression?

Upvotes: 1

Views: 1768

Answers (2)

bryn
bryn

Reputation: 1275

I had some success doing something similar with the retry package, which has the functionality to repeatedly run code until some condition is met.

https://cran.r-project.org/package=retry

My situation was a little different from yours in that my code for generating/sending emails would fail periodically, and so I wanted to rerun it until the return condition wasn't NULL.

retry::retry({
  # original code to compose an email,
  # add some attachments and then send 
},
until = \(val, condition) { is.null(condition) },
max_tries = 10)

I imagine if you change the until function, you could test for the return value e.g.

until = \(val, condition) { !is.na(val) }

Upvotes: 0

G. Grothendieck
G. Grothendieck

Reputation: 269694

1) We could turn it into a function which returns x if it finds one or stops if not. Also we use for instead of while and if instead of ifelse.

retry <- function() {
  for(i in 1:100) {
    rand <- runif(1)
    x <- if (rand > 0.3) rand else NA
    if (!is.na(x)) return(x)
  }
  stop("x is NA")
}

retry()

2) or if you don't want the stop in the function then remove the stop line replacing it with a line that returns x and then use this (although it does involve testing x for NA twice):

x <- retry()
if (is.na(x)) stop("x is NA")

3) or another option is to pass the bad value to the function. Because of lazy evaluation the bad argument is only evaluated if it is, in fact, bad:

retry2 <- function(bad) {
  for(i in 1:100) {
    rand <- runif(1)
    x <- if (rand > 0.3) rand else NA
    if (!is.na(x)) return(x)
  }
  bad
}

retry2(stop("x is NA"))

4) If you don't mind testing x for NA twice using break also works even without a function:

for(i in 1:100) {
  rand <- runif(1)
  x <- if (rand > 0.3) rand else NA
  if (!is.na(x)) break
}
if (is.na(x)) stop("x is NA")
x

Upvotes: 2

Related Questions