当结果不明显时如何使用tryCatch

时间:2018-04-25 18:53:24

标签: r function error-handling try-catch

我有一个函数,它返回一个带有两个变量的数据帧。举个简单的例子,我们有:

test <- function(x) {y <- matrix( 5 , nrow= x , ncol =  2)
                    z<- data.frame(y) 
                    return(z) }

我想找出这个函数给出错误的x值。 (在我们的例子中,我认为负值,但我只是想传达这个概念。)所以我尝试:

z <- rep(0)
testnumbers <- c(0,1,2,3,4,-1,5)

for (i in 1:length(testnumbers)) {

  tempo <- tryCatch( testfun(testnumbers[i]) , error= function(e) return(0) )

  if (tempo == 0 )  z[i] <- {testnumbers[i] next}

}

我的流程有什么问题,如何找到我的功能无效的地方?

3 个答案:

答案 0 :(得分:2)

如果你想要运行testnumbers所有而不管其中任何一个失败,我建议稍微不同。

基础R

这借鉴了Rui对inherits的使用,它更加强大和明确。它更进一步,不仅保留哪一个有错误,还保留实际的错误文本:

testfun <- function(x) {
    y <- matrix(5, nrow = x, ncol = 2)
    z <- as.data.frame(y)
    z
}
testnumbers <- c(0, 1, 2, 3, 4, -1, 5)

rets <- setNames(
  lapply(testnumbers, function(n) tryCatch(testfun(n), error=function(e) e)),
  testnumbers
)

sapply(rets, inherits, "error")
#     0     1     2     3     4    -1     5 
# FALSE FALSE FALSE FALSE FALSE  TRUE FALSE 
Filter(function(a) inherits(a, "error"), rets)
# $`-1`
# <simpleError in matrix(5, nrow = x, ncol = 2): invalid 'nrow' value (< 0)>

setNames(lapply(...), ...)是因为输入是数字,所以sapply(..., simplify=F)没有保留名称,我认为这很重要。)

所有这一切都与一些人认为好的做法一致:如果你正在为很多“事情”做一个功能,那么就在list中进行,因此在{*apply中进行1}}函数。

tidyverse

purrr中有一个函数可以将其形式化为safely,它返回一个围绕其参数的函数。例如:

library(purrr)
safely(testfun)
# function (...) 
# capture_error(.f(...), otherwise, quiet)
# <environment: 0x0000000015151d90>

它返回一个可以传递的函数。一次性通话看起来像下列之一:

safely(testfun)(0)
# $result
# [1] V1 V2
# <0 rows> (or 0-length row.names)
# $error
# NULL
testfun_safe <- safely(testfun)
testfun_safe(0)
# $result
# [1] V1 V2
# <0 rows> (or 0-length row.names)
# $error
# NULL

要在此处使用,您可以:

rets <- setNames(
  lapply(testnumbers, safely(testfun)),
  testnumbers
)
str(rets[5:6])
# List of 2
#  $ 4 :List of 2
#   ..$ result:'data.frame':    4 obs. of  2 variables:
#   .. ..$ V1: num [1:4] 5 5 5 5
#   .. ..$ V2: num [1:4] 5 5 5 5
#   ..$ error : NULL
#  $ -1:List of 2
#   ..$ result: NULL
#   ..$ error :List of 2
#   .. ..$ message: chr "invalid 'nrow' value (< 0)"
#   .. ..$ call   : language matrix(5, nrow = x, ncol = 2)
#   .. ..- attr(*, "class")= chr [1:3] "simpleError" "error" "condition"
Filter(Negate(is.null), sapply(rets, `[[`, "error"))
# $`-1`
# <simpleError in matrix(5, nrow = x, ncol = 2): invalid 'nrow' value (< 0)>

并获得所有运行的结果(包括错误运行):

str(sapply(rets, `[[`, "result"))
# List of 7
#  $ 0 :'data.frame':   0 obs. of  2 variables:
#   ..$ V1: num(0) 
#   ..$ V2: num(0) 
#  $ 1 :'data.frame':   1 obs. of  2 variables:
#   ..$ V1: num 5
#   ..$ V2: num 5
#  $ 2 :'data.frame':   2 obs. of  2 variables:
#   ..$ V1: num [1:2] 5 5
#   ..$ V2: num [1:2] 5 5
#  $ 3 :'data.frame':   3 obs. of  2 variables:
#   ..$ V1: num [1:3] 5 5 5
#   ..$ V2: num [1:3] 5 5 5
#  $ 4 :'data.frame':   4 obs. of  2 variables:
#   ..$ V1: num [1:4] 5 5 5 5
#   ..$ V2: num [1:4] 5 5 5 5
#  $ -1: NULL
#  $ 5 :'data.frame':   5 obs. of  2 variables:
#   ..$ V1: num [1:5] 5 5 5 5 5
#   ..$ V2: num [1:5] 5 5 5 5 5

或只是未运行失败的结果:

str(Filter(Negate(is.null), sapply(rets, `[[`, "result")))
# List of 6
#  $ 0:'data.frame':    0 obs. of  2 variables:
#   ..$ V1: num(0) 
#   ..$ V2: num(0) 
#  $ 1:'data.frame':    1 obs. of  2 variables:
#   ..$ V1: num 5
#   ..$ V2: num 5
#  $ 2:'data.frame':    2 obs. of  2 variables:
#   ..$ V1: num [1:2] 5 5
#   ..$ V2: num [1:2] 5 5
#  $ 3:'data.frame':    3 obs. of  2 variables:
#   ..$ V1: num [1:3] 5 5 5
#   ..$ V2: num [1:3] 5 5 5
#  $ 4:'data.frame':    4 obs. of  2 variables:
#   ..$ V1: num [1:4] 5 5 5 5
#   ..$ V2: num [1:4] 5 5 5 5
#  $ 5:'data.frame':    5 obs. of  2 variables:
#   ..$ V1: num [1:5] 5 5 5 5 5
#   ..$ V2: num [1:5] 5 5 5 5 5

答案 1 :(得分:1)

你其实很亲密。我不知道到底是什么伎俩,但我

  1. 更改了1:length(testnumbers),因为这是不必要的
  2. return(0)更改为字符
  3. 如果长度大于1或无法评估,如果它仍然失败,则将你的if包裹在另一个中。
  4. 然后你得到了正确的结果。您可以尝试逐位更改代码以查看错误。

    test <- function(x) {y <- matrix( 5 , nrow = x , ncol =  2)
    z<- data.frame(y) 
    return(z) }
    
    errored <- numeric()
    testnumbers <- c(0,1,2,3,4,-1,5)
    
    for (i in testnumbers) {      
      tempo <- tryCatch(test(i), error = function(e) "error")      
      if (length(tempo) == 1) {
        if (tempo == "error")  errored <- c(errored, i)
      }      
    }
    errored
    > -1
    

答案 2 :(得分:1)

您需要tryCatch才能返回错误,而不是零。

testfun <- function(x) {
    y <- matrix(5, nrow = x, ncol = 2)
    z <- as.data.frame(y)
    z
}

testnumbers <- c(0, 1, 2, 3, 4, -1, 5)
z <- numeric(length(testnumbers))

for (i in seq_along(testnumbers)) {
    tempo <- tryCatch(testfun(testnumbers[i]), error = function(e) e)
    if (inherits(tempo, "error")) {
        z[i] <- testnumbers[i]
    }
}

z
#[1]  0  0  0  0  0 -1  0

另外,

  • 为了强制使用matrix data.frame使用as.data.frame
  • 我已删除对return的调用,因为函数的最后一个值是其返回值。
  • rep(0)0相同,由numeric(length(testnumbers))取代。
  • seq_along(testnumbers)总是优于1:length(testnumbers)。尝试使用长度为零的testnumbers,看看会发生什么。