web-dev-qa-db-ja.com

ループでエラーをスキップする方法

ループ内のエラー(ある場合)をスキップして、次の反復を続けたいと思います。 {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
}

3回目の反復で、行列は特異であり、コードはエラーメッセージで実行を停止します。実際には、このエラーをバイパスして次のループに進みたいと思います。 tryまたはtryCatch関数を使用する必要があることは知っていますが、使用方法はわかりません。ここでも同様の質問が出されましたが、それらはすべて本当に複雑であり、答えは私の理解をはるかに超えています。誰かがこの質問に特化した完全なコードを提供してくれれば、本当に感謝しています。

25
Patrick Li

これは、特異行列のNULLsをinversesに入れます:

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

tryCatchの呼び出しの最初の式でエラーが発生した場合、error引数に指定された関数の値を実行して返します。 error argに提供される関数は、エラー自体を引数としてとる必要があります(ここではeと呼びます)が、何もする必要はありません。

その後、inverses[! is.null(inverses)]NULLエントリを削除できます。

または、下位レベル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およびreplicatelapplyループを使用することを避けることができます。

_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の2番目の引数はexpressionとして扱われることに注意してください。これは、レプリケートごとに新たに実行されることを意味します。つまり、replicateを使用して、同じ式から生成される任意の数のランダムオブジェクトのlistを作成できます。

29
Matthew Plourde

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
}

この手順は、生成されたすべての行列に逆行列があることを保証します。

9
Sven Hohenstein

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)
      }
    )
_
8
Señor O

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))
5
Arun