RでtryCatch(または同様のもの)をループとして使用する方法、または警告引数のexprを操作する方法はありますか?

Nov 27 2020

回帰モデル(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です。を使用するwithCallingHandlersinvokeRestarts、モデルの計算が終了し、警告が表示されなく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(実際にはで生成されますoperationsresult、で何かを実行したいので、実行しますが、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、私は警告を取得するときに何をしたいのか、ではないが、それは表現の一部を行います。

私たちを手伝ってくれますか?

ありがとうございました!

回答

2 KonradRudolph Nov 28 2020 at 14:24

問題は、基本的に、割り当てが行われる前にハンドラーが呼び出されることです。そうでない場合でも、ハンドラーは式とは異なるスコープで実行される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呼び出しをネストするときに発生する可能性がある実際の状況です。

2 ekoam Nov 27 2020 at 11:37

関数呼び出しの戻り値と副作用の両方をキャプチャする関数ラッパーを探しているようです。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
1 RuiBarradas Nov 27 2020 at 10:38

次のようなものをお探しですか?で実行する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
JoaoPedroMacalos Nov 27 2020 at 10:04

たぶん、あなたxは取り扱い条件で再び割り当てることができますか?

tryCatch(
  warning = function(cnd) {
    x <- suppressWarnings(as.numeric(y))
    print(x)},
  expr = {x <- as.numeric(y)}
)
#> [1] NA

おそらく最もエレガントな答えではありませんが、おもちゃの例を解決します。

user2554330 Nov 27 2020 at 10:05

割り当てを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)

                                }
         }
)

追加するために編集:

警告を処理する前に評価を完了させるために、を使用することはできませんtryCatchevaluateパッケージは、(とも呼ばれる機能を有し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、比較のために英語版のメッセージを翻訳するために使用する必要があります。