27

我想在循环中跳过一个错误(如果有的话)并继续下一次迭代。我想计算 2×2 矩阵的 100 个逆矩阵,其中元素从 {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
}

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

4

4 回答 4

33

这会将NULLs 放入inverses奇异矩阵中:

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

如果调用中的第一个表达式tryCatch引发错误,它将执行并返回提供给其error参数的函数的值。提供给errorarg 的函数必须将错误本身作为参数(这里我称之为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返回一个带有 class 的对象try-error。如果 .它会将消息打印到屏幕上silent=FALSE。在这种情况下,如果x.inv有 class try-error,我们调用next以停止当前迭代的执行并移动到下一个迭代,否则我们添加x.invinverses.

编辑:

您可以避免将repeat循环与replicateand一起使用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))

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

于 2012-12-27T19:06:11.440 回答
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
}

这个过程保证了所有生成的矩阵都有一个逆矩阵。

于 2012-12-27T19:31:54.143 回答
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)
      }
    )
于 2012-12-27T19:11:34.370 回答
5

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))
于 2012-12-27T18:56:42.627 回答