Gibt es eine Möglichkeit, tryCatch (oder ähnliches) in R als Schleife zu verwenden oder den Ausdruck im Warnargument zu manipulieren?
Ich habe ein Regressionsmodell ( lm
oder glm
oder 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 update
das 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-else
Struktur so befinden, dass if(w$message satisfies something)
ich abhängig von der Warnung das suitable to do on the model
Innere anpassen würde update
.
Das Problem ist, dass ich bekomme Error in ... object 'fitmodel' not found
. Wenn ich withCallingHandlers
mit 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 expr
und 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 update
ich 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 update
Innere warning
auch 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, withCallingHandlers
anstatt tryCatch
ohne Erfolg zu verwenden, und auch, invokeRestart
aber es macht den Ausdrucksteil, nicht das, was ich tun möchte, wenn ich eine Warnung bekomme.
Kannst du mir helfen?
Vielen Dank!
Antworten
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 tryCatch
Ausdruck 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 tryCatch
dieser Effekt erzielt wird. Von der Verwendung try
wird 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 try
der 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 try
Anrufe auftreten kann.
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::quietly
ist 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
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
Vielleicht könnten Sie x
im 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.
Setzen Sie die Aufgabe nicht in den tryCatch
Anruf ein, sondern stellen Sie sie nach draußen. Zum Beispiel,
y <- "a"
x <- tryCatch(expr = {as.numeric(y)},
warning = function(w) {y})
Ordnet y
zu 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 expr
Wert 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 evaluate
Paket 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 res
Liste enthält viele verschiedene Dinge, einschließlich Meldungen, Warnungen, Fehler usw. Mein Code berücksichtigt nur die Warnungen. Früher habe ich conditionMessage
die 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.