Come scrivere il trycatch in R


342

Voglio scrivere trycatchcodice per gestire gli errori durante il download dal Web.

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)

Queste due istruzioni vengono eseguite correttamente. Di seguito, creo un indirizzo Web inesistente:

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

url[1]non esiste. Come si scrive un trycatchciclo (funzione) in modo che:

  1. Quando l'URL è errato, l'output sarà: "l'URL Web è errato, impossibile ottenere".
  2. Quando l'URL è errato, il codice non si ferma, ma continua a scaricare fino alla fine dell'elenco di URL?

Risposte:


626

Bene allora: benvenuti nel mondo R ;-)

Ecco qui

Impostazione del codice

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

Applicazione del codice

> 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

Indagare sull'output

> 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

Osservazioni aggiuntive

prova a prendere

tryCatchrestituisce il valore associato all'esecuzione a exprmeno che non ci sia un errore o un avviso. In questo caso, è possibile specificare valori di ritorno specifici (vedere return(NA)sopra) fornendo una rispettiva funzione del gestore (vedere argomenti errore warningin ?tryCatch). Queste possono essere funzioni già esistenti, ma puoi anche definirle all'interno tryCatch()(come ho fatto sopra).

Le implicazioni della scelta di valori di ritorno specifici delle funzioni del gestore

Come abbiamo specificato che NAdovrebbe essere restituito in caso di errore, il terzo elemento yè NA. Se avessimo scelto NULLdi essere il valore di ritorno, la lunghezza di ysarebbe stata 2invece di 3come lapply()semplicemente "ignoreremo" i valori di ritorno che sono NULL. Si noti inoltre che se non si specifica un valore di ritorno esplicito tramite return(), verranno restituite le funzioni del gestore NULL(ovvero in caso di errore o condizione di avviso).

Messaggio di avviso "indesiderato"

Dato warn=FALSEche non sembra avere alcun effetto, un modo alternativo per sopprimere l'avvertimento (che in questo caso non è davvero interessante) è usare

suppressWarnings(readLines(con=url))

invece di

readLines(con=url, warn=FALSE)

Espressioni multiple

Nota che puoi anche inserire più espressioni nella "parte delle espressioni effettive" (argomento exprdi tryCatch()) se le avvolgi tra parentesi graffe (proprio come ho illustrato nella finallyparte).


Dato che la prima stringa nelle tue pastefunzioni termina con uno spazio, perché non omettere lo spazio e il sep=""?
seancarmody,

2
@seancarmody: true ;-) Sono così abituato a mettere insieme stringhe più lunghe / complicate dove devo controllare gli spazi scrivendoli.
Rappster,

3
Dovresti usare paste0per quello!
seancarmody,

6
paste0() è nella base. Internamente, entrambi paste()e paste0()chiama do_pastein paste.c . L'unica differenza è paste0()che non passa un separgomento.
jthetzel,

1
@JulienNavarre: ricorda che la "parte di prova" restituisce sempre l' ultimo oggetto ( readLines(con=url, warn=FALSE)che attualmente è la cosa reale che potrebbe andare storto). Quindi, se si desidera aggiungere un messaggio, sarebbe necessario archiviare il valore di risincronizzazione effettivo in una variabile: out <- readLines(con=url, warn=FALSE)seguito da message("Everything worked")seguito outper rendere questo l'ultimo oggetto effettivamente restituito
Rappster

69

R utilizza le funzioni per l'implementazione del blocco try-catch:

La sintassi è in qualche modo simile a questa:

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

In tryCatch () ci sono due 'condizioni' che possono essere gestite: 'avvertimenti' ed 'errori'. La cosa importante da capire quando si scrive ogni blocco di codice è lo stato di esecuzione e l'ambito. @fonte


5
Sostituisci error-handler-codeconcat("web url is wrong, can't get")
seancarmody il

2
hai lasciato fuori la cattura dei messaggi
rawr

52

tryCatchha una struttura di sintassi leggermente complessa. Tuttavia, una volta comprese le 4 parti che costituiscono una chiamata tryCatch completa come mostrato di seguito, diventa facile ricordare:

expr : [ Obbligatorio ] Codici R da valutare

errore : [ Opzionale ] Cosa dovrebbe essere eseguito se si è verificato un errore durante la valutazione dei codici in expr

warning : [ Opzionale ] Cosa dovrebbe essere eseguito se si è verificato un avviso durante la valutazione dei codici in expr

infine : [ Facoltativo ] Cosa deve essere eseguito poco prima di chiudere la chiamata tryCatch, indipendentemente dal fatto che expr sia stato eseguito correttamente, con un errore o con un avviso

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

Pertanto, un esempio di giocattolo, per calcolare il registro di un valore, potrebbe apparire come:

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

Ora, eseguendo tre casi:

Un caso valido

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

Un caso di "avvertimento"

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

Un caso di "errore"

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

Ho scritto di alcuni utili casi d'uso che uso regolarmente. Trova maggiori dettagli qui: https://rsangole.netlify.com/post/try-catch/

Spero sia utile.


34

Ecco un esempio chiaro :

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

Se si desidera acquisire anche un "avviso", aggiungere semplicemente warning=un error=elemento simile alla parte.


1
Dovrebbero esserci parentesi graffe attorno alla exprparte, poiché ci sono due linee invece di una?
Paul

Grazie! Dopo il doppio controllo, non vedo alcun bisogno di parentesi graffe
Paul

Grazie per il doppio controllo. Quando eseguo il tuo codice, ho ricevuto Error: unexpected ')' in " )"e Error: unexpected ')' in " )". L'aggiunta di una coppia di parentesi graffe risolve il problema.
Paul,

Per la maggior parte dei casi d'uso, hai ragione, grazie! È stato corretto.
Paul,

23

Dato che ho appena perso due giorni della mia vita cercando di risolvere per tryCatch per una funzione irr, ho pensato di condividere la mia saggezza (e cosa manca). Cordiali saluti - irr è una funzione effettiva di FinCal in questo caso in cui sono stati rilevati errori in alcuni casi su un set di dati di grandi dimensioni.

  1. Imposta tryCatch come parte di una funzione. Per esempio:

    irr2 <- function (x) {
      out <- tryCatch(irr(x), error = function(e) NULL)
      return(out)
    }
  2. Affinché l'errore (o l'avviso) funzioni, in realtà è necessario creare una funzione. Originariamente per la parte di errore ho appena scritto error = return(NULL)e TUTTI i valori sono tornati nulli.

  3. Ricorda di creare un output secondario (come il mio "out") e di return(out).


3
Perché è necessario il numero 3?
Jan-Glx,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.