【问题标题】:How to skip an error in a loop如何跳过循环中的错误
【发布时间】:2012-12-13 03:17:39
【问题描述】:

我想在循环中跳过一个错误(如果有的话)并继续下一次迭代。我想计算 100 个 2 x 2 矩阵的逆矩阵,其中元素从 {0, 1, 2} 中随机采样。可以有一个奇异矩阵(例如,

1 0
2 0

这是我的代码

set.seed(1)
count <- 1
inverses <- vector(mode = "list", 100)
repeat {
    x <- matrix(sample(0:2, 4, replace = T), 2, 2)
    inverses[[count]] <- solve(x)
    count <- count + 1
    if (count > 100) break
}

在第三次迭代中,矩阵是奇异的,代码停止运行并显示错误消息。在实践中,我想绕过这个错误并继续下一个循环。我知道我需要使用trytryCatch 函数,但我不知道如何使用它们。这里也有人问过类似的问题,但它们都非常复杂,答案远远超出了我的理解。如果有人可以给我一个专门针对这个问题的完整代码,我真的很感激。

【问题讨论】:

    标签: r error-handling try-catch


    【解决方案1】:

    对于奇异矩阵,这会将NULLs 放入inverses

    inverses[[count]] <- tryCatch(solve(x), error=function(e) NULL)
    

    如果调用tryCatch 中的第一个表达式引发错误,它将执行并返回提供给其error 参数的函数的值。提供给error arg 的函数必须将错误本身作为参数(这里我称之为e),但您不必对它做任何事情。

    然后您可以使用inverses[! is.null(inverses)] 删除NULL 条目。

    或者,您可以使用较低级别的try。选择真的是品味问题。

    count <- 0
    repeat {
        if (count == 100) break
        count <- count + 1
        x <- matrix(sample(0:2, 4, replace = T), 2, 2)
        x.inv <- try(solve(x), silent=TRUE)
        if ('try-error' %in% class(x.inv)) next
        else inverses[[count]] <- x.inv
    }
    

    如果您的表达式产生错误,try 将返回一个类为 try-error 的对象。如果silent=FALSE,它将把消息打印到屏幕上。在这种情况下,如果x.inv 具有类try-error,我们调用next 来停止当前迭代的执行并移动到下一个迭代,否则我们将x.inv 添加到inverses

    编辑:

    您可以避免将repeat 循环与replicatelapply 一起使用。

    matrices <- replicate(100, matrix(sample(0:2, 4, replace=T), 2, 2), simplify=FALSE)
    inverses <- lapply(matrices, function(mat) if (det(mat) != 0) solve(mat))
    

    有趣的是,replicate 的第二个参数被视为expression,这意味着它会在每次复制时重新执行。这意味着您可以使用replicate 来生成从同一表达式生成的任意数量的随机对象的list

    【讨论】:

    • 有没有使用tryCatch函数或者其他调试函数的解决方案?我只是有一个类似的问题应该跳过一些东西。更复杂的是我找不到数学解决方案来解决它,所以我只想使用一些调试功能立即修复它。
    【解决方案2】:

    您可以使用函数det 简单地计算矩阵的行列式,而不是使用tryCatch。矩阵是奇异的当且仅当行列式为零。

    因此,您可以测试行列式是否不同于零,并仅在测试为正时才计算逆:

    set.seed(1)
    count <- 1
    inverses <- vector(mode = "list", 100)
    repeat {
      x <- matrix(sample(0:2, 4, replace = T), 2, 2)
      # if (det(x)) inverses[[count]] <- solve(x)
      # a more robust replacement for the above line (see comment):
      if (is.finite(determinant(x)$modulus)) inverses[[count]] <- solve(x)
      count <- count + 1
      if (count > 100) break
    }
    

    更新

    但是,可以避免生成奇异矩阵。 2×2 矩阵mat 的行列式定义为mat[1] * mat[4] - mat[3] * mat[2]。您可以使用这些知识对随机数进行抽样。只是不要对会产生奇异矩阵的数字进行采样。当然,这取决于之前采样的数字。

    set.seed(1)
    count <- 1
    inverses <- vector(mode = "list", 100)
    
    set <- 0:2 # the set of numbers to sample from
    
    repeat {
    
      # sample the first value
      x <- sample(set, 1)
      # if the first value is zero, the second and third one are not allowed to be zero.
      new_set <- ifelse(x == 0, setdiff(set, 0), set)
      # sample the second and third value
      x <- c(x, sample(new_set, 2, replace = T))
      # calculate which 4th number would result in a singular matrix
      not_allowed <- abs(-x[3] * x[2] / x[1])
      # remove this number from the set
      new_set <- setdiff(0:2, not_allowed)
      # sample the fourth value and build the matrix
      x <- matrix(c(x, sample(new_set, 1)), 2, 2)
    
      inverses[[count]] <- solve(x)
      count <- count + 1
      if (count > 100) break
    }
    

    此过程保证所有生成的矩阵都有逆矩阵。

    【讨论】:

    • 在某些情况下,行列式为零(或者如果矩阵很大,则非常接近零)的矩阵仍然可以具有逆矩阵。例如,A &lt;- diag(rep(0.000000000001,1000)) det(A)。但 A 是可逆的。
    • 我其实编了一个简单的例子。真正的问题要复杂得多,但它也涉及取大矩阵的逆,有时solve 会给我错误消息。无法按照您的更新获取非奇异矩阵。
    • @PatrickLi 如果矩阵是可逆的,它的行列式必须不为零。在您的示例中,A 是一个非奇异矩阵,但由于小数字带来的舍入问题,函数det(A) 返回零。如果您查看determinant(A),您会看到(对数)行列式不为零。因此,比det(A) == 0 更好的测试是is.finite(determinant(A)$modulus)
    【解决方案3】:

    try 只是告诉R 的一种方式:“如果您在以下括号内犯了错误,请跳过它并继续。”

    所以如果你担心x &lt;- matrix(sample(0:2, 4, replace = T), 2, 2) 可能会给你一个错误,那么你所要做的就是:

    try(x <- matrix(sample(0:2, 4, replace = T), 2, 2))
    

    但是,请记住,如果您这样做,x 将是未定义的,并且最终无法计算答案。当您到达 solve(x) 时,这可能会导致问题 - 因此您可以在 try 之前定义 x 或者只是“尝试”整个事情:

    try(
          {
          x <- matrix(sample(0:2, 4, replace = T), 2, 2)
          inverses[[count]] <- solve(x)
          }
        )
    

    【讨论】:

    • 你可以将调用的x outside分配给try,如:x &lt;- try(matrix(.etc.)) 然后测试x的类
    【解决方案4】:

    The documentation for try 很好地解释了您的问题。我建议你彻底看完。

    Edit: 文档示例看起来非常简单,与操作员的问题非常相似。 (还是)感谢你的建议。以下是文档页面中示例的答案:

    # `idx` is used as a dummy variable here just to illustrate that
    # all 100 entries are indeed calculated. You can remove it.
    set.seed(1)
    mat_inv <- function(idx) {
        print(idx)
        x <- matrix(sample(0:2, 4, replace = T), nrow = 2)
        solve(x)
    }
    inverses <- lapply(1:100, function(idx) try(mat_inv(idx), TRUE))
    

    【讨论】:

    • 建议彻底阅读文档并不是答案。
    • 我没有投反对票,但您只是提到了try 的一般功能而没有解决他的问题
    猜你喜欢
    • 1970-01-01
    • 2013-01-22
    • 1970-01-01
    • 2018-04-14
    • 1970-01-01
    • 2021-06-16
    • 1970-01-01
    • 2015-08-20
    • 1970-01-01
    相关资源
    最近更新 更多