Czy istnieje sposób używania tryCatch (lub podobnego) w R jako pętli lub manipulowania wyrażeniem w argumencie ostrzeżenia?
Mam modelu regresji ( lm
albo glm
albo lmer
...) i mam fitmodel <- lm(inputs)
gdzie inputs
zmiany wewnątrz pętli (wzoru i danych). Następnie, jeśli funkcja modelu nie generuje żadnego ostrzeżenia, które chcę zachować fitmodel
, ale jeśli otrzymam ostrzeżenie, chcę, aby update
model i nie chciał, aby ostrzeżenie nie było drukowane, więc robię to w fitmodel <- lm(inputs)
środku tryCatch
. Tak więc, jeżeli wytwarza ostrzeżenie, wewnątrz warning = function(w){f(fitmodel)}
, f(fitmodel)
byłoby coś
fitmodel <- update(fitmodel, something suitable to do on the model)
W rzeczywistości to przypisanie byłoby wewnątrz if-else
konstrukcji w taki sposób, że w zależności od ostrzeżenia if(w$message satisfies something)
dostosowałbym suitable to do on the model
wnętrze update
.
Problem w tym, że dostaję Error in ... object 'fitmodel' not found
. Jeśli używam withCallingHandlers
with invokeRestarts
, po prostu kończy obliczenia modelu z ostrzeżeniem bez update
niego. Jeśli dodam ponownie w fitmodel <- lm(inputs)
środku something suitable to do on the model
, zostanie wydrukowane ostrzeżenie; teraz myślę, że mógłbym spróbować suppresswarnings(fitmodel <- lm(inputs))
, ale mimo to uważam, że nie jest to eleganckie rozwiązanie, ponieważ muszę dodać 2 razy linię fitmodel <- lm(inputs)
, wykonując 2 razy wszystkie obliczenia (wewnątrz expr
i wewnątrz warning
).
Podsumowując, chciałbym, ale zawodziło:
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)
}
}
)
Co mogę zrobić?
Część pętli pytania jest taka, ponieważ pomyślałem, że to następuje (może to inne pytanie, ale na razie zostawię je tutaj): może się zdarzyć, że po tym update
otrzymam kolejne ostrzeżenie, więc zrobię coś takiego while(get a warning on update){update}
; w pewnym sensie to update
wnętrze warning
należy również rozumieć jako expr
. Czy coś takiego jest możliwe?
Dziękuję Ci bardzo!
Ogólna wersja pytania z minimalnym przykładem:
Powiedzmy, że mam tryCatch(expr = {result <- operations}, warning = function(w){f(...)}
i jeśli otrzymam ostrzeżenie expr
(wyprodukowane w rzeczywistości operations
), chcę coś zrobić result
, więc zrobiłbym warning = function(w){f(result)}
, ale wtedy otrzymuję Error in ... object 'result' not found
.
Minimalny przykład:
y <- "a"
tryCatch(expr = {x <- as.numeric(y)},
warning = function(w) {print(x)})
Error in ... object 'x' not found
Próbowałem używać withCallingHandlers
zamiast tryCatch
bez powodzenia, a także używać, invokeRestart
ale robi to część wyrażenia, a nie to, co chcę zrobić, gdy otrzymuję ostrzeżenie.
Czy mógłbyś mi pomóc?
Dziękuję Ci!
Odpowiedzi
Zasadniczo problem polega na tym, że procedura obsługi jest wywoływana przed przypisaniem. A nawet gdyby tak nie było, procedura obsługi działa w innym zakresie niż tryCatch
wyrażenie, więc procedura obsługi nie może uzyskać dostępu do nazw w innym zakresie.
Musimy oddzielić obsługę od transformacji wartości.
W przypadku błędów (ale nie ostrzeżeń) podstawa R zapewnia funkcję try
, która zawija, tryCatch
aby osiągnąć ten efekt. Jednak używanie try
jest odradzane, ponieważ jego typ zwracany jest niewłaściwy . 1 Jak wspomniała odpowiedź ekoam , „mruczenie” zapewnia dobrze napisane funkcjonalne opakowania (np. safely
), Aby osiągnąć podobny efekt.
Możemy jednak zbudować również własne, co może lepiej pasować do tej sytuacji:
with_warning = function (expr) {
self = environment()
warning = NULL
result = withCallingHandlers(expr, warning = function (w) {
self$warning = w
tryInvokeRestart('muffleWarning')
})
list(result = result, warning = warning)
}
Daje nam to opakowanie, które rozróżnia wartość wyniku od ostrzeżenia. Możemy teraz użyć go do realizacji Twojego wymagania:
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 Oznacza to, że try
zwracany typ nie rozróżnia błędu od niebędącej błędem wartości typu try-error
. Jest to prawdziwa sytuacja, która może wystąpić na przykład podczas zagnieżdżania wielu try
połączeń.
Wygląda na to, że szukasz opakowania funkcjonalnego, które przechwytuje zarówno zwracaną wartość, jak i efekty uboczne wywołania funkcji. Myślę, że purrr::quietly
jest idealnym kandydatem do tego typu zadań. Rozważ coś takiego
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)
}
}
}
Wynik
> 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
Szukasz czegoś takiego jak poniżej? Jeśli jest uruchamiany z y <- "123"
, "OK"
wiadomość zostanie wydrukowana.
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))
}
Łatwiej jest przetestować kilka wartości argumentów za pomocą powyższego kodu przepisanego jako funkcja.
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
Może mógłbyś x
ponownie przypisać w stanie obsługi?
tryCatch(
warning = function(cnd) {
x <- suppressWarnings(as.numeric(y))
print(x)},
expr = {x <- as.numeric(y)}
)
#> [1] NA
Być może nie jest to najbardziej elegancka odpowiedź, ale rozwiązuje twój przykład zabawki.
Nie umieszczaj zadania w tryCatch
rozmowie, wyślij go na zewnątrz. Na przykład,
y <- "a"
x <- tryCatch(expr = {as.numeric(y)},
warning = function(w) {y})
To przypisuje y
do x
, ale możesz umieścić cokolwiek w treści ostrzeżenia, a wynik zostanie przypisany do x
.
Twój przykład „co chciałbym” jest bardziej skomplikowany, ponieważ chcesz mieć dostęp do expr
wartości, ale nie została ona nigdzie przypisana w momencie generowania ostrzeżenia. Myślę, że będziesz musiał to przeliczyć:
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)
}
}
)
Edytowano, aby dodać:
Aby umożliwić ocenę do końca przed przetworzeniem ostrzeżenia, nie możesz użyć tryCatch
. evaluate
Pakiet posiada funkcję (zwany również evaluate
), że można to zrobić. Na przykład,
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
}
Kilka uwag: res
lista będzie zawierała wiele różnych rzeczy, w tym komunikaty, ostrzeżenia, błędy itp. Mój kod sprawdza tylko ostrzeżenia. Kiedyś conditionMessage
wyodrębniłem komunikat ostrzegawczy, ale zostanie on przetłumaczony na lokalny język, więc należy użyć go gettext
do przetłumaczenia angielskiej wersji komunikatu w celu porównania.