R 调试的一般建议

当使用我写的 R 函数时,我得到一个错误:

Warning messages:
1: glm.fit: algorithm did not converge
2: glm.fit: algorithm did not converge

我所做的:

  1. 单步执行函数
  2. 添加 print 以查明错误发生在哪一行表明有两个函数不应该使用 glm.fit。它们是 window()save()

我的一般方法包括添加 printstop命令,逐行遍历函数,直到找到异常。

然而,我并不清楚在代码中使用这些技术时这个错误是从哪里来的。我甚至不能确定代码中的哪些函数依赖于 glm.fit。我该如何诊断这个问题呢?

46512 次浏览

迄今为止,我见过的最好的演练是:

Http://www.biostat.jhsph.edu/%7erpeng/docs/r-debug-tools.pdf

有人同意或不同意吗?

Mark Bravington 的调试器,作为 CRAN 上的 debug包提供,非常好,非常直接。

library(debug);
mtrace(myfunction);
myfunction(a,b);
#... debugging, can query objects, step, skip, run, breakpoints etc..
qqq(); # quit the debugger only
mtrace.off(); # turn off debugging

The code pops up in a highlighted Tk window so you can see what's going on and, of course you can call another mtrace() while in a different function.

高温

我想说调试是一门艺术,所以没有明确的灵丹妙药。任何语言都有很好的调试策略,这里也适用(例如 读这篇好文章)。例如,第一件事是到 再现问题... ... 如果你不能做到这一点,那么你需要获得更多的信息(例如使用日志)。一旦你可以复制它,你需要 减少它下降到源。

与其说这是一个“把戏”,不如说我有一个最喜欢的调试例程:

  1. 当出现错误时,我通常做的第一件事是通过调用 traceback()来查看堆栈跟踪: 这将显示出错误发生的位置,如果您有多个嵌套函数,这一点尤其有用。
  2. 接下来,我将设置 options(error=recover); 这将立即切换到发生错误的浏览器模式,因此您可以从那里浏览工作区。
  3. If I still don't have enough information, I usually use the debug() function and step through the script line by line.

在 R 2.10中(处理脚本文件时) ,最好的新技巧是使用 findLineNum()setBreakpoint()函数。

作为最后的注释: 根据错误,在外部函数调用(特别是在处理 S4类时)周围设置 try()tryCatch()语句也非常有帮助。这有时会提供更多的信息,并且还使您能够更好地控制在运行时如何处理错误。

These related questions have a lot of suggestions:

经过所有的步骤建议在这里我刚刚了解到,设置 .verbose = TRUEforeach()也给了我吨有用的信息。特别是,foreach(.verbose=TRUE)准确地显示 foreach 循环中错误发生的位置,而 traceback()不查看 foreach 循环内部。

So browser(), traceback() and debug() walk into a bar, but trace() waits outside and keeps the motor running.

通过在函数中插入 browser,执行将停止并等待输入。您可以使用 n(或者 Enter)向前移动,使用 c运行整个块(迭代) ,使用 f完成当前循环/函数,或者使用 Q退出; 请参阅 ?browser

使用 debug时,效果与使用浏览器时相同,但这会在函数开始时停止执行。同样的捷径。这个函数将处于“调试”模式,直到您使用 undebug关闭它(也就是说,在 debug(foo)之后,运行函数 foo将每次进入“调试”模式,直到您运行 undebug(foo))。

一个更短暂的替代方案是 debugonce,它将在下次计算函数时从函数中删除“调试”模式。

traceback will give you the flow of execution of functions all the way up to where something went wrong (an actual error).

您可以使用 trace(例如 browser)在函数中插入代码位(即自定义函数)。这对于包中的函数非常有用,而且您太懒了,无法获得折叠得很好的源代码。

My general strategy looks like:

  1. 运行 traceback()查找明显的问题
  2. options(warn=2)设置为将警告视为错误
  3. 如果发生错误,将 options(error=recover)设置为单步进入调用堆栈

在某个时刻,glm.fit被调用。这意味着您调用的函数之一或这些函数调用的函数之一正在使用 glmglm.fit

另外,正如我在上面的评论中提到的,这是一个 警告而不是一个 错误,这有很大的区别。您不能通过一个警告触发 R 的任何调试工具(在有人告诉我错了之前使用默认选项;)。

如果我们更改选项以将警告转换为错误,那么我们可以开始使用 R 的调试工具。来自 ?options的消息:

 ‘warn’: sets the handling of warning messages.  If ‘warn’ is
negative all warnings are ignored.  If ‘warn’ is zero (the
default) warnings are stored until the top-level function
returns.  If fewer than 10 warnings were signalled they will
be printed otherwise a message saying how many (max 50) were
signalled.  An object called ‘last.warning’ is created and
can be printed through the function ‘warnings’.  If ‘warn’ is
one, warnings are printed as they occur.  If ‘warn’ is two or
larger all warnings are turned into errors.

所以如果你逃跑

options(warn = 2)

然后运行代码,R 将抛出一个错误

traceback()

查看调用堆栈。

> options(warn = 2)
> foo <- function(x) bar(x + 2)
> bar <- function(y) warning("don't want to use 'y'!")
> foo(1)
Error in bar(x + 2) : (converted from warning) don't want to use 'y'!
> traceback()
7: doWithOneRestart(return(expr), restart)
6: withOneRestart(expr, restarts[[1L]])
5: withRestarts({
.Internal(.signalCondition(simpleWarning(msg, call), msg,
call))
.Internal(.dfltWarn(msg, call))
}, muffleWarning = function() NULL)
4: .signalSimpleWarning("don't want to use 'y'!", quote(bar(x +
2)))
3: warning("don't want to use 'y'!")
2: bar(x + 2)
1: foo(1)

这里您可以忽略标记为 4:或更高的帧。我们看到 foo称为 bar,而 bar生成了警告。这将显示调用 glm.fit的函数。

如果您现在想要调试它,我们可以转向另一个选项,告诉 R 在遇到错误时进入调试器,并且由于我们已经制造了警告错误,所以当初始警告被触发时,我们将得到一个调试器。为此,你应该逃跑:

options(error = recover)

这里有一个例子:

> options(error = recover)
> foo(1)
Error in bar(x + 2) : (converted from warning) don't want to use 'y'!


Enter a frame number, or 0 to exit


1: foo(1)
2: bar(x + 2)
3: warning("don't want to use 'y'!")
4: .signalSimpleWarning("don't want to use 'y'!", quote(bar(x + 2)))
5: withRestarts({
6: withOneRestart(expr, restarts[[1]])
7: doWithOneRestart(return(expr), restart)


Selection:

然后,您可以进入这些框架中的任何一个,查看抛出警告时发生了什么。

若要将上述选项重置为默认值,请输入

options(error = NULL, warn = 0)

As for the specific warning you quote, it is highly likely that you need to allow more iterations in the code. Once you've found out what is calling glm.fit, work out how to pass it the control argument using glm.control - see ?glm.control.

我喜欢 Gavin 的回答: 我不知道选项(错误 = 恢复)。我还喜欢使用“调试”包,它提供了一种可视化的方式来逐步通过您的代码。

require(debug)
mtrace(foo)
foo(1)

此时,它将打开一个单独的调试窗口,显示函数,并用一条黄线显示您在代码中的位置。在主窗口中,代码进入调试模式,你可以按回车键一步步通过代码(还有其他命令) ,并检查变量值,等等。调试窗口中的黄线不断移动以显示您在代码中的位置。完成调试后,可以通过以下方式关闭跟踪:

mtrace.off()

正如在 另一个问题中向我指出的那样,Rprof()summaryRprof()对于 找到程序的慢部分来说是不错的工具,它们可能会从加速或转向 C/C + + 实现中受益。如果您正在从事模拟工作或其他计算或数据密集型活动,这可能更适用。profr包裹可以帮助可视化的结果。

I'm on a bit of a learn-about-debugging kick, so another suggestion from 另一条线:

  • options(warn=2)设置为将警告视为错误

您还可以使用您最喜欢的调试功能,使用 options在出现错误或警告时将您直接投入到最激烈的操作中。例如:

  • 设置 options(error=recover)以在发生错误时运行 recover(),正如 Shane 所指出的(并且正如在 调试指南中所记录的那样)。或者任何你觉得有用的方便的函数。

另外两种方法来自@Shane 的 链接:

  • try()包装一个内部函数调用以返回关于它的更多信息。
  • 对于 * application 函数,使用 .inform=TRUE(来自 plyr 包)作为 application 命令的一个选项

@ Joshua Ulrich 也指出使用经典 browser()命令的条件能力打开/关闭调试的一种简单方法:

  • 将您可能想要调试 browser(expr=isTRUE(getOption("myDebug")))的函数放入其中
  • 并将全局选项设置为 options(myDebug=TRUE)
  • 您甚至可以包装浏览器调用: myBrowse <- browser(expr=isTRUE(getOption("myDebug"))),然后使用 myBrowse()调用,因为它使用全局。

然后是 R 2.10中提供的新函数:

  • findLineNum()获取源文件名和行号,并返回函数和环境。这似乎是有帮助的,当你 source() a。R 文件,并且它在 # n 行返回一个错误,但是您需要知道在 # n 行的函数是什么。
  • setBreakpoint()获取源文件名和行号,并在其中设置断点

代码工具包,特别是它的 checkUsage函数可以特别有助于快速检测编译器通常会报告的语法和样式错误(未使用的局部变量、未定义的全局函数和变量、部分参数匹配等等)。

对于 trace()来说,setBreakpoint()是一个更加用户友好的前端。详细的内部如何这个工程是在一个 最近在 R 杂志上发表的文章提供。

If you are trying to debug someone else's package, once you have located the problem you can over-write their functions with fixInNamespace and assignInNamespace, but do not use this in production code.

None of this should preclude the tried-and-true 标准 R 调试工具, some of which are above and others of which are not. In particular, the 事后调试工具 are handy when you have a time-consuming bunch of code that you'd rather not re-run.

最后,对于那些看起来不会抛出错误消息的棘手问题,您可以使用 options(error=dump.frames),如下面这个问题中所详述的: 未引发错误的错误

根据我收到的 给你的答案,你一定要检查一下 options(error=recover)的设置。当设置完成后,在遇到错误时,您将在控制台上看到类似以下内容的文本(traceback输出) :

> source(<my filename>)
Error in plot.window(...) : need finite 'xlim' values
In addition: Warning messages:
1: In xy.coords(x, y, xlabel, ylabel, log) : NAs introduced by coercion
2: In min(x) : no non-missing arguments to min; returning Inf
3: In max(x) : no non-missing arguments to max; returning -Inf


Enter a frame number, or 0 to exit


1: source(<my filename>)
2: eval.with.vis(ei, envir)
3: eval.with.vis(expr, envir, enclos)
4: LinearParamSearch(data = dataset, y = data.frame(LGD = dataset$LGD10), data.names = data
5: LinearParamSearch.R#66: plot(x = x, y = y.data, xlab = names(y), ylab = data.names[i])
6: LinearParamSearch.R#66: plot.default(x = x, y = y.data, xlab = names(y), ylab = data.nam
7: LinearParamSearch.R#66: localWindow(xlim, ylim, log, asp, ...)
8: LinearParamSearch.R#66: plot.window(...)


Selection:

在这一点上,您可以选择输入哪个“框架”。当你进行选择时,你会进入 browser()模式:

Selection: 4
Called from: stop(gettextf("replacement has %d rows, data has %d", N, n),
domain = NA)
Browse[1]>

您可以像发生错误时一样检查环境。完成后,键入 c返回到帧选择菜单。当您完成后,正如它告诉您的那样,键入 0退出。

我把这个答案给了一个 more recent question,,但是为了完整起见,我把它添加到这里。

就个人而言,我倾向于不使用函数来调试。我经常发现,这种做法带来的麻烦和解决的一样多。另外,从 Matlab 背景出发,我希望能够在集成开发环境(IDE)中完成这项工作,而不是在代码中完成这项工作。使用 IDE 可以保持代码的清晰和简单。

对于 R,我使用一个名为“ RStudio”(http://www.rstudio.com)的 IDE,它可用于 windows、 mac 和 linux,并且非常容易使用。

自2013年10月以来 RStudio 的版本(0.98左右?)具有在脚本和函数中添加断点的能力: 要做到这一点,只需单击文件的左边缘来添加断点。您可以设置一个断点,然后从该点开始逐步执行。您还可以访问该环境中的所有数据,因此可以尝试使用命令。

详情请参阅 http://www.rstudio.com/ide/docs/debugging/overview。如果您已经安装了 RStudio,那么您可能需要升级——这是一个相对较新的(2013年末)特性。

您还可以找到具有类似功能的其他 IDE。

不可否认,如果它是一个内置函数,您可能不得不求助于其他人在这个讨论中提出的一些建议。但是,如果需要修复的是您自己的代码,那么基于 IDE 的解决方案可能正是您所需要的。

调试没有实例引用的 参考类别方法

ClassName$trace(methodName, browser)

我开始觉得不打印错误行号——一个最基本的要求—— BY DEFAILT ——是 R/R 工作室中的一个笑话。我找到的找到错误发生地点的唯一可靠方法是额外调用 追踪()并查看顶行。