处理tryCatch中的多个错误

时间:2018-06-29 22:04:42

标签: r try-catch

我正在尝试在for循环中处理两个可能的错误,该循环调用stplanr::dist_google与API进行交互。我知道这些错误,因此我想在发生错误时采取具体措施。

如果我仅尝试处理一种可能的错误,则它可以起作用:

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)
    }
  )
}

我需要获得2500多个观察结果。然后,自动使用两个API。我试图将其他信息和一些小技巧作为一种动作。当我调用循环时,我反复收到两条错误消息。

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)
      }
  )
}

我对tryCatch感到困惑。我在做什么错了?

非常感谢您的帮助。

编辑

在马丁明确解释之后,我试图将所有内容放入函数中。它不起作用。

如果错误是:

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

然后函数将继续并返回一个空对象。配额结束且错误为:

You have exceeded your daily request quota for this API.

它递归地返回一个错误,而不是执行n <- n + 1并继续循环:

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.

我只是被困住了。

1 个答案:

答案 0 :(得分:3)

有两种捕获错误类型的方法,这取决于错误的产生方式。这是一个会产生错误的函数

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)
        }
    )
}

如果type是“ a”或“ b”,则该函数会生成标准错误条件,但带有不同的条件消息(“ oops a:...”和“ oops b:...”) 。如果type为“ c”,则该错误具有特定的类“ custom_error”,该类扩展了标准错误的(S3)类。

> 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

这是一个捕获错误的函数。

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)
        }
    })
}

tryCatch的处理程序参数按照在参数列表中出现的顺序进行测试,如果它们与错误的类匹配,则对它们进行评估。类型“ a”或“ b”的错误具有相同的类,因此由同一处理程序捕获;区分它们的唯一选择是“抓取”错误消息以查看条件(错误)消息,以确定一个人正在处理的错误的类型(希望程序包不那么复杂以至于包括错误消息翻译,例如base R可以,因为通常不可能以健壮的方式抓取翻译后的错误...)

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

另一方面,处理程序可以捕获类型“ c”,因为它具有自己的类。所以...

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

一个人实际上可以沿着错误处理程序或向上调用堆栈传递错误

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)
    })
}

使用

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

很少有软件包实际使用犯自定义错误的功能,因此您可能会想尝试抓取您看到的错误。对于您的情况,看来您必须抓取,所以

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
    }
})