Je veux ignorer une erreur (s'il y en a) dans une boucle et continuer l'itération suivante. Je veux calculer 100 matrices inverses d'une matrice 2 par 2 avec des éléments échantillonnés au hasard à partir de {0, 1, 2}. Il est possible d'avoir une matrice singulière (par exemple,
1 0
2 0
Voici mon 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
}
À la troisième itération, la matrice est singulière et le code cesse de fonctionner avec un message d'erreur. En pratique, je voudrais contourner cette erreur et passer à la boucle suivante. Je sais que je dois utiliser une fonction try
ou tryCatch
mais je ne sais pas comment les utiliser. Des questions similaires ont été posées ici, mais elles sont toutes très compliquées et les réponses sont bien au-delà de ma compréhension. Si quelqu'un peut me donner un code complet spécifiquement pour cette question, je l'apprécie vraiment.
Cela mettrait NULL
s dans inverses
pour les matrices singulières:
inverses[[count]] <- tryCatch(solve(x), error=function(e) NULL)
Si la première expression d'un appel à tryCatch
déclenche une erreur, elle s'exécute et renvoie la valeur de la fonction fournie à son argument error
. La fonction fournie à l'argument error
doit prendre l'erreur elle-même comme argument (ici je l'appelle e
), mais vous n'avez rien à faire avec.
Vous pouvez ensuite supprimer les entrées NULL
avec inverses[! is.null(inverses)]
.
Vous pouvez également utiliser le niveau inférieur try
. Le choix est vraiment une question de goût.
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
}
Si votre expression génère une erreur, try
renvoie un objet avec la classe try-error
. Il imprimera le message à l'écran si silent=FALSE
. Dans ce cas, si x.inv
A la classe try-error
, Nous appelons next
pour arrêter l'exécution de l'itération en cours et passer à la suivante, sinon nous ajoutons x.inv
à inverses
.
Vous pourriez éviter d'utiliser la boucle repeat
avec replicate
et 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))
Il est intéressant de noter que le deuxième argument de replicate
est traité comme un expression
, ce qui signifie qu'il est exécuté à nouveau pour chaque réplication. Cela signifie que vous pouvez utiliser replicate
pour créer un list
d'un nombre quelconque d'objets aléatoires générés à partir de la même expression.
Au lieu d'utiliser tryCatch
, vous pouvez simplement calculer le déterminant de la matrice avec la fonction det
. Une matrice est singulière si et seulement si le déterminant est nul.
Par conséquent, vous pouvez tester si le déterminant est différent de zéro et calculer l'inverse uniquement si le test est positif:
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
}
Mise à jour:
Il est cependant possible d'éviter de générer des matrices singulières. Le déterminant d'une matrice 2 par 2 mat
est défini comme mat[1] * mat[4] - mat[3] * mat[2]
. Vous pouvez utiliser ces connaissances pour échantillonner des nombres aléatoires. N'échantillonnez simplement pas les nombres qui produiront une matrice singulière. Cela, bien sûr, dépend des nombres échantillonnés auparavant.
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
}
Cette procédure est une garantie que toutes les matrices générées auront un inverse.
try
n'est qu'un moyen de dire à R
: "Si vous commettez une erreur entre les parenthèses suivantes, sautez-la et continuez."
Donc, si vous craignez que x <- matrix(sample(0:2, 4, replace = T), 2, 2)
ne vous donne une erreur, alors tout ce que vous avez à faire est de:
try(x <- matrix(sample(0:2, 4, replace = T), 2, 2))
Cependant, gardez à l'esprit que x
ne sera pas défini si vous le faites et qu'il finit par ne pas être en mesure de calculer la réponse. Cela pourrait provoquer un problème lorsque vous arrivez à solve(x)
- vous pouvez donc soit définir x
avant try
ou simplement "essayer" le tout:
try(
{
x <- matrix(sample(0:2, 4, replace = T), 2, 2)
inverses[[count]] <- solve(x)
}
)
La documentation pour essayer explique assez bien votre problème. Je vous suggère de le parcourir complètement.
Edit:
L'exemple de documentation semblait assez simple et très similaire à la question de l'op. Merci pour la suggestion. Voici la réponse en suivant l'exemple de la page de documentation:
# `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))