在 R 中调试的一般建议

Posted

技术标签:

【中文标题】在 R 中调试的一般建议【英文标题】:General suggestions for debugging in R 【发布时间】:2011-05-25 11:00:50 【问题描述】:

使用我编写的 R 函数时出现错误:

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

我做了什么:

    单步执行函数 添加打印以找出错误发生在哪一行,这表明两个函数不应使用glm.fit。他们是window()save()

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

但是,我不清楚使用这些技术在代码中的错误来源。我什至不确定代码中的哪些函数依赖于glm.fit。我该如何诊断这个问题?

【问题讨论】:

在Debugging in R查看邓肯默多克的页面 好的,我会明确说明:这是一个警告而不是一个错误 @gavin-simpson 我没有意识到存在技术差异,感谢您指出这一点。但到头来,说明我之前的机能功能失调了。 @David +1 表示“……我以前的功能功能失调。” @David:请回复您的 p.s.。这为没有示例的问题增加了一个维度;即当只产生警告时如何让R进入调试模式?如果您忽略了这个细节,我们都不会将您指向options(warn = 2)。因此,在这种情况下,细节对于回答您的一般问题至关重要。来自我的 +1。 【参考方案1】:

到目前为止我见过的最好的演练是:

http://www.biostat.jhsph.edu/%7Erpeng/docs/R-debug-tools.pdf

有人同意/不同意吗?

【讨论】:

非常详尽的指南 - 描述了 R 核心中包含的基本工具:debug()、traceback() 和 recover()。【参考方案2】:

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

代码会在突出显示的 Tk 窗口中弹出,因此您可以查看发生了什么,当然您可以在不同的函数中调用另一个 mtrace()

HTH

【讨论】:

【参考方案3】:

我会说调试是一种艺术形式,所以没有明确的灵丹妙药。任何语言都有很好的调试策略,它们也适用于这里(例如read this nice article)。例如,第一件事是重现问题...如果您不能这样做,那么您需要获取更多信息(例如使用日志记录)。一旦你可以重现它,你需要reduce它到源头。

我会说我有一个最喜欢的调试例程,而不是“技巧”:

    发生错误时,我通常做的第一件事是通过调用traceback() 查看堆栈跟踪:它会显示错误发生的位置,如果您有多个嵌套函数,这尤其有用。 接下来我将设置options(error=recover);这会立即切换到发生错误的浏览器模式,因此您可以从那里浏览工作区。 如果我仍然没有足够的信息,我通常会使用debug() 函数并逐行执行脚本。

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

作为最后的评论:根据错误,围绕外部函数调用设置try()tryCatch() 语句也非常有帮助(尤其是在处理S4 类时)。这有时会提供更多信息,并且还可以让您更好地控制在运行时如何处理错误。

这些相关问题有很多建议:

Debugging tools for the R language Debugging lapply/sapply calls Getting the state of variables after an error occurs in R R script line numbers at error?

【讨论】:

你也可以将 debugonce() 添加到 debug() 中。 虽然不仅在调试时有用,但 fix(df1) 会打开图形 R 编辑器,其中加载了数据框 df1,您可以即时编辑或浏览一下。 在 R 中调试似乎非常困难,例如there is no easy solution to see code lines of warnings browser() 用于不触发警告/错误的错误(来源:此页面上的 Roman Luštrik)。任何其他工具,如browser()【参考方案4】:

在完成此处建议的所有步骤后,我刚刚了解到在foreach() 中设置.verbose = TRUE 也可以为我提供大量有用的信息。特别是foreach(.verbose=TRUE) 准确显示了在 foreach 循环中发生错误的位置,而traceback() 不查看 foreach 循环内部。

【讨论】:

【参考方案5】:

所以browser()traceback()debug() 走进了一家酒吧,但trace() 在外面等待并保持电机运转。

通过在函数的某处插入browser,执行将停止并等待您的输入。您可以使用 n(或 Enter)向前移动,使用 c 运行整个块(迭代),使用 f,或用 Q 退出;见?browser

使用debug,您可以获得与使用浏览器相同的效果,但这会在函数的开始处停止执行。相同的快捷方式适用。该函数将处于“调试”模式,直到您使用undebug 将其关闭(即在debug(foo) 之后,每次运行函数foo 都会进入“调试”模式,直到您运行undebug(foo))。

一个更临时的替代方案是debugonce,它将在下一次评估后从函数中删除“调试”模式。

traceback 将为您提供函数的执行流程,直至出现问题(实际错误)。

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

【讨论】:

【参考方案6】:

我的总体策略如下:

    运行 traceback() 以查看是否存在明显问题 设置options(warn=2) 将警告视为错误 设置 options(error=recover) 以在出错时进入调用堆栈

【讨论】:

【参考方案7】:

在某些时候,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)

至于您引用的具体警告,您很可能需要在代码中允许更多迭代。一旦你找到了调用 glm.fit 的内容,请弄清楚如何使用 glm.controlcontrol 参数传递给它 - 请参阅 ?glm.control

【讨论】:

很好的答案。悲观的一点是,这类收敛错误通常发生在不稳定/不稳定的数据集(完全分离等)中,并且“收敛得很好”和“不收敛但不能通过增加数量来修复”之间的窗口迭代次数——需要一些更剧烈的改变'通常是狭窄的 加文,我领先你 25 秒。我要求您删除您过于有用的答案,并停止窃取我的赞成票。 ;-) @Ben 很好。如果大卫的问题是分离,那么增加迭代次数应该没有帮助,它仍然应该无法收敛。此时查看估计值和标准误差可能表明存在问题。如果分离或类似问题是一个问题,我还希望看到有关拟合值的警告,数值为 0 或 1。如果增加迭代次数没有帮助,David 可以发布另一个 Q 寻求帮助,我可以窃取更多 @Joshua 的支持 ;-) @Joshua,没有办法打败他。我不再计算我可能因为他而失去的赞成票。但无论如何,到目前为止,他提供的帮助说明了这一点。如果你打败了他,就必须找到自己的利基。我建议在这里每次击键点赞... :) 该死的@ran2,你挫败了我接管世界的卑鄙狡猾的计划,哇哈哈哈哈哈!!!!【参考方案8】:

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

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

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

mtrace.off()

【讨论】:

【参考方案9】:

正如在another question 中向我指出的那样,Rprof()summaryRprof() 是find slow parts of your program 的好工具,它们可能受益于加速或转向C/C++ 实现。如果您正在进行模拟工作或其他计算或数据密集型活动,这可能更适用。 profr package 可以帮助可视化结果。

我正在学习调试方面的知识,所以another thread 的另一个建议是:

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

您还可以使用options 在发生错误或警告时立即投入操作,使用您最喜欢的调试功能。例如:

options(error=recover) 设置为在发生错误时运行recover(),正如Shane 所指出的那样(正如R debugging guide 中所记录的那样。或任何其他您认为有用的方便运行的函数。

还有来自@Shane 的links 之一的另外两种方法:

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

@JoshuaUlrich also pointed out 使用经典browser() 命令的条件能力打开/关闭调试的巧妙方法:

放入你可能要调试的函数browser(expr=isTRUE(getOption("myDebug"))) 并通过options(myDebug=TRUE)设置全局选项 您甚至可以包装浏览器调用:myBrowse &lt;- browser(expr=isTRUE(getOption("myDebug"))),然后使用 myBrowse() 调用,因为它使用全局变量。

然后是 R 2.10 中可用的新功能:

findLineNum() 获取源文件名和行号并返回函数和环境。当您 source() 一个 .R 文件并在第 #n 行返回错误时,这似乎很有帮助,但您需要知道第 #n 行是什么函数。 setBreakpoint() 获取源文件名和行号并在此处设置断点

codetools 包,尤其是它的 checkUsage 函数对于快速发现编译器通常会报告的语法和风格错误(未使用的局部变量、未定义的全局函数和变量、部分参数匹配等)特别有帮助第四次)。

setBreakpoint() 是一个比trace() 更加用户友好的前端。 recent R Journal article 中提供了有关其工作原理的内部细节。

如果您尝试调试其他人的包,一旦找到问题,您可以over-write their functions 和fixInNamespaceassignInNamespace,但不要在生产代码中使用它。

这些都不应该排除久经考验的standard R debugging tools,其中一些在上面,而另一些则不是。特别是,post-mortem debugging tools 在您有一堆不想重新运行的耗时代码时非常方便。

最后,对于似乎不会引发错误消息的棘手问题,您可以使用options(error=dump.frames),如本问题所述: Error without an error being thrown

【讨论】:

+1 感谢您为将这些问题合二为一并保持开放状态所做的所有工作!【参考方案10】:

根据我收到的here 的回答,您一定要检查一下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 退出。

【讨论】:

【参考方案11】:

我将此答案提供给 more recent question,,但为了完整起见,我将其添加到此处。

我个人倾向于不使用函数进行调试。我经常发现这带来的麻烦和解决的麻烦一样多。此外,来自 Matlab 背景的我喜欢能够在集成开发环境 (IDE) 中执行此操作,而不是在代码中执行此操作。使用 IDE 可以让您的代码保持简洁。

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

大约从 2013 年 10 月开始的 Rstudio 版本(0.98ish?)能够在脚本和函数中添加断点:为此,只需单击文件的左边距以添加断点。您可以设置一个断点,然后从该点开始逐步执​​行。您还可以访问该环境中的所有数据,因此您可以尝试命令。

详情请见http://www.rstudio.com/ide/docs/debugging/overview。如果您已经安装了 Rstudio,则可能需要升级 - 这是一个相对较新(2013 年末)的功能。

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

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

【讨论】:

【参考方案12】:

在没有实例引用的情况下调试引用类方法

ClassName$trace(methodName, browser)

【讨论】:

【参考方案13】:

我开始认为在 R/Rstudio 中不打印错误行号 - 一个最基本的要求 - BY DEFAILT - 是某种笑话。我发现找到错误发生位置的唯一可靠方法是额外努力调用 traceback() 并查看第一行。

【讨论】:

以上是关于在 R 中调试的一般建议的主要内容,如果未能解决你的问题,请参考以下文章

逆向调试入门-OllyDbg的使用

Linux内核调试技术——进程上下文R状态死锁监测

如何在电脑中将日志一般状态改成调试状态

CORE DUMP生成调试

谷歌浏览器,调试时,页面的刷新,强制刷新+清空缓存 操作详情。

一般在R中编号