处理tryCatch中的多个错误

Bru*_*iro 5 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)
    }
  )
}
Run Code Online (Sandbox Code Playgroud)

我需要获得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)
      }
  )
}
Run Code Online (Sandbox Code Playgroud)

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

非常感谢您的帮助。

编辑

在Martin明确解释之后,我尝试将所有内容放入函数中。没用

如果错误是:

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

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

You have exceeded your daily request quota for this API.
Run Code Online (Sandbox Code Playgroud)

它递归地返回错误,而不是执行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.
Run Code Online (Sandbox Code Playgroud)

我只是被困住了。

Mar*_*gan 4

有两种方法可以捕获错误类型,这取决于错误是如何生成的。这是一个产生错误的函数

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)
        }
    )
}
Run Code Online (Sandbox Code Playgroud)

如果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
Run Code Online (Sandbox Code Playgroud)

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

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)
        }
    })
}
Run Code Online (Sandbox Code Playgroud)

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

> g("a")
found type 'a' error: oops a: type 1 error
> g("b")
found type 'b' error: oops b: type 2 error
Run Code Online (Sandbox Code Playgroud)

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

> g("c")
found custom_error: oop 2: custom error class
Run Code Online (Sandbox Code Playgroud)

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

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)
    })
}
Run Code Online (Sandbox Code Playgroud)

> h("c")
found custom_error: oop 2: custom error class
found an error: oop 2: custom error class
Error: oop 2: custom error class
Run Code Online (Sandbox Code Playgroud)

很少有软件包实际上使用了产生自定义错误的功能,因此您可能会一直试图清除所看到的错误。对于你的情况,看来你必须刮,所以

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
    }
})
Run Code Online (Sandbox Code Playgroud)