Gibt es eine Möglichkeit, tryCatch (oder ähnliches) in R als Schleife zu verwenden oder den Ausdruck im Warnargument zu manipulieren?

Nov 27 2020

Ich habe ein Regressionsmodell ( lmoder glmoder lmer...) und mache es, fitmodel <- lm(inputs)wo sich inputsÄnderungen innerhalb einer Schleife (der Formel und der Daten) ergeben. Wenn dann die Modellfunktion erzeugt keine Warnung , die ich behalten wollen fitmodel, aber wenn ich eine Warnung ich will bekommen updatedas Modell , und ich möchte , dass die Warnung nicht gedruckt, so dass ich nach fitmodel <- lm(inputs)innen tryCatch. Wenn es also eine Warnung im Inneren erzeugt warning = function(w){f(fitmodel)}, f(fitmodel)wäre das so etwas wie

fitmodel <- update(fitmodel, something suitable to do on the model)

Tatsächlich würde sich diese Zuordnung innerhalb einer if-elseStruktur so befinden, dass if(w$message satisfies something)ich abhängig von der Warnung das suitable to do on the modelInnere anpassen würde update.

Das Problem ist, dass ich bekomme Error in ... object 'fitmodel' not found. Wenn ich withCallingHandlersmit verwende invokeRestarts, wird die Berechnung des Modells nur mit der Warnung ohne beendet update. Wenn ich wieder fitmodel <- lm(inputs)innen hinzufüge something suitable to do on the model, wird die Warnung gedruckt. Jetzt denke ich, ich könnte es versuchen suppresswarnings(fitmodel <- lm(inputs)), aber ich denke, es ist keine elegante Lösung, da ich das 2-fache der Zeile hinzufügen muss fitmodel <- lm(inputs), was das 2-fache der gesamten Berechnung (innen exprund innen warning) ergibt .

Zusammenfassend möchte ich sagen, was aber scheitert:

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)

                                }
         }
)

Was kann ich tun?

Der Schleifenteil der Frage ist, weil ich dachte, dass es wie folgt ist (vielleicht ist es eine andere Frage, aber im Moment lasse ich sie hier): Es kann passieren, dass updateich nach dem eine weitere Warnung bekomme, also würde ich so etwas tun while(get a warning on update){update}; In gewisser Weise sollte dieses updateInnere warningauch als verstanden werden expr. Ist so etwas möglich?

Vielen Dank!


Generische Version der Frage mit minimalem Beispiel:

Nehmen wir an, ich habe eine tryCatch(expr = {result <- operations}, warning = function(w){f(...)}und wenn ich eine Warnung bekomme expr(tatsächlich in operations), möchte ich etwas damit machen result, also würde ich es tun warning = function(w){f(result)}, aber dann bekomme ich Error in ... object 'result' not found.

Ein minimales Beispiel:

y <- "a"
tryCatch(expr = {x <- as.numeric(y)},
    warning = function(w) {print(x)})
Error in ... object 'x' not found

Ich habe versucht, withCallingHandlersanstatt tryCatchohne Erfolg zu verwenden, und auch, invokeRestartaber es macht den Ausdrucksteil, nicht das, was ich tun möchte, wenn ich eine Warnung bekomme.

Kannst du mir helfen?

Vielen Dank!

Antworten

2 KonradRudolph Nov 28 2020 at 14:24

Grundsätzlich besteht das Problem darin, dass der Handler aufgerufen wird, bevor die Zuweisung erfolgt. Und selbst wenn dies nicht der Fall wäre, wird der Handler in einem anderen Bereich als der tryCatchAusdruck ausgeführt, sodass der Handler nicht auf die Namen im anderen Bereich zugreifen kann.

Wir müssen die Behandlung von der Werttransformation trennen.

Für Fehler (aber keine Warnungen) stellt Basis R die Funktion bereit try, mit der tryCatchdieser Effekt erzielt wird. Von der Verwendung trywird jedoch abgeraten, da der Rückgabetyp nicht korrekt ist . 1 Wie in der Antwort von ekoam erwähnt, bietet 'purrr' gut typisierte funktionale Wrapper (z. B. safely), um einen ähnlichen Effekt zu erzielen.

Wir können jedoch auch unsere eigenen bauen, was in dieser Situation möglicherweise besser passt:

with_warning = function (expr) {
    self = environment()
    warning = NULL

    result = withCallingHandlers(expr, warning = function (w) {
        self$warning = w
        tryInvokeRestart('muffleWarning')
    })
    list(result = result, warning = warning)
}

Dies gibt uns einen Wrapper, der zwischen dem Ergebniswert und einer Warnung unterscheidet. Wir können es jetzt verwenden, um Ihre Anforderung umzusetzen:

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 Dies bedeutet, dass tryder Rückgabetyp nicht zwischen einem Fehler- und einem Nicht-Fehlerwert des Typs unterscheidet try-error. Dies ist eine reale Situation, die beispielsweise beim Verschachteln mehrerer tryAnrufe auftreten kann.

2 ekoam Nov 27 2020 at 11:37

Es scheint, dass Sie nach einem funktionalen Wrapper suchen, der sowohl den zurückgegebenen Wert als auch die Nebenwirkungen eines Funktionsaufrufs erfasst. Ich denke, es purrr::quietlyist ein perfekter Kandidat für diese Art von Aufgabe. Betrachten Sie so etwas

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)
    }
  }
}

Ausgabe

> 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

Suchen Sie so etwas wie das Folgende? Wenn es mit ausgeführt wird y <- "123", wird die "OK"Nachricht gedruckt.

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))
}

Es ist einfacher, mehrere Argumentwerte mit dem oben als Funktion neu geschriebenen Code zu testen.

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

Vielleicht könnten Sie xim Handhabungszustand nochmal zuordnen ?

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

Vielleicht nicht die eleganteste Antwort, löst aber Ihr Spielzeugbeispiel.

user2554330 Nov 27 2020 at 10:05

Setzen Sie die Aufgabe nicht in den tryCatchAnruf ein, sondern stellen Sie sie nach draußen. Zum Beispiel,

y <- "a"
x <- tryCatch(expr = {as.numeric(y)},
    warning = function(w) {y})

Ordnet yzu x, aber man konnte nichts in der Warnung Körper gelegt, und das Ergebnis wird zu zugeordnet werden x.

Ihr Beispiel "Was ich möchte" ist komplizierter, da Sie auf den exprWert zugreifen möchten , es jedoch zum Zeitpunkt der Generierung der Warnung nirgendwo zugewiesen wurde. Ich denke, Sie müssen es neu berechnen:

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)

                                }
         }
)

Bearbeitet, um hinzuzufügen:

Sie können nicht verwenden, damit die Auswertung vor der Verarbeitung der Warnung abgeschlossen werden kann tryCatch. Das evaluatePaket verfügt über eine Funktion (auch aufgerufen evaluate), die dies ausführen kann. Zum Beispiel,

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
}

Einige Hinweise: Die resListe enthält viele verschiedene Dinge, einschließlich Meldungen, Warnungen, Fehler usw. Mein Code berücksichtigt nur die Warnungen. Früher habe ich conditionMessagedie Warnmeldung extrahiert, sie wird jedoch in die Landessprache gettextübersetzt. Sie sollten sie daher verwenden , um die englische Version der Nachricht zum Vergleich zu übersetzen.