在R中调试的一般建议

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

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

我做了什么:

  1. 逐步完成功能
  2. 添加打印以查明错误发生在哪一行提示两个不应该使用glm.fit 。 他们是window()save()

我的一般方法包括添加printstop命令,逐行逐行扫描功能,直到找到异常。

然而,我不清楚使用这些技术的错误来自代码。 我甚至不确定代码中的哪些函数依赖于glm.fit 。 我如何去诊断这个问题?

我会说调试是一种艺术形式,所以没有明确的银弹。 在任何语言中都有很好的调试策略,也适用于这里(例如阅读这篇不错的文章 )。 例如,第一件事是重现问题 …如果你不能这样做,那么你需要获得更多的信息(例如记录)。 一旦你可以重现它,你需要把它降低到源。

而不是“诡计”,我会说我有一个最喜欢的调试程序:

  1. 当发生错误时,我通常通过调用traceback()函数来查看堆栈跟踪信息,该信息显示错误发生的位置,如果您有多个嵌套函数,那么该错误特别有用。
  2. 接下来我会设置options(error=recover) ; 这会立即切换到发生错误的浏览器模式,所以您可以从那里浏览工作区。
  3. 如果我仍然没有足够的信息,我通常使用debug()函数并逐行执行脚本。

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

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

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

  • R语言的调试工具
  • 调试lapply / sapply调用
  • 在R中发生错误后获取变量的状态
  • R脚本行号错误?

到目前为止,我所见过的最佳演练是:

~rpeng/docs/R-debug-tools.pdf

有人同意/不同意?

正如我在另一个问题中指出的那样, Rprof()summaryRprof()是很好的工具,用于查找可能从加速或转向C / C ++实现中受益的程序的慢速部分 。 如果您正在进行模拟工作或其他计算或数据密集型活动,则这可能更适用。 profr软件包可以帮助可视化结果。

我正在进行一些关于调试的学习,所以另外一个线程的建议是:

  • 设置options(warn=2)来处理警告,如错误

当出现错误或警告时,您也可以使用options使您最喜欢的调试功能进入操作的热度。 例如:

  • 设置options(error=recover)在发生错误时运行recover() ,正如Shane指出的那样(以及R调试指南中记录的那样),或者其他任何可以运行的方便的函数。

另外两个方法来自@ Shane的链接之一 :

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

@JoshuaUlrich 还指出了一种使用经典的browser()命令的条件能力打开/关闭调试的巧妙方法:

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

然后R 2.10中提供了新的功能:

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

codetools包,特别是它的checkUsage函数对于快速拾取编译器通常会报告的语法和样式错误(未使用的局部变量,未定义的全局函数和变量,部分参数匹配等)特别有帮助。

setBreakpoint()setBreakpoint()的更加用户友好的前端。 关于这个内部工作的细节可以在最近的R期刊文章中找到 。

如果您试图调试其他人的软件包,一旦找到问题,可以使用fixInNamespaceassignInNamespace 重写它们的函数 ,但不要在生产代码中使用它。

这些都不应该排除已经被证实的标准R调试工具 ,其中一些是上面的,另外一些则不是。 特别是,当你有一大堆你不想重新运行的代码时,这种验尸调试工具是非常方便的。

最后,对于似乎不会引发错误消息的棘手问题,您可以使用在此问题中详述的options(error=dump.frames) : 错误不会引发错误

在某个时候, 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:和更高的帧。 我们看到foobar ,那个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.fit ,就可以使用glm.control了解如何传递control参数 – 参见?glm.control

因此, browser()traceback()debug()走到一个酒吧,但trace()等待外面,保持电机运行。

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

通过debug ,您可以获得与浏览器相同的效果,但这会在开始时停止执行一个函数。 相同的捷径适用。 这个功能是在“调试”模式下,而不是你关掉usb undebug 。 从2.10开始,你也可以进行debugonce

traceback将为您提供执行功能的流程,直到出错的地方(实际的错误)。

您可以在使用trace功能(例如browser )中插入代码位(即自定义函数)。 这对于来自软件包的函数是有用的,而你却懒得得到很好的折叠的源代码。

我的总体战略是这样的:

  1. 运行traceback()查看显而易见的问题
  2. 设置options(warn=2)来处理警告,如错误
  3. 设置options(error=recover)进入错误的调用堆栈

在完成了这里提到的所有步骤之后,我刚刚学习了在foreach()中设置.verbose = TRUE ,这也给了我很多有用的信息。 特别是foreach(.verbose=TRUE)显示foreach循环内部发生错误的位置,而traceback()不在foreach循环内部。

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

我喜欢加文的答案:我不知道选项(错误=恢复)。 我也喜欢使用“调试”包,提供一个可视化的方式来通过你的代码。

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

在这一点上,它打开一个单独的调试窗口,显示你的功能,用黄线显示你在代码中的位置。 在主窗口中,代码进入调试模式,您可以继续点击进入以逐步执行代码(并且还有其他命令),并检查变量值等。调试窗口中的黄色线路不断移动以显示哪里你在代码中。 完成调试后,可以关闭跟踪:

 mtrace.off() 

基于我在这里收到的答案,你一定要检查出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退出。

我给了这个答案更近的一个问题,但是为了完整性,我在这里添加它。

我个人倾向于不使用函数进行调试。 我经常发现,这会造成尽可能多的麻烦,因为它解决了。 另外,来自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的解决方案可能就是您所需要的。

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

 ClassName$trace(methodName, browser)