Estoy usando lapply para ejecutar una función compleja en un gran número de artículos, y me gustaría guardar la salida de cada elemento (si cualquier) junto con cualquiera de advertencias/errores que se produjeron de modo que puedo decir que el elemento que produce advertencia/error.

He encontrado una manera de coger advertencias de uso withCallingHandlers (se describe aquí). Sin embargo, tengo que coger para errores así. Que puedo hacer envolviéndolo en un tryCatch (como en el código de abajo), pero hay una manera mejor de hacerlo?

catchToList <- function(expr) {
  val <- NULL
  myWarnings <- NULL
  wHandler <- function(w) {
    myWarnings <<- c(myWarnings, w$message)
    invokeRestart("muffleWarning")
  }
  myError <- NULL
  eHandler <- function(e) {
    myError <<- e$message
    NULL
  }
  val <- tryCatch(withCallingHandlers(expr, warning = wHandler), error = eHandler)
  list(value = val, warnings = myWarnings, error=myError)
} 

Muestra de la salida de esta función es:

> catchToList({warning("warning 1");warning("warning 2");1})
$value
[1] 1

$warnings
[1] "warning 1" "warning 2"

$error
NULL

> catchToList({warning("my warning");stop("my error")})
$value
NULL

$warnings
[1] "my warning"

$error
[1] "my error"

Hay varias preguntas aquí sobre LO que discutir tryCatch y el manejo de errores, pero ninguno de ellos me encontré con que la dirección de este tema en particular. Ver ¿Cómo puedo comprobar si una llamada de función de los resultados en una advertencia?, advertencias() no funciona dentro de una función? ¿Cómo se puede evitar esto?, y Cómo saber lapply ignorar un error y el proceso lo siguiente en la lista? de los más relevantes.

InformationsquelleAutor Aaron | 2011-02-09

4 Comentarios

  1. 42

    Tal vez este es el mismo que el de su solución, pero escribí un factory para convertir el viejo y simple de las funciones dentro de las funciones que la captura de sus valores, errores y advertencias, así que puedo

    test <- function(i)
        switch(i, "1"=stop("oops"), "2"={ warning("hmm"); i }, i)
    res <- lapply(1:3, factory(test))

    con cada elemento del resultado con el valor, el error, y /o advertencias. Esto funciona con las funciones de usuario, funciones del sistema o funciones anónimas (factory(function(i) ...)). Aquí está la fábrica de

    factory <- function(fun)
        function(...) {
            warn <- err <- NULL
            res <- withCallingHandlers(
                tryCatch(fun(...), error=function(e) {
                    err <<- conditionMessage(e)
                    NULL
                }), warning=function(w) {
                    warn <<- append(warn, conditionMessage(w))
                    invokeRestart("muffleWarning")
                })
            list(res, warn=warn, err=err)
        }

    y algunos ayudantes para tratar con la lista de resultados

    .has <- function(x, what)
        !sapply(lapply(x, "[[", what), is.null)
    hasWarning <- function(x) .has(x, "warn")
    hasError <- function(x) .has(x, "err")
    isClean <- function(x) !(hasError(x) | hasWarning(x))
    value <- function(x) sapply(x, "[[", 1)
    cleanv <- function(x) sapply(x[isClean(x)], "[[", 1)
    • Sí, la misma idea, pero se ve mucho mejor! Han considerado que la envolviéndola en un paquete? De las otras preguntas que me vio solo aquí en LO demás sería útil también.
    • Tengo una función que almacena su llamada en la salida. Después de invocar factory esta llamada se cambia, por ejemplo,fun(formula = ..1, data = ..2, method = "genetic", ratio = ..4, print.level = 0), donde formula debería ser mi entrada original de la fórmula, pero se sobrescribe. Algún consejo?
    • Supongo que es porque es realmente hacer una nueva función fun y llamadas que con el ... en lugar de llamar a la tuya directamente. Me pregunto si mi catchToList funciona, o si el factory podría ser modificado, tal vez usando do.call. ¿Cómo puede ser reproducido?
    • Me gustó mucho el enfoque de @russellpierce a continuación para la captura de advertencia y mensajes de error mediante el uso de atributos. Así que el centro NULL anterior podría ser reemplazado con NA o "An error occurred" o similar, y la 2ª a la última línea reemplazado con attr(res,"warning") <- warn; attr(res,"error") <- err; return(res)
    • Cuando trato de usar por encima de, por ejemplo, con f.log <- factory(log) y luego puedo hacer isClean(f.log("a")) o cualquier otro auxiliar de la función de recibir: «Error en la DIVERSIÓN(X[[i]], …) : subíndice fuera de los límites «. Mismo error que puedo hacer si isClean(f.log(10))
    • la intención de uso de una función en lapply, por ejemplo, isClean(lapply(list(1, 2, "a"), f.log))

  2. 15

    Probar el evaluar paquete.

    library(evaluate)
    test <- function(i)
        switch(i, "1"=stop("oops"), "2"={ warning("hmm"); i }, i)
    
    t1 <- evaluate("test(1)")
    t2 <- evaluate("test(2)")
    t3 <- evaluate("test(3)")

    Que en la actualidad carece de una buena manera de evaluar la expresión, aunque – esto es principalmente debido a que es destinado a reproducir exactamente lo R de salida del dado de entrada de texto en la consola.

    replay(t1)
    replay(t2)
    replay(t3)

    También captura los mensajes, la salida a la consola, y se asegura de que todo está correctamente intercaladas en el orden en que se produjeron.

    • Es allí una manera de capturar la salida de repetición(t2) en este ejemplo, o en general? x <- capture.output(replay(t1)) no produce el mensaje de error completo. Gracias.
  3. 8

    He combinado Martins soulution (https://stackoverflow.com/a/4952908/2161065) y el de la R-ayudar a la lista de correo que usted consigue con demo(error.catching).

    La idea principal es mantener tanto, la advertencia o mensaje de error así como el comando de activación de este problema.

    myTryCatch <- function(expr) {
      warn <- err <- NULL
      value <- withCallingHandlers(
        tryCatch(expr, error=function(e) {
          err <<- e
          NULL
        }), warning=function(w) {
          warn <<- w
          invokeRestart("muffleWarning")
        })
      list(value=value, warning=warn, error=err)
    }

    Ejemplos:

    myTryCatch(log(1))
    myTryCatch(log(-1))
    myTryCatch(log("a"))

    De salida:

    > myTryCatch(log(1))

    $valor [1] 0
    $advertencia NULL
    $error NULL

    > myTryCatch(log(-1))

    $valor [1] NaN
    $advertencia
    $error NULL

    > myTryCatch(log(«a»))

    $valor NULL
    $advertencia NULL
    $error

    • Es bueno, pero no captura los mensajes o impresiones. Estaría bueno que con sólo una única función que llamó a todos los 4 principales tipos de salida. Me dicen principal, porque hay un par de otros, tales como parcelas, escrito en el portapapeles y escribir en el archivo. En algunos casos, uno podría ver en la captura de estos también.
  4. 6

    El propósito de mi respuesta (y la modificación de Martin excelente código) es así que la fábrica-ed, la función devuelve la estructura de datos se espera que si todo va bien. Si la advertencia es experimentado, se adjunta el resultado en la factory-warning atributo. de datos.tabla setattr función se utiliza para permitir la compatibilidad con el paquete. Si un error es experimentado, el resultado es el elemento de carácter «se produjo Un error en la fábrica de la función» y el factory-error atributo llevar el mensaje de error.

    #' Catch errors and warnings and store them for subsequent evaluation
    #'
    #' Factory modified from a version written by Martin Morgan on Stack Overflow (see below).  
    #' Factory generates a function which is appropriately wrapped by error handlers.  
    #' If there are no errors and no warnings, the result is provided.  
    #' If there are warnings but no errors, the result is provided with a warn attribute set.
    #' If there are errors, the result retutrns is a list with the elements of warn and err.
    #' This is a nice way to recover from a problems that may have occurred during loop evaluation or during cluster usage.
    #' Check the references for additional related functions.
    #' I have not included the other factory functions included in the original Stack Overflow answer because they did not play well with the return item as an S4 object.
    #' @export
    #' @param fun The function to be turned into a factory
    #' @return The result of the function given to turn into a factory.  If this function was in error "An error as occurred" as a character element.  factory-error and factory-warning attributes may also be set as appropriate.
    #' @references
    #' \url{http://stackoverflow.com/questions/4948361/how-do-i-save-warnings-and-errors-as-output-from-a-function}
    #' @author Martin Morgan; Modified by Russell S. Pierce
    #' @examples 
    #' f.log <- factory(log)
    #' f.log("a")
    #' f.as.numeric <- factory(as.numeric)
    #' f.as.numeric(c("a","b",1))
    factory <- function (fun) {
      errorOccurred <- FALSE
      library(data.table)
      function(...) {
        warn <- err <- NULL
        res <- withCallingHandlers(tryCatch(fun(...), error = function(e) {
          err <<- conditionMessage(e)
          errorOccurred <<- TRUE
          NULL
        }), warning = function(w) {
          warn <<- append(warn, conditionMessage(w))
          invokeRestart("muffleWarning")
        })
        if (errorOccurred) {
          res <- "An error occurred in the factory function"
        } 
    
        if (is.character(warn)) {
          data.table::setattr(res,"factory-warning",warn)
        } else {
          data.table::setattr(res,"factory-warning",NULL) 
        }
    
        if (is.character(err)) {
          data.table::setattr(res,"factory-error",err)
        } else {
          data.table::setattr(res, "factory-error", NULL)
        }  
        return(res)
      }
    }

    Porque nosotros no envuelva el resultado en una lista adicional que no podemos hacer este tipo de supuestos que permiten a algunas de sus funciones de descriptor de acceso, pero podemos escribir un control sencillo y decidir cómo manejar los casos, es apropiado para nuestro particular resultante de la estructura de datos.

    .has <- function(x, what) {
      !is.null(attr(x,what))
    }
    hasWarning <- function(x) .has(x, "factory-warning")
    hasError <- function(x) .has(x, "factory-error")
    isClean <- function(x) !(hasError(x) | hasWarning(x))
    • f.log(10) en el ejemplo se imprime «se ha producido Un error en la fábrica de función» en lugar de devolver el resultado como yo esperaba. Sin embargo, el isClean() informa correctamente como VERDADERO. Así se puede obtener el valor de retorno o es solamente para los de error y de advertencia de la comprobación?
    • Yo no soy capaz de replicar su problema a través de R 3.5.1. Por favor, compruebe que no hay ni un poco de bodrio en copiar y pegar. Si el problema persiste, por favor compartir más información acerca de su entorno de ejecución.
    • R es la versión 3.5.2. Acabo de copiar, pegar, y corrió de nuevo. Si la primera vez que ejecute f.log(10), funciona y devuelve un valor. Entonces si me quedo f.log("a") y, a continuación, ejecute de nuevo con f.log(10) después de eso, se trata de volver con «se produjo Un error en la fábrica de la función».
    • Ah, esa es una buena sugerencia. El cierre es de que se está actualizando. Yo sólo había utilizado este para multinúcleo casos donde cada copia de la función fue envoked una vez. Voy a echar un vistazo y ver si hay una solución. Probablemente la actualización de la variable de error en FALSE cuando se inicia

Dejar respuesta

Please enter your comment!
Please enter your name here