Jak napisać trycatch w R.

342

Chcę napisać trycatchkod, aby poradzić sobie z błędem podczas pobierania z sieci.

url <- c(
    "http://stat.ethz.ch/R-manual/R-devel/library/base/html/connections.html",
    "http://en.wikipedia.org/wiki/Xz")
y <- mapply(readLines, con=url)

Te dwie instrukcje działają poprawnie. Poniżej tworzę nieistniejący adres internetowy:

url <- c("xxxxx", "http://en.wikipedia.org/wiki/Xz")

url[1]nie istnieje. Jak napisać trycatchpętlę (funkcję), aby:

  1. Gdy adres URL jest nieprawidłowy, wynikiem będzie: „URL jest nieprawidłowy, nie można go dostać”.
  2. Jeśli adres URL jest nieprawidłowy, kod nie zatrzymuje się, ale pobiera do końca listy adresów URL?
Dd Pp
źródło

Odpowiedzi:

626

A więc: witaj w świecie R. ;-)

Proszę bardzo

Konfigurowanie kodu

urls <- c(
    "http://stat.ethz.ch/R-manual/R-devel/library/base/html/connections.html",
    "http://en.wikipedia.org/wiki/Xz",
    "xxxxx"
)
readUrl <- function(url) {
    out <- tryCatch(
        {
            # Just to highlight: if you want to use more than one 
            # R expression in the "try" part then you'll have to 
            # use curly brackets.
            # 'tryCatch()' will return the last evaluated expression 
            # in case the "try" part was completed successfully

            message("This is the 'try' part")

            readLines(con=url, warn=FALSE) 
            # The return value of `readLines()` is the actual value 
            # that will be returned in case there is no condition 
            # (e.g. warning or error). 
            # You don't need to state the return value via `return()` as code 
            # in the "try" part is not wrapped insided a function (unlike that
            # for the condition handlers for warnings and error below)
        },
        error=function(cond) {
            message(paste("URL does not seem to exist:", url))
            message("Here's the original error message:")
            message(cond)
            # Choose a return value in case of error
            return(NA)
        },
        warning=function(cond) {
            message(paste("URL caused a warning:", url))
            message("Here's the original warning message:")
            message(cond)
            # Choose a return value in case of warning
            return(NULL)
        },
        finally={
        # NOTE:
        # Here goes everything that should be executed at the end,
        # regardless of success or error.
        # If you want more than one expression to be executed, then you 
        # need to wrap them in curly brackets ({...}); otherwise you could
        # just have written 'finally=<expression>' 
            message(paste("Processed URL:", url))
            message("Some other message at the end")
        }
    )    
    return(out)
}

Stosowanie kodu

> y <- lapply(urls, readUrl)
Processed URL: http://stat.ethz.ch/R-manual/R-devel/library/base/html/connections.html
Some other message at the end
Processed URL: http://en.wikipedia.org/wiki/Xz
Some other message at the end
URL does not seem to exist: xxxxx
Here's the original error message:
cannot open the connection
Processed URL: xxxxx
Some other message at the end
Warning message:
In file(con, "r") : cannot open file 'xxxxx': No such file or directory

Badanie produkcji

> head(y[[1]])
[1] "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">"      
[2] "<html><head><title>R: Functions to Manipulate Connections</title>"      
[3] "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">"
[4] "<link rel=\"stylesheet\" type=\"text/css\" href=\"R.css\">"             
[5] "</head><body>"                                                          
[6] ""    

> length(y)
[1] 3

> y[[3]]
[1] NA

Dodatkowe uwagi

próbuj złapać

tryCatchzwraca wartość związaną z wykonaniem, exprchyba że wystąpi błąd lub ostrzeżenie. W takim przypadku określone wartości zwracane (patrz return(NA)wyżej) można określić, podając odpowiednią funkcję modułu obsługi (patrz argumenty errori warningin ?tryCatch). Mogą to być funkcje, które już istnieją, ale możesz je również zdefiniować w sobie tryCatch()(tak jak to zrobiłem powyżej).

Implikacje wyboru określonych wartości zwracanych przez funkcje obsługi

Jak ustaliliśmy, że NApowinien zostać zwrócony w przypadku błędu, trzecim elementem yjest NA. Gdybyśmy wybrali NULLwartość zwracaną, długość ybyłaby po prostu 2zamiast, 3ponieważ lapply()po prostu „ignoruje” zwracane wartości, które są NULL. Zauważ również, że jeśli nie określisz wyraźnej wartości zwracanej przez return(), funkcje modułu obsługi zwrócą NULL(tj. W przypadku błędu lub warunku ostrzegawczego).

Komunikat ostrzegawczy „niepożądany”

Ponieważ warn=FALSEwydaje się, że nie ma to żadnego efektu, alternatywnym sposobem tłumienia ostrzeżenia (które w tym przypadku tak naprawdę nie jest interesujące) jest użycie

suppressWarnings(readLines(con=url))

zamiast

readLines(con=url, warn=FALSE)

Wiele wyrażeń

Pamiętaj, że możesz także umieścić wiele wyrażeń w „rzeczywiste wyrażeń części” (teza expro tryCatch()) jeśli zawinąć je w nawiasach klamrowych (podobnie jak ja pokazano w finallyczęści).

Rappster
źródło
Biorąc pod uwagę, że pierwszy ciąg w twoich pastefunkcjach kończy się spacją, dlaczego nie pominąć spacji i sep=""?
seancarmody
2
@seancarmody: true ;-) Jestem tak przyzwyczajony do łączenia dłuższych / bardziej skomplikowanych ciągów, gdybym musiał kontrolować spacje poprzez ich wypisywanie.
Rappster
3
Powinieneś użyć paste0do tego!
seancarmody
6
paste0() jest w bazie. Wewnętrznie zarówno paste()i paste0()wywołaj do_pastew paste.c . Jedyną różnicą jest to, paste0()że nie przekazuje separgumentu.
jthetzel
1
@JulienNavarre: pamiętaj, że „część próbna” zawsze zwraca ostatni obiekt (aktualnie readLines(con=url, warn=FALSE)to, co może się nie powieść ). Tak więc, jeśli chcesz dodać wiadomość, musisz zapisać aktualną wartość retunu w zmiennej: out <- readLines(con=url, warn=FALSE)po niej message("Everything worked")następuje out, aby uczynić to ostatnim zwracanym obiektem
Rappster
69

R używa funkcji do implementacji bloku try-catch:

Składnia wygląda mniej więcej tak:

result = tryCatch({
    expr
}, warning = function(warning_condition) {
    warning-handler-code
}, error = function(error_condition) {
    error-handler-code
}, finally={
    cleanup-code
})

W tryCatch () można spełnić dwa „warunki”: „ostrzeżenia” i „błędy”. Ważną rzeczą do zrozumienia podczas pisania każdego bloku kodu jest stan wykonania i zakres. @źródło

heretolearn
źródło
5
Wymienić error-handler-codezcat("web url is wrong, can't get")
seancarmody
2
pominąłeś
52

tryCatchma nieco złożoną strukturę składni. Jednak gdy zrozumiemy 4 części, które stanowią kompletne wywołanie tryCatch, jak pokazano poniżej, łatwo jest zapamiętać:

wyrażenie : [ wymagane ] R kod (y) do oceny

error : [ Opcjonalnie ] Co powinno się uruchomić, jeśli wystąpił błąd podczas oceny kodów w wyrażeniu

ostrzeżenie : [ Opcjonalnie ] Co powinno się uruchomić, jeśli wystąpi ostrzeżenie podczas oceny kodów w wyrażeniu

na koniec : [ Opcjonalnie ] Co powinno działać tuż przed zakończeniem wywołania tryCatch, niezależnie od tego, czy wyrażenie uruchomione pomyślnie, z błędem czy z ostrzeżeniem

tryCatch(
    expr = {
        # Your code...
        # goes here...
        # ...
    },
    error = function(e){ 
        # (Optional)
        # Do this if an error is caught...
    },
    warning = function(w){
        # (Optional)
        # Do this if an warning is caught...
    },
    finally = {
        # (Optional)
        # Do this at the end before quitting the tryCatch structure...
    }
)

Przykład zabawkowy do obliczenia dziennika wartości może wyglądać następująco:

log_calculator <- function(x){
    tryCatch(
        expr = {
            message(log(x))
            message("Successfully executed the log(x) call.")
        },
        error = function(e){
            message('Caught an error!')
            print(e)
        },
        warning = function(w){
            message('Caught an warning!')
            print(w)
        },
        finally = {
            message('All done, quitting.')
        }
    )    
}

Teraz uruchamiam trzy sprawy:

Ważna sprawa

log_calculator(10)
# 2.30258509299405
# Successfully executed the log(x) call.
# All done, quitting.

Przypadek „ostrzegawczy”

log_calculator(-10)
# Caught an warning!
# <simpleWarning in log(x): NaNs produced>
# All done, quitting.

Przypadek „błędu”

log_calculator("log_me")
# Caught an error!
# <simpleError in log(x): non-numeric argument to mathematical function>
# All done, quitting.

Pisałem o kilku użytecznych przypadkach, z których regularnie korzystam. Znajdź więcej szczegółów tutaj: https://rsangole.netlify.com/post/try-catch/

Mam nadzieję, że to jest pomocne.

Rahul
źródło
34

Oto prosty przykład :

# Do something, or tell me why it failed
my_update_function <- function(x){
    tryCatch(
        # This is what I want to do...
        {
        y = x * 2
        return(y)
        },
        # ... but if an error occurs, tell me what happened: 
        error=function(error_message) {
            message("This is my custom message.")
            message("And below is the error message from R:")
            message(error_message)
            return(NA)
        }
    )
}

Jeśli chcesz również uchwycić „ostrzeżenie”, po prostu dodaj warning=podobny do error=części.

Paweł
źródło
1
Czy wokół exprczęści powinny znajdować się nawiasy klamrowe , ponieważ zamiast jednej są dwie linie?
Paul,
Dzięki! Po podwójnym sprawdzeniu, nie widzę potrzeby nawiasów klamrowych
Paul
Dzięki za podwójne sprawdzenie. Kiedy uruchamiam twój kod, mam Error: unexpected ')' in " )"i Error: unexpected ')' in " )". Dodanie pary nawiasów klamrowych rozwiązuje problem.
Paul
W większości przypadków masz rację, dziękuję! Zostało to naprawione.
Paul
23

Ponieważ właśnie straciłem dwa dni mojego życia, próbując rozwiązać problemy związane z funkcją tryCatch, pomyślałem, że powinienem podzielić się swoją mądrością (i tym, czego brakuje). FYI - irr jest faktyczną funkcją FinCal w tym przypadku, w której wystąpiły błędy w kilku przypadkach na dużym zbiorze danych.

  1. Skonfiguruj tryCatch jako część funkcji. Na przykład:

    irr2 <- function (x) {
      out <- tryCatch(irr(x), error = function(e) NULL)
      return(out)
    }
  2. Aby błąd (lub ostrzeżenie) zadziałał, faktycznie musisz utworzyć funkcję. Pierwotnie z powodu błędu właśnie napisałem, error = return(NULL)a WSZYSTKIE wartości wróciły do ​​zera.

  3. Pamiętaj, aby utworzyć pod-wyjście (jak mój „out”) i return(out).

James
źródło
3
Dlaczego numer 3 jest konieczny?
jan-glx