Как пропустить ошибку в цикле
Я хочу пропустить ошибку (если она есть) в цикле и продолжить следующую итерацию. Я хочу вычислить 100 обратных матриц матрицы 2 на 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
}
На третьей итерации матрица является сингулярной, и код перестает работать с сообщением об ошибке. На практике я хотел бы обойти эту ошибку и перейти к следующему циклу. Я знаю, что мне нужно использовать функцию try
или tryCatch
, но я не знаю, как их использовать. Здесь были заданы аналогичные вопросы, но все они очень сложны, и ответы далеки от моего понимания. Если кто-то может дать мне полный код специально для этого вопроса, я действительно ценю его.
Ответы
Ответ 1
Это положило бы NULL
в inverses
для сингулярных матриц:
inverses[[count]] <- tryCatch(solve(x), error=function(e) NULL)
Если первое выражение в вызове tryCatch
вызывает ошибку, оно выполняет и возвращает значение функции, предоставленной ее аргументу error
. Функция, предоставляемая аргументу error
, должна принять ошибку как аргумент (здесь я называю это 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
в 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
для создания list
любого числа случайных объектов, которые генерируются из одного и того же выражения.
Ответ 2
Вместо использования 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
}
Эта процедура является гарантией того, что все сгенерированные матрицы будут иметь обратный.
Ответ 3
try
- это просто способ сообщить R
: "Если вы совершаете ошибку внутри следующих скобок, пропустите ее и перейдите".
Итак, если вы опасаетесь, что x <- matrix(sample(0:2, 4, replace = T), 2, 2)
может дать вам сообщение об ошибке, то все, что вам нужно сделать, это:
try(x <- matrix(sample(0:2, 4, replace = T), 2, 2))
Однако, имейте в виду, что x
будет undefined, если вы это сделаете, и он не сможет вычислить ответ. Это может вызвать проблему, когда вы дойдете до solve(x)
- чтобы вы могли определить x
до try
или просто "попробовать" все это:
try(
{
x <- matrix(sample(0:2, 4, replace = T), 2, 2)
inverses[[count]] <- solve(x)
}
)
Ответ 4
Документация для 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))