Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handle more than one possible error in tryCatch

Tags:

r

try-catch

I'm trying to handle two possible errors in a for loop, which calls stplanr::dist_google to interact with an API. I know the errors, so I want to take specific actions when they occur.

If I try to handle just one of the possible errors, it works:

data(cents, package = "stplanr")
data(flow, package = "stplanr")

od <- stplanr::od2odf(flow=flow, zones=cents)

uma_linha <- data.frame(from_addresses=NA, to_addresses=NA, distances=NA,
                        duration=NA, currency=NA, fare=NA)
output <- data.frame()

for (linha in 1:nrow(od)) {
  o <- od[linha, 3:4]
  d  <- od[linha, 5:6]
  output <- tryCatch(
    {
      rbind(output, stplanr::dist_google(from = o, to = d,
                                         mode = 'walking'))
    },
    error = function(na) {
      message("Erro: No results for this request (e.g. due to lack of support for this mode between the from and to locations)")
      message(na)
      output <- rbind(output, uma_linha)
    }
  )
}

I need to get results for more than 2500 observations. Then, to automatize the use of two apis. I tried to include the other message and a little trick as an action. When I call the loop, I get the two error messages repeatedly.

n <- 1
apis <- c("api01", "api02", "api03")

for (linha in 1:nrow(od)) {
  o <- od[linha, 3:4]
  d  <- od[linha, 5:6]
  output <- tryCatch(
    {
      rbind(output, stplanr::dist_google(from = o, to = d,
                                         mode = 'walking',
                                         google_api = apis[n]))
    },
    error = function(na) {
      message("Erro: No results for this request (e.g. due to lack of support for this mode between the from and to locations)")
      message(na)
      output <- rbind(output, uma_linha)
      },
    error = function(quota) {
      message("Erro: You have exceeded your daily request quota for this API.")
      message(quota)
      n <- n + 1
      return(n)
      }
  )
}

I'm confused with tryCatch. What am I doing wrong?

Many thanks for any help.

EDIT

After Martin's clear explanation, I tried to put everything inside a function. It's not working.

If the error is:

No results for this request (e.g. due to lack of support for this mode between the from and to locations)

then the function will go ahead and return an empty object. When the quota ends and the error is:

You have exceeded your daily request quota for this API.

it returned an error recursively, instead of performing n <- n + 1 and continued looping:

asha_dists <- function(fluxo, zonas, api) {
  zonas <- as(st_transform(zonas, 4326), "Spatial")
  od <- stplanr::od2odf(flow = fluxo, zones = zonas)
  uma_linha <- data.frame(from_addresses=NA, to_addresses=NA, distances=NA,
                          duration=NA, currency=NA, fare=NA)
  n <- 1
  output <- data.frame()
  for (linha in 1:nrow(od)) {
    o <- od[linha, 3:4]
    d  <- od[linha, 5:6]
    output <- tryCatch(
      {
        rbind(output, stplanr::dist_google(from = o, to = d,
                                           mode = 'walking', google_api = api[n]))
      },
      custom_error = function(e) {
        err <- conditionMessage(e)
        message("found custom_error: ", err)
        output <- rbind(output, uma_linha)
      },
      error = function(e) {
        err <- conditionMessage(e)
        message("found an error: ", err)
        n <- n + 1
      }
    )
  }
  return(output)
}

Sent this request: https://maps.googleapis.com/maps/api/distancematrix/json?units=metric&origins=-23.5678377804732,-46.5708261676873&destinations=-23.5706647015703,-46.5755950203842&mode=walking&key=AIzaSyBRPrAjSE_pRMWSq_XlO4BFwGD63j_gB4U
found an error: You have exceeded your daily request quota for this API.
Sent this request: https://maps.googleapis.com/maps/api/distancematrix/json?units=metric&origins=-23.5665596480444,-46.5682308348154&destinations=-23.5706647015703,-46.5755950203842&mode=walking&key=AIzaSyBRPrAjSE_pRMWSq_XlO4BFwGD63j_gB4U
found an error: You have exceeded your daily request quota for this API.

I'm simply stuck.

like image 419
Bruno Pinheiro Avatar asked Jun 29 '18 22:06

Bruno Pinheiro


People also ask

How do you handle errors in R?

In R Programming, there are basically two ways in which we can implement an error handling mechanism. Either we can directly call the functions like stop() or warning(), or we can use the error options such as “warn” or “warning. expression”.

How do I skip an error in R?

The simplest way of handling conditions in R is to simply ignore them: Ignore errors with try() . Ignore warnings with suppressWarnings() . Ignore messages with suppressMessages() .

Where do you put try catch?

A try catch block is placed around code that could throw an exception. If an exception is thrown, this try catch block will handle the exception to ensure that the application does not cause an unhandled exception, user error, or crash the application.

How does try catch work Javascript?

A catch -block contains statements that specify what to do if an exception is thrown in the try -block. If any statement within the try -block (or in a function called from within the try -block) throws an exception, control is immediately shifted to the catch -block.


1 Answers

There are two ways to catch types of errors, and it depends on how the error is generated. Here's a function that generates errors

f <- function(type) {
    switch(
        type,
        a = stop("oops a: type 1 error"),
        b = stop("oops b: type 2 error"),
        c = {
            err <- simpleError("oop 2: custom error class")
            class(err) <- c("custom_error", class(err))
            stop(err)
        }
    )
}

If type is "a" or "b", then the function generates a standard error condition but with different condition messages ("oops a: ..." and "oops b: ..."). If type is "c", then the error has a particular class "custom_error", that extends the (S3) class of a standard error.

> f("a")
Error in f("a") : oops a: type 1 error
> f("b")
Error in f("b") : oops b: type 2 error
> f("c")
Error: oop 2: custom error class

Here's a function that catches errors.

g <- function(type) {
    tryCatch({
        f(type)
    }, custom_error = function(e) {
        err <- conditionMessage(e)
        message("found custom_error: ", err)
    }, error = function(e) {
        err <- conditionMessage(e)
        if (startsWith(err, "oops a")) {
            message("found type 'a' error: ", err)
        } else if (startsWith(err, "oops b")) {
            message("found type 'b' error: ", err)
        } else {
            message("generic error: ", err)
        }
    })
}

The handler arguments to tryCatch are tested in the order they occur in the argument list, and they are evaluated if they match the class of the error. Errors of type "a" or "b" have the same class, so are caught by the same handler; the only option to differentiate between them is to 'scrape' the error message looking at the condition (error) message to determine the type of error one is dealing with (hopefully the package isn't so sophisticated as to include error message translation, like base R does, because it is generally not possible to scrape translated errors in a robust way...)

> g("a")
found type 'a' error: oops a: type 1 error
> g("b")
found type 'b' error: oops b: type 2 error

On the other hand, type "c" can be caught by the handler because it has it's own class. So...

> g("c")
found custom_error: oop 2: custom error class

One can actually pass errors along the error handlers or up the call stack

h <- function(type) {
    tryCatch({
        f(type)
    }, custom_error = function(e) {
        err <- conditionMessage(e)
        message("found custom_error: ", err)
        stop(e)
    }, error = function(e) {
        err <- conditionMessage(e)
        message("found an error: ", err)
        stop(e)
    })
}

with

> h("c")
found custom_error: oop 2: custom error class
found an error: oop 2: custom error class
Error: oop 2: custom error class

Few packages actually use the ability to make custom errors, so you're probably stuck with trying to scrape the errors that you do see. For your case, it seems like you have to scrape, so

output <- tryCatch({
    rbind(
        output,
        stplanr::dist_google(
            from = o, to = d, mode = 'walking', google_api = api[n]
        )
    )
}, error = function(e) {
    err <- conditionMessage(e)
    if (startsWith("No results for this request", err) {
        warning(err)  # warn instead of error
        n <<- n + 1   # '<<-' to update n _outside_ this function
        rbind(output, uma_linha)  # empty output as result
    } else if (startsWith("You have exceeded your daily", err) {
        stop(e)  # signal the original error
    } else {
        stop(e)  # no error that we know how to handle, signal
    }
})
like image 186
Martin Morgan Avatar answered Oct 15 '22 10:10

Martin Morgan