【问题标题】:How should one implement "is.error()" for R, to identify and parse errors?应该如何为 R 实现“is.error()”来识别和解析错误?
【发布时间】:2023-03-25 10:23:01
【问题描述】:

我正在尝试测试对象是否是错误的结果。该用例主要是通过产生错误的foreach() 循环产生的(尽管对于测试,将simpleError() 分配给变量似乎就足够了),我对如何识别何时发生感到困惑:我如何测试给定的对象实际上是一个错误?一旦我确定这是一个错误,除了一条消息,我还能提取什么?也许我遗漏了 R 的错误处理工具,因为似乎有必要编写一个错误对象测试函数de novo

这里有两个示例,一个使用foreach,将.errorhandling 参数设置为pass。我已经开始将其用作大规模或无人值守处理的默认设置,以防数据切片出现异常。这种异常很少见,不值得让整个 for 循环崩溃(特别是如果异常发生在最后,这似乎是我的 murphysListSortingAlgorithm() 的默认行为;-))。相反,事后检测是需要的。

library(foreach)
library(doMC)
registerDoMC(2)
results = foreach(ix = 1:10, .errorhandling = "pass") %dopar%{
    if(ix == 6){
        stop("Perfect")
    } 
    if(ix == 7){
        stop("LuckyPrime")
    } else {
        return(ix)
    }
}

为简单起见,这是一个非常简单的错误(根据定义):

a = simpleError("SNAFU")

虽然似乎没有像is.error() 这样的命令,而像typeof()mode() 这样的命令似乎毫无意义,但我发现最好的方法是使用class()attributes(),这给出指示错误的属性。如何以保证确定我有错误并完全处理该错误的方式使用这些?例如a$message 返回SNAFU,但a$callNULL。我是否应该期望能够从例如res[[6]]$call 中提取任何有用的信息?


注意 1:如果没有多核功能来重现第一个示例,我应该指出 results[[6]]simpleError("Perfect") 不同:

> b = simpleError("Perfect")
> identical(results[[6]], b)
[1] FALSE
> results[[6]]
<simpleError in eval(expr, envir, enclos): Perfect>
> b
<simpleError: Perfect>

这说明了为什么我不能(非常天真地)测试列表元素是否是香草simpleError

注意 2。我知道 trytryCatch,并在某些情况下使用它们。但是,我不完全确定如何使用它们对foreach 循环的输出进行后处理。例如,第一个示例中的 results 对象:在我看来,使用 tryCatch 包装器处理其元素没有意义。对于操作的 RHS,即foreach() 循环,我也不确定tryCatch 是否会按照我的意愿行事。我可以使用它来捕获错误,但我想我需要获取消息并在此时插入处理。我看到两个问题:每个循环都需要用tryCatch() 包装,否定.errorhandling 参数的一部分,并且我仍然无法稍后处理results 对象。如果这是进行此处理的唯一方法,那么它就是解决方案,但这意味着无法以与许多其他 R 对象(例如矩阵、向量、数据帧等)类似的方式识别和处理错误。


更新 1。我在 foreach 循环中添加了一个额外的停止触发器,以提供两个不同的消息来识别和解析,以防万一这有帮助。

更新 2。我选择 Richie Cotton 的答案。这似乎是对我应该寻找的最完整的解释,尽管完整的实现需要其他几位代码(以及最新版本的 R)。最重要的是,他指出我们需要牢记两种类型的错误,这对于彻底彻底尤为重要。也可以看看别人的cmet和答案,以充分开发自己的is.error()测试功能;在查找结果列表中的错误时,我给出的答案可能是一个有用的开始,Richie 的代码是测试函数的一个很好的起点。

【问题讨论】:

  • 你试过try和/或tryCatch吗?
  • @HongOoi 我在其他情况下使用过这些,但我不清楚如何使用它们来检测和解析错误:假设我有很多不是错误的值 - 什么我会吗?不过,这是一个有趣的途径,我会更新这个问题。
  • 我也很困惑为什么tryCatch 在这里不起作用。我不一定会将错误视为对象....
  • 一开始有condition。和conditionBlueScreenOfDeath,与motherOfAllErrors 和生errorerror在干涸的地球上流浪多年,直到遇见simpleton,然后他们一起生了simpleError。生活是为了conditions。
  • @gsk3 我们有fortunes() 的候选人。 :)

标签: r error-handling


【解决方案1】:

来自 ?simpleError:

条件是继承自抽象类条件的对象。 错误和警告是从抽象继承的对象 子类错误和警告。类 simpleError 是使用的类 通过停止和所有内部错误信号。同样,simpleWarning 是 被警告使用,simpleMessage 被消息使用。这 同名的构造函数采用描述条件的字符串 作为参数和可选调用。函数 conditionMessage 和 conditionCall 是返回消息并调用的通用函数 条件。

所以class(a) 返回:

[1] "simpleError" "error"       "condition"  

那么一个简单的函数:

is.condition <- function(x) {
  require(taRifx)
  last(class(x))=="condition"
}

正如@flodel 所说,用inherits(x,"condition") 替换函数体更加健壮。

【讨论】:

  • 你说的这个last()函数在哪里? :) 尽管如此,尝试f=6;class(f) = "condition" - 不能保证这只会检测到错误。现在我们正在研究 I 型和 II 型错误。 ;-)
  • (续)我愿意考虑测试“first()”(又名head(...,1),是simpleError 是一个有用的测试,这是我正在考虑做的事情。不过,似乎解析错误是关于 $message 元素,在 $call 中出现了一些问题 - 我不确定那是什么。
  • 糟糕。修复了:-)。我对?conditions 的解读是,如果是警告或错误,那么最高级别是“条件”,如果是某种错误,第二高级别始终是“错误”。所以也许测试一下而不是 simpleError,因为它会捕获更多错误?
  • 这是真的。阶级等级制度确实是我们当今社会面临的基本问题之一。
  • inherits(x, "condition") 可能更合适。
【解决方案2】:

使用@flodel 关于inherits() 的建议,得到@gsk3 提到的抽象类继承,这是我目前的解决方案:

is.error.element <- function(x){
    testError   <- inherits(x, "error")
    if(testError == TRUE){
        testSimple  <- inherits(x, "simpleError")
        errMsg      <- x$message
    } else {
        testSimple  <- FALSE
        errMsg      <- NA
    }
    return(data.frame(testError, testSimple, errMsg, stringsAsFactors = FALSE))
}

is.error <- function(testObject){
    quickTest <- is.error.element(testObject)
    if(quickTest$testError == TRUE){
        return(quickTest)
    } else {
        return(lapply(testObject, is.error.element))
    }
}

这是通过ldplyresults 列表制作的漂亮结果:

> ldply(is.error(results))
   testError testSimple     errMsg
1      FALSE      FALSE       <NA>
2      FALSE      FALSE       <NA>
3      FALSE      FALSE       <NA>
4      FALSE      FALSE       <NA>
5      FALSE      FALSE       <NA>
6       TRUE       TRUE    Perfect
7       TRUE       TRUE LuckyPrime
8      FALSE      FALSE       <NA>
9      FALSE      FALSE       <NA>
10     FALSE      FALSE       <NA>

> is.error(a)
  testError testSimple errMsg
1      TRUE       TRUE  SNAFU

这对我来说仍然很粗糙,尤其是因为我没有提取有意义的call 值,并且外部函数isError() 在其他结构上可能效果不佳。我怀疑这可以通过sapply*apply*ply (plyr) 家族的其他成员来改善。

【讨论】:

    【解决方案3】:

    您可能会在野外看到的仅有两种类型的错误是simpleErrors,就像您在这里一样,try-errors 是在对try 的调用中包装一些异常抛出代码的结果.有人可以创建自己的错误类,尽管这些错误类很少见,应该基于这两个类之一。事实上(自 R2.14.0 起)try-errors 包含一个simpleError

    e <- try(stop("throwing a try-error"))
    attr(e, "condition")
    

    检测simpleError 很简单。

    is_simple_error <- function(x) inherits(x, "simpleError")
    

    try catch 错误的等价物是

    is_try_error <- function(x) inherits(x, "try-error")
    

    因此,在这里,您可以通过将其应用于您的结果列表来检查结果是否存在问题。

    the_fails <- sapply(results, is_simple_error)
    

    同样,返回消息和调用是单行的。为方便起见,我已将调用转换为字符串,但您可能不希望这样。

    get_simple_error_message <- function(e) e$message
    get_simple_error_call <- function(e) deparse(e$call)
    
    sapply(results[the_fails], get_simple_error_message)
    sapply(results[the_fails], get_simple_error_call)
    

    【讨论】:

    • 你能发布你从第一个attr() 得到的输出吗?我得到NULL 并且不确定与您提到的包含simpleError 的内容相关的内容,尤其是。相对于attributes(e)
    • 对我来说,attr(e, "condition") 返回&lt;simpleError in doTryCatch(return(expr), name, parentenv, handler): throwing a try-error&gt;
    • 啊哈:我使用的是 {2.13.1, 2.13.2},而您看到的行为是 2.14.0 的新行为(根据 NEWS 文件)。我想是时候告别旧的、熟悉的错误并欢迎新的错误进入我的生活了。嗯,是时候升级了。 :)
    • 接受了这个答案,因为您指出有两种类型的错误需要查询,而不仅仅是我的代码会检查的普通错误。
    【解决方案4】:

    我按照这个问题的描述使用 try 和 catch: How do I save warnings and errors as output from a function?

    这个想法是循环中的每个项目都返回一个包含三个元素的列表:返回值、任何警告和任何错误。结果是一个列表列表,然后可以查询这些列表,不仅可以找出循环中每个项目的值,还可以找出循环中的哪些项目有警告或错误。

    在这个例子中,我会做这样的事情:

    library(foreach)
    library(doMC)
    registerDoMC(2)
    results = foreach(ix = 1:10, .errorhandling = "pass") %dopar%{
      catchToList({
        if(ix == 6){
            stop("Perfect")
        } 
        if(ix == 7){
            stop("LuckyPrime")
        } else {
            ix
        }
      })
    }
    

    然后我会像这样处理结果

    > ok <- sapply(results, function(x) is.null(x$error))
    > which(!ok)
    [1] 6 7
    > sapply(results[!ok], function(x) x$error)
    [1] "Perfect"    "LuckyPrime"
    > sapply(results[ok], function(x) x$value)
    [1]  1  2  3  4  5  8  9 10
    

    catchToList 的结果提供一个类并重载一些访问函数以使上述语法更容易,这将是相当简单的,但我还没有发现真正的需要。

    【讨论】:

    • +1 这里和来自该链接的许多非常有趣的想法以及它所链接的其他相关问答帖子。
    • Martin 在链接中的回答非常好,但对于传递公式来说不是很可靠。 ://
    • @RomanLuštrik:是的。也许 OP 中的catchToList 解决方案(在那个链接上)会更好用?
    猜你喜欢
    • 2013-07-06
    • 2017-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多