Подтвердить что ты не робот

Как пропустить ошибку в цикле

Я хочу пропустить ошибку (если она есть) в цикле и продолжить следующую итерацию. Я хочу вычислить 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, но я не знаю, как их использовать. Здесь были заданы аналогичные вопросы, но все они очень сложны, и ответы далеки от моего понимания. Если кто-то может дать мне полный код специально для этого вопроса, я действительно ценю его.

4b9b3361

Ответ 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))