如何跳过循环中的错误

时间:2012-12-27 18:48:01

标签: r error-handling try-catch

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

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函数,但我不知道如何使用它们。这里也提出了类似的问题,但它们都非常复杂,答案远远超出我的理解范围。如果有人可以给我一个专门针对这个问题的完整代码,我真的很感激。

4 个答案:

答案 0 :(得分:29)

对于单数矩阵,这会将NULL放入inverses

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

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

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

或者,您可以使用较低级别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添加到{ {1}}。

编辑:

您可以避免将inverses循环与repeatreplicate一起使用。

lapply

值得注意的是,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从同一表达式生成的任意数量的随机对象。

答案 1 :(得分:9)

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

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

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
}

此过程可保证所有生成的矩阵都具有反转。

答案 2 :(得分:8)

try只是告诉R的一种方式:“如果你在下面的括号内提交错误,那么跳过它然后继续。”

因此,如果您担心x <- matrix(sample(0:2, 4, replace = T), 2, 2)可能会给您一个错误,那么您所要做的就是:

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

然而,请记住,如果您执行此操作并且最终无法计算答案,则x将不确定。当你到达solve(x)时,这可能会导致问题 - 所以你可以在x之前定义try或者只是“尝试”整个事情:

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

答案 3 :(得分:5)

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

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

# `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))