R 如果遇到非有限值(NA、NaN或Inf),如何强制执行错误
Matlab中有一个条件调试标志我忽略了:R 如果遇到非有限值(NA、NaN或Inf),如何强制执行错误,r,debugging,conditional,nan,R,Debugging,Conditional,Nan,Matlab中有一个条件调试标志我忽略了:dbstop if infnan。如果设置,当遇到Inf或NaN时,此条件将停止代码执行(IIRC,Matlab没有NAs) 如何在R中以比在每次赋值操作后测试所有对象更有效的方式实现这一点 目前,我认为实现这一点的唯一方法是通过以下黑客: 在可能遇到这些值的所有位置之后手动插入测试(例如,除法,其中可能出现0除法)。测试将在每个元素上使用is.finite() 使用body() 修改R的源代码(?!?) 尝试使用traceem识别已更改的变量,并仅检查
dbstop if infnan
。如果设置,当遇到Inf
或NaN
时,此条件将停止代码执行(IIRC,Matlab没有NAs)
如何在R中以比在每次赋值操作后测试所有对象更有效的方式实现这一点
目前,我认为实现这一点的唯一方法是通过以下黑客:
is.finite()
body()
traceem
识别已更改的变量,并仅检查这些变量是否存在错误值duplicate
函数
有没有我错过的更好的方法?也许是马克·布拉文顿(Mark Bravington)、卢克·蒂尔尼(Luke Tierney)的一些聪明的工具,或者是一些相对基本的东西——类似于编译R时的options()
参数或标志
示例代码下面是一些非常简单的示例代码,用于测试,其中包含了Josh O'Brien提出的addTaskCallback
函数。代码不会中断,但在第一种情况下会发生错误,而在第二种情况下不会发生错误(即badDiv(0,0,FALSE)
不会中止)。我仍在调查回调,因为这看起来很有希望
badDiv <- function(x, y, flag){
z = x / y
if(flag == TRUE){
return(z)
} else {
return(FALSE)
}
}
addTaskCallback(stopOnNaNs)
badDiv(0, 0, TRUE)
addTaskCallback(stopOnNaNs)
badDiv(0, 0, FALSE)
badDiv恐怕没有这样的捷径。从理论上讲,在unix上有SIGFPE
,您可以利用它,但实际上是这样的
没有标准的方法来启用FP操作来捕获它(即使C99也不包括这方面的规定)-它是高度系统特定的(例如Linux上的feenableexcept
,AIX上的FP\u enable\u all
等等),或者需要为目标CPU使用汇编程序
如今,FP操作通常以向量单位(如SSE)进行,因此您甚至无法确定FPU是否参与其中
R截取一些操作,比如NaN
s、NA
s,并分别处理它们,这样它们就不会进入FP代码
也就是说,如果您足够努力(禁用SSE等),您可以攻击自己的R,它将捕获平台和CPU的一些异常。这不是我们会考虑建立R,而是为了一个特殊的目的,它可能是可行的。
但是,除非您更改R内部代码,否则它仍然无法捕获NaN
/NA
操作。此外,您必须检查正在使用的每个包,因为它们可能在其C代码中使用FP操作,也可能单独处理NA
/NaN
如果您只担心被零除或过流/下流之类的问题,那么上面的方法会起作用,并且可能是最接近解决方案的方法
仅仅检查您的结果可能不太可靠,因为您不知道结果是否基于某个中间NaN
计算,该计算更改了聚合值,该值可能不需要也是NaN
。如果您愿意放弃这种情况,那么您可以简单地递归地遍历结果对象或工作区。这不应该是非常低效的,因为您只需要担心REALSXP
,而不需要担心其他任何事情(除非您也不喜欢NA
s,否则您会有更多的工作)
这是一个可用于递归遍历R对象的示例代码:
static int do_isFinite(SEXP x) {
/* recurse into generic vectors (lists) */
if (TYPEOF(x) == VECSXP) {
int n = LENGTH(x);
for (int i = 0; i < n; i++)
if (!do_isFinite(VECTOR_ELT(x, i))) return 0;
}
/* recurse into pairlists */
if (TYPEOF(x) == LISTSXP) {
while (x != R_NilValue) {
if (!do_isFinite(CAR(x))) return 0;
x = CDR(x);
}
return 1;
}
/* I wouldn't bother with attributes except for S4
where attributes are slots */
if (IS_S4_OBJECT(x) && !do_isFinite(ATTRIB(x))) return 0;
/* check reals */
if (TYPEOF(x) == REALSXP) {
int n = LENGTH(x);
double *d = REAL(x);
for (int i = 0; i < n; i++) if (!R_finite(d[i])) return 0;
}
return 1;
}
SEXP isFinite(SEXP x) { return ScalarLogical(do_isFinite(x)); }
# in R: .Call("isFinite", x)
static int do_isFinite(SEXP x){
/*递归到通用向量(列表)*/
if(TYPEOF(x)=VECSXP){
int n=长度(x);
对于(int i=0;i
下面概述的想法(及其实现)非常不完善。我甚至不太愿意提出这个建议,但是:(a)我认为它有点有趣,尽管它很丑陋;(b)我能想到一些有用的情况。考虑到你现在听起来像是在每次计算后手动插入支票,我希望你的情况就是这样
我的是两步破解。Fi
# Sketch of a function that tests for NaNs in several types of objects
nanDetector <- function(X) {
# To examine data frames
if(is.data.frame(X)) {
return(any(unlist(sapply(X, is.nan))))
}
# To examine vectors, matrices, or arrays
if(is.numeric(X)) {
return(any(is.nan(X)))
}
# To examine lists, including nested lists
if(is.list(X)) {
return(any(rapply(X, is.nan)))
}
return(FALSE)
}
# Set up the taskCallback
stopOnNaNs <- function(...) {
if(nanDetector(.Last.value)) {stop("NaNs detected!\n")}
return(TRUE)
}
addTaskCallback(stopOnNaNs)
# Try it out
j <- 1:00
y <- rnorm(99)
l <- list(a=1:4, b=list(j=1:4, k=NaN))
# Error in function (...) : NaNs detected!
# Subsequent time consuming code that could be avoided if the
# error thrown above is used to stop its evaluation.