RでtryCatch(または同様のもの)をループとして使用する方法、または警告引数のexprを操作する方法はありますか?
回帰モデル(lm
またはglm
またはlmer
...)があり、ループ内fitmodel <- lm(inputs)
でinputs
変更が行われる場所(式とデータ)を実行します。次に、モデル関数が警告を生成しない場合は保持したいのですfitmodel
が、警告が表示された場合update
はモデルを作成し、警告を出力しないようにしたいので、fitmodel <- lm(inputs)
内部で実行しますtryCatch
。したがって、警告が発生した場合、内部warning = function(w){f(fitmodel)}
はf(fitmodel)
次のようになります。
fitmodel <- update(fitmodel, something suitable to do on the model)
実際、この割り当てはif-else
、警告if(w$message satisfies something)
に応じてsuitable to do on the model
内部を適応させるような方法で構造内にありupdate
ます。
問題は私が得ることError in ... object 'fitmodel' not found
です。を使用するwithCallingHandlers
とinvokeRestarts
、モデルの計算が終了し、警告が表示されなくupdate
なります。fitmodel <- lm(inputs)
内部something suitable to do on the model
に再度追加すると、警告が出力されます。今は試すことができると思いますsuppresswarnings(fitmodel <- lm(inputs))
が、それでも、2倍の行を追加しfitmodel <- lm(inputs)
、すべての計算(内部expr
と内部warning
)を2倍にする必要があるため、エレガントな解決策ではないと思います。
要約すると、私が望むが失敗するのは:
tryCatch(expr = {fitmodel <- lm(inputs)},
warning = function(w) {if (w$message satisfies something) {
fitmodel <- update(fitmodel, something suitable to do on the model)
} else if (w$message satisfies something2){
fitmodel <- update(fitmodel, something2 suitable to do on the model)
}
}
)
私に何ができる?
質問のループ部分は、次のように思ったためです(別の質問かもしれませんが、今のところここに残しておきます):update
別の警告が表示された後、次のようにすることがありwhile(get a warning on update){update}
ます。ある意味で、このupdate
内部warning
はとしても理解されるべきexpr
です。このようなことは可能ですか?
どうもありがとうございました!
最小限の例を含む質問の一般的なバージョン:
を持っていて、tryCatch(expr = {result <- operations}, warning = function(w){f(...)}
で警告が表示された場合expr
(実際にはで生成されますoperations
)result
、で何かを実行したいので、実行しますが、warning = function(w){f(result)}
その後は取得しError in ... object 'result' not found
ます。
最小限の例:
y <- "a"
tryCatch(expr = {x <- as.numeric(y)},
warning = function(w) {print(x)})
Error in ... object 'x' not found
私が使用してみましたwithCallingHandlers
代わりにtryCatch
成功せず、また、使用してinvokeRestart
、私は警告を取得するときに何をしたいのか、ではないが、それは表現の一部を行います。
私たちを手伝ってくれますか?
ありがとうございました!
回答
問題は、基本的に、割り当てが行われる前にハンドラーが呼び出されることです。そうでない場合でも、ハンドラーは式とは異なるスコープで実行されるtryCatch
ため、ハンドラーは他のスコープの名前にアクセスできません。
処理を値の変換から分離する必要があります。
エラー(警告ではない)の場合、ベースRは、この効果を実現するためにtry
ラップtryCatch
する関数を提供します。ただし、try
戻り値の型が正しくないため、使用はお勧めしません。1 ekoamの回答で述べたように、「purrr」は、同様の効果を実現するために、適切に型指定された機能ラッパー(eg safely
)を提供します。
ただし、独自に作成することもできます。これは、この状況により適している可能性があります。
with_warning = function (expr) {
self = environment()
warning = NULL
result = withCallingHandlers(expr, warning = function (w) {
self$warning = w
tryInvokeRestart('muffleWarning')
})
list(result = result, warning = warning)
}
これにより、結果値と警告を区別するラッパーが得られます。これで、これを使用して要件を実装できます。
fitmodel = with(with_warning(lm(inputs)), {
if (! is.null(warning)) {
if (conditionMessage(warning) satisfies something) {
update(result, something suitable to do on the model)
} else {
update(result, something2 suitable to do on the model)
}
} else {
result
}
})
1これが意味するのは、try
の戻り値の型は、タイプのエラー値と非エラー値を区別しないということtry-error
です。これは、たとえば、複数のtry
呼び出しをネストするときに発生する可能性がある実際の状況です。
関数呼び出しの戻り値と副作用の両方をキャプチャする関数ラッパーを探しているようです。purrr::quietly
この種のタスクには最適な候補だと思います。このようなことを考えてください
quietly <- purrr::quietly
foo <- function(x) {
if (x < 3)
warning(x, " is less than 3")
if (x < 4)
warning(x, " is less than 4")
x
}
update_foo <- function(x, y) {
x <- x + y
foo(x)
}
keep_doing <- function(inputs) {
out <- quietly(foo)(inputs)
repeat {
if (length(out$warnings) < 1L) return(out$result)
cat(paste0(out$warnings, collapse = ", "), "\n") # This is for you to see the process. You can delete this line. if (grepl("less than 3", out$warnings[[1L]])) {
out <- quietly(update_foo)(out$result, 1.5) } else if (grepl("less than 4", out$warnings[[1L]])) {
out <- quietly(update_foo)(out$result, 1)
}
}
}
出力
> keep_doing(1)
1 is less than 3, 1 is less than 4
2.5 is less than 3, 2.5 is less than 4
[1] 4
> keep_doing(3)
3 is less than 4
[1] 4
次のようなものをお探しですか?で実行するy <- "123"
と、"OK"
メッセージが出力されます。
y <- "a"
#y <- "123"
x <- tryCatch(as.numeric(y),
warning = function(w) w
)
if(inherits(x, "warning")){
message(x$message)
} else{
message(paste("OK:", x))
}
上記のコードを関数として書き直して、いくつかの引数値をテストする方が簡単です。
testWarning <- function(x){
out <- tryCatch(as.numeric(x),
warning = function(w) w
)
if(inherits(out, "warning")){
message(out$message)
} else{
message(paste("OK:", out))
}
invisible(out)
}
testWarning("a")
#NAs introduced by coercion
testWarning("123")
#OK: 123
たぶん、あなたx
は取り扱い条件で再び割り当てることができますか?
tryCatch(
warning = function(cnd) {
x <- suppressWarnings(as.numeric(y))
print(x)},
expr = {x <- as.numeric(y)}
)
#> [1] NA
おそらく最もエレガントな答えではありませんが、おもちゃの例を解決します。
割り当てをtryCatch
通話に入れないで、外に置いてください。例えば、
y <- "a"
x <- tryCatch(expr = {as.numeric(y)},
warning = function(w) {y})
これはに割り当てy
られx
ますが、警告本文に何でも入れることができ、結果はに割り当てられx
ます。
「欲しいもの」の例は、expr
値にアクセスしたいのでもっと複雑ですが、警告が生成された時点ではどこにも割り当てられていません。私はあなたがそれを再計算する必要があると思います:
fitmodel <- tryCatch(expr = {lm(inputs)},
warning = function(w) {if (w$message satisfies something) { update(lm(inputs), something suitable to do on the model) } else if (w$message satisfies something2){
update(lm(inputs), something2 suitable to do on the model)
}
}
)
追加するために編集:
警告を処理する前に評価を完了させるために、を使用することはできませんtryCatch
。evaluate
パッケージは、(とも呼ばれる機能を有しevaluate
、これを行うことができます)。例えば、
y <- "a"
res <- evaluate::evaluate(quote(x <- as.numeric(y)))
for (i in seq_along(res)) {
if (inherits(res[[i]], "warning") &&
conditionMessage(res[[i]]) == gettext("NAs introduced by coercion",
domain = "R"))
x <- y
}
いくつかの注意:res
リストには、メッセージ、警告、エラーなど、さまざまなものが含まれます。私のコードは警告のみを調べます。以前conditionMessage
は警告メッセージを抽出していましたが、現地の言語に翻訳されるのでgettext
、比較のために英語版のメッセージを翻訳するために使用する必要があります。