58 Stimmen

Wie speichere ich Warnungen und Fehler als Ausgabe einer Funktion?

Ich benutze lapply Ich möchte eine komplexe Funktion für eine große Anzahl von Elementen ausführen und die Ausgabe jedes Elements (falls vorhanden) zusammen mit den erzeugten Warnungen/Fehlern speichern, damit ich feststellen kann, welches Element welche Warnung/Fehler erzeugt hat.

Ich habe einen Weg gefunden, Warnungen abzufangen, indem ich withCallingHandlers ( hier beschrieben ). Allerdings muss ich auch Fehler abfangen. Ich kann das tun, indem ich es in eine tryCatch (wie im nachstehenden Code), aber gibt es einen besseren Weg, dies zu tun?

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

Ein Beispiel für die Ausgabe dieser Funktion ist:

> 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"

Es gibt mehrere Fragen hier auf SO, die Folgendes behandeln tryCatch und Fehlerbehandlung, aber keine, die ich gefunden habe, die dieses spezielle Problem behandeln. Siehe Wie kann ich überprüfen, ob ein Funktionsaufruf zu einer Warnung führt? , warnings() funktioniert nicht innerhalb einer Funktion? Wie kann man dies umgehen? y Wie kann man lapply anweisen, einen Fehler zu ignorieren und den nächsten Eintrag in der Liste zu bearbeiten? für die wichtigsten.

61voto

Martin Morgan Punkte 44845

Vielleicht ist dies die gleiche Lösung wie Ihre, aber ich habe eine factory einfache alte Funktionen in Funktionen umzuwandeln, die ihre Werte, Fehler und Warnungen erfassen, damit ich

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

wobei jedes Element des Ergebnisses den Wert, den Fehler und / oder die Warnungen enthält. Dies würde mit Benutzerfunktionen, Systemfunktionen oder anonymen Funktionen funktionieren ( factory(function(i) ...) ). Hier ist die Fabrik

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

und einige Hilfsprogramme für den Umgang mit der Ergebnisliste

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

21voto

hadley Punkte 97925

Versuchen Sie die Paket bewerten .

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

Es fehlt derzeit eine schöne Art der Auswertung Ausdruck aber - das ist vor allem, weil es auf die Reproduktion genau das, was R Ausgabe gegeben Texteingabe auf der Konsole ausgerichtet ist.

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

Es erfasst auch Meldungen, gibt sie auf der Konsole aus und stellt sicher, dass alles in der richtigen Reihenfolge ineinander verschachtelt wird.

18voto

user2161065 Punkte 1806

Ich habe Martins Soulution zusammengeführt ( https://stackoverflow.com/a/4952908/2161065 ) und die von der R-Hilfe-Mailingliste, die Sie mit demo(error.catching) .

Die Hauptidee besteht darin, sowohl die Warn-/Fehlermeldung als auch den Befehl, der das Problem auslöst, zu behalten.

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

Beispiele:

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

Salida:

> myTryCatch(log(1))

$Wert [1] 0 $Warnung NULL $Fehler NULL

> myTryCatch(log(-1))

$ $Warnung $Fehler NULL

> myTryCatch(log("a"))

$Wert NULL $Warnung NULL $Fehler

9voto

russellpierce Punkte 4458

Der Zweck meiner Antwort (und der Änderung von Martins ausgezeichnetem Code) besteht darin, dass die werkseitige Funktion die erwartete Datenstruktur zurückgibt, wenn alles gut geht. Wenn eine Warnung auftritt, wird sie an das Ergebnis unter dem Parameter factory-warning Attribut. data.table's setattr Funktion wird verwendet, um die Kompatibilität mit diesem Paket zu gewährleisten. Tritt ein Fehler auf, ist das Ergebnis das Zeichenelement "An error occurred in the factory function" und die factory-error Attribut wird die Fehlermeldung enthalten.

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

Da wir das Ergebnis nicht in eine zusätzliche Liste verpacken, können wir nicht die Art von Annahmen treffen, die einige seiner Accessor-Funktionen ermöglichen, aber wir können einfache Überprüfungen schreiben und entscheiden, wie wir die Fälle behandeln, die für unsere spezielle resultierende Datenstruktur angemessen sind.

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X