How to skip an error in a loop

42,956

Solution 1

This would put NULLs into inverses for the singular matrices:

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

If the first expression in a call to tryCatch raises an error, it executes and returns the value of the function supplied to its error argument. The function supplied to the error arg has to take the error itself as an argument (here I call it e), but you don't have to do anything with it.

You could then drop the NULL entries with inverses[! is.null(inverses)].

Alternatively, you could use the lower level try. The choice is really a matter of taste.

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
}

If your expression generates an error, try returns an object with class try-error. It will print the message to screen if silent=FALSE. In this case, if x.inv has class try-error, we call next to stop the execution of the current iteration and move to the next one, otherwise we add x.inv to inverses.

Edit:

You could avoid using the repeat loop with replicate and 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))

It's interesting to note that the second argument to replicate is treated as an expression, meaning it gets executed afresh for each replicate. This means you can use replicate to make a list of any number of random objects that are generated from the same expression.

Solution 2

Instead of using tryCatch you could simply calculate the determinant of the matrix with the function det. A matrix is singular if and only if the determinant is zero.

Hence, you could test whether the determinant is different from zero and calculate the inverse only if the test is positive:

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
}

Update:

It is, however, possible to avoid generating singular matrices. The determinant of a 2-by-2 matrix mat is definded as mat[1] * mat[4] - mat[3] * mat[2]. You could use this knowledge for sampling random numbers. Just do not sample numbers which will produce a singular matrix. This, of course, depends on the numbers sampled before.

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
}

This procedure is a guarantee that all generated matrices will have an inverse.

Solution 3

try is just a way of telling R: "If you commit an error inside the following parentheses, then skip it and move on."

So if you're worried that x <- matrix(sample(0:2, 4, replace = T), 2, 2) might give you an error, then all you have to do is:

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

However, keep in mind then that x will be undefined if you do this and it ends up not being able to compute the answer. That could cause a problem when you get to solve(x) - so you can either define x before try or just "try" the whole thing:

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

Solution 4

The documentation for try explains your problem pretty well. I suggest you go through it completely.

Edit: The documentation example looked pretty straightforward and very similar to the op's question. Thanks for the suggestion though. Here goes the answer following the example in the documentation page:

# `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))
Share:
42,956

Related videos on Youtube

JACKY88
Author by

JACKY88

I am JACKY.

Updated on July 12, 2022

Comments

  • JACKY88
    JACKY88 almost 2 years

    I want to skip an error (if there is any) in a loop and continue the next iteration. I want to compute 100 inverse matrices of a 2 by 2 matrix with elements randomly sampled from {0, 1, 2}. It is possible to have a singular matrix (for example,

    1 0
    2 0
    

    Here is my code

    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
    }
    

    At the third iteration, the matrix is singular and the code stops running with an error message. In practice, I would like to bypass this error and continue to the next loop. I know I need to use a try or tryCatch function but I don't know how to use them. Similar questions have been asked here, but they are all really complicated and the answers are far beyond my understanding. If someone can give me a complete code specifically for this question, I really appreciate it.

  • Matthew Plourde
    Matthew Plourde over 11 years
    A suggestion to read the documentation thoroughly isn't an answer.
  • Señor O
    Señor O over 11 years
    I didn't downvote it, but you just referred to the general function of try without addressing his question
  • JACKY88
    JACKY88 over 11 years
    In some cases, a matrix with determinant of zero (or really close to zero if the matrix is big) can still have an inverse matrix. For example, A <- diag(rep(0.000000000001,1000)) det(A). But A is invertible.
  • JACKY88
    JACKY88 over 11 years
    I actually made up this simple example. The real problem is much more complicated but it also involves taking the inverse of a big matrix and sometimes solve gives me error messages. It is not possible to follow your update to get a non-singular matrix.
  • Sven Hohenstein
    Sven Hohenstein over 11 years
    @PatrickLi If the matrix is invertible, its determinant must be different from zero. In your example, A is a non-singular matrix but due to rounding issues entailed by the small numbers, the function det(A) returns zero. If you have a look at determinant(A), you will see the (log) determinant is different from zero. Hence, a better test than det(A) == 0 would be is.finite(determinant(A)$modulus).
  • Ricardo Saporta
    Ricardo Saporta almost 11 years
    you can assign x outside of the call to try, as in: x <- try(matrix(.etc.)) Then test x's class
  • Leon Smith
    Leon Smith over 3 years
    Is there a solution that use tryCatch function or other debug function? I just had a similar problem that should skip something. What is more comlicated is that I can not find a mathematical solution to solve it, so I would like to just use some debug function to fix it at once.