代码之家  ›  专栏  ›  技术社区  ›  Novic

结果不明确时如何使用tryCatch

  •  0
  • Novic  · 技术社区  · 6 年前

    我有一个函数,它返回一个包含两个变量的数据帧。作为一个简单的示例,我们有:

    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 回复  |  直到 6 年前
        1
  •  2
  •   r2evans    6 年前

    如果你想跑步 全部的 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)>
    

    (The 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
  •   JBGruber    6 年前

    事实上,你们非常接近。我不确定到底是什么耍了这个把戏,但我

    1. 更改了1:长度(testnumbers),因为这不是必需的
    2. 已更改 return(0) 到角色
    3. 如果长度大于1或无法评估,则将if包装在另一个if中,因为它会不断失败。

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

    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
  •  1
  •   Rui Barradas    6 年前

    你需要 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 长度为零,看看会发生什么。