【问题标题】:How to use tryCatch when the outcome is not distinct当结果不明确时如何使用 tryCatch
【发布时间】:2026-01-31 16:30:01
【问题描述】:

我有一个函数,它返回一个带有两个变量的数据框。作为一个简单的例子,让我们有:

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}

}

我的流程出了什么问题,我如何才能找到我的功能在哪里不起作用?

【问题讨论】:

    标签: r function error-handling try-catch


    【解决方案1】:

    如果您希望运行 所有 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 函数之一中执行.

    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
    

    【讨论】:

      【解决方案2】:

      其实你们很亲近。我不确定到底是什么伎俩,但我

      1. 更改了 1:length(testnumbers),因为这是不必要的
      2. return(0) 更改为字符
      3. 将您的 if 包装在另一个 if 中,因为如果长度大于 1 或无法评估,它会一直失败。

      然后你会得到正确的结果。您可以尝试一点一点地更改代码,看看有什么问题。

      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
      

      【讨论】:

        【解决方案3】:

        您需要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,看看会发生什么。

        【讨论】: