Como escrever trycatch em R

342

Quero escrever um trycatchcódigo para lidar com erros no download da 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)

Essas duas instruções são executadas com êxito. Abaixo, crio um endereço da web inexistente:

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

url[1]não existe. Como se escreve um trycatchloop (função) para que:

  1. Quando o URL estiver errado, a saída será: "O URL da Web está errado, não é possível obter".
  2. Quando o URL está errado, o código não para, mas continua o download até o final da lista de URLs?
Dd Pp
fonte

Respostas:

625

Bem, então: bem-vindo ao mundo R ;-)

Aqui está

Configurando o código

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

Aplicando o código

> 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

Investigando a saída

> 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

Observações adicionais

tryCatch

tryCatchretorna o valor associado à execução, a exprmenos que haja um erro ou aviso. Nesse caso, valores de retorno específicos (veja return(NA)acima) podem ser especificados fornecendo uma função de manipulador respectiva (consulte argumentos errore warningin ?tryCatch). Podem ser funções que já existem, mas você também pode defini-las dentrotryCatch() (como fiz acima).

As implicações da escolha de valores de retorno específicos das funções do manipulador

Como especificamos que NAdeve ser retornado em caso de erro, o terceiro elemento yé NA. Se tivéssemos escolhido NULLpara ser o valor de retorno, o comprimento yseria apenas ter sido 2em vez de 3como lapply()simplesmente "ignorar" valores de retorno que são NULL. Observe também que, se você não especificar um valor de retorno explícito via return(), as funções do manipulador retornarão NULL(ou seja, no caso de um erro ou condição de aviso).

Mensagem de aviso "indesejado"

Como warn=FALSEnão parece ter efeito, uma maneira alternativa de suprimir o aviso (que neste caso não é realmente interessante) é usar

suppressWarnings(readLines(con=url))

ao invés de

readLines(con=url, warn=FALSE)

Várias expressões

Observe que você também pode colocar várias expressões na "parte de expressões reais" (argumento exprde tryCatch()) se as envolver entre colchetes (como ilustrado na finallypeça).

Rappster
fonte
Dado que a primeira string em suas pastefunções termina com um espaço, por que não omitir o espaço e o sep=""?
Seancarmody 30/08/2012
2
@seancarmody: true ;-) Estou tão acostumado a montar cordas mais longas / mais complicadas que tenho que controlar os espaços por meio da escrita deles.
Rappster 30/08/12
3
Você deveria usar paste0para isso!
Seancarmody
6
paste0() está na base. Internamente, ambos paste()e paste0()chame do_pasteem paste.c . A única diferença é paste0()que não passa um separgumento.
Jtetzel
11
@JulienNavarre: lembre-se de que a "parte try" sempre retorna o último objeto (atualmente, o readLines(con=url, warn=FALSE)que realmente pode dar errado). Então, se você quiser adicionar uma mensagem, você precisaria armazenado o valor retun real em uma variável: out <- readLines(con=url, warn=FALSE)seguido por message("Everything worked")seguido por out, a fim de tornar este o último objeto que é realmente devolvido
Rappster
69

R usa funções para implementar o bloco try-catch:

A sintaxe é algo como isto:

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

No tryCatch (), existem duas 'condições' que podem ser tratadas: 'avisos' e 'erros'. O importante a entender ao escrever cada bloco de código é o estado de execução e o escopo. @fonte

heretolearn
fonte
5
Substitua error-handler-codeporcat("web url is wrong, can't get")
seancarmody 30/08/2012
2
você deixou de fora a captura de mensagens
rawr
52

tryCatchpossui uma estrutura de sintaxe um pouco complexa. No entanto, uma vez que entendemos as quatro partes que constituem uma chamada tryCatch completa, como mostrado abaixo, fica fácil lembrar:

expr : [ obrigatório ] código (s) R a ser avaliado

erro : [ Opcional ] O que deve ser executado se ocorrer um erro ao avaliar os códigos em expr

aviso : [ Opcional ] O que deve ser executado se um aviso ocorrer durante a avaliação dos códigos em expr

finalmente : [ Opcional ] O que deve ser executado antes de sair da chamada tryCatch, independentemente de se o expr for executado com sucesso, com um erro ou com um aviso

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

Assim, um exemplo de brinquedo, para calcular o log de um valor, pode parecer com:

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

Agora, executando três casos:

Um caso válido

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

Um caso de "aviso"

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

Um caso de "erro"

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

Eu escrevi sobre alguns casos de uso úteis que uso regularmente. Encontre mais detalhes aqui: https://rsangole.netlify.com/post/try-catch/

Espero que isso seja útil.

Rahul
fonte
34

Aqui está um exemplo direto :

# 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 você também deseja capturar um "aviso", basta adicionar warning=semelhante à error=peça.

Paulo
fonte
11
Deve haver colchetes ao redor da exprpeça, pois existem duas linhas em vez de uma?
Paul
Obrigado! Após a verificação dupla, não vejo necessidade de colchetes
Paul
Obrigado pela verificação dupla. Quando executo seu código, recebi Error: unexpected ')' in " )"e Error: unexpected ')' in " )". Adicionar um par de colchetes resolve o problema.
Paul
Na maioria dos casos de uso, você está certo, obrigado! Foi consertado.
Paul
23

Como acabei de perder dois dias da minha vida tentando resolver o tryCatch para uma função irr, achei que deveria compartilhar minha sabedoria (e o que está faltando). FYI - irr é uma função real do FinCal, neste caso, onde houve erros em alguns casos em um grande conjunto de dados.

  1. Configure o tryCatch como parte de uma função. Por exemplo:

    irr2 <- function (x) {
      out <- tryCatch(irr(x), error = function(e) NULL)
      return(out)
    }
  2. Para que o erro (ou aviso) funcione, você realmente precisa criar uma função. Eu originalmente para a parte do erro acabei de escrever error = return(NULL)e TODOS os valores voltaram nulos.

  3. Lembre-se de criar uma sub-saída (como minha "saída") e para return(out).

James
fonte
3
Por que o número 3 é necessário?
jan-GLX