我想在循环中跳过错误(如果有的话)并继续下一次迭代。我想计算具有从{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
}
在第三次迭代中,矩阵是单数的,代码停止运行并显示错误消息。在实践中,我想绕过这个错误并继续下一个循环。我知道我需要使用try
或tryCatch
函数,但我不知道如何使用它们。这里也提出了类似的问题,但它们都非常复杂,答案远远超出我的理解范围。如果有人可以给我一个专门针对这个问题的完整代码,我真的很感激。
答案 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
循环与repeat
和replicate
一起使用。
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))