R中调试的一般建议


120

使用我编写的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。我该如何诊断该问题?


5
查阅Duncan Murdoch关于“ 在R中调试”
Rob Hyndman,2009年

10
好的,我要说的很明显:那是警告而不是错误
加文·辛普森

10
@ gavin-simpson我没有意识到技术上的差异,感谢您指出这一点。但是最后,这表明我以前的功能不正常。
戴维·勒鲍尔

11
@David +1表示“ ...我以前的功能不正常。”
Joshua Ulrich 2010年

5
@David:是你的ps。如果没有示例,这将为问题增加一个维度;即仅发出警告时如何使R进入调试模式?如果您忽略了这一细节,我们都不会向您指出options(warn = 2)。因此,在这种情况下,细节对于回答您的一般问题至关重要。向我+1。
加文·辛普森

Answers:


167

我会说调试是一种艺术形式,因此没有明确的灵丹妙药。有使用任何语言进行调试的好策略,它们也适用于此(例如,阅读这篇不错的文章)。例如,第一件事就是重现问题 ……如果您无法做到这一点,则需要获取更多信息(例如,使用日志记录)。一旦你能复制它,你需要减少下来的来源。

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

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

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

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

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


8
您也可以将debugonce()添加到debug()中。
Joris Meys 2011年

2
尽管fix(df1)不仅在调试时有用,但它会打开图形R编辑器,并在其中加载数据框df1,您可以随时对其进行编辑或浏览。
德米特里(Dmitrii I)2012年

R中的调试似乎非常困难,例如,没有简单的解决方案来查看警告的代码行
TMS

browser()对于何时出现不会触发警告/错误的错误(来源:本页上的RomanLuštrik)。还有其他工具browser()吗?
PatrickT


32

正如向我指出的另一个问题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")))
  • 并通过设置全局选项 options(myDebug=TRUE)
  • 您甚至可以包装浏览器调用:myBrowse <- browser(expr=isTRUE(getOption("myDebug"))),然后myBrowse()使用进行调用,因为它使用全局变量。

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

  • findLineNum()获取源文件名和行号,并返回函数和环境。当您source()使用.R文件并在#n行返回错误时,这似乎很有用,但是您需要知道在#n行中有什么函数。
  • setBreakpoint() 带有源文件名和行号,并在那里设置断点

编码工具编码工具包,特别是其checkUsage功能特别有帮助在快速回升的语法和文体错误,一个编译器通常会报告(未使用的当地人,未定义的全局函数和变量,部分参数匹配,等等)。

setBreakpoint()是的更人性化的前端trace()最近的R Journal文章中提供了有关其工作原理的详细信息。

如果要调试其他人的程序包,找到问题后,可以使用和覆盖他们的功能,但不要在生产代码中使用此功能fixInNamespaceassignInNamespace

所有这些都不能排除经过实践检验的标准R调试工具,其中一些工具位于上面,而另一些则没有。特别是,当您有很多不想重新运行的耗时代码时,事后调试工具非常方便。

最后,对于似乎没有引发错误消息的棘手问题,可以使用options(error=dump.frames)以下问题中的详细说明: 错误而不会引发错误


1
对于将这些问题合并为一个问题并保持公开状态所做的所有工作,+ 1。
GSee

29

在某个时候,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调用barbar生成了警告。那应该告诉您正在调用哪些函数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,制定出如何通过它的control使用参数glm.control-见?glm.control


4
好答案。悲观主义的一个注解是,这类收敛错误通常在不稳定/不稳定的数据集(完全分离等)下发生,并且“收敛很好”和“不收敛但不能通过增加数量来固定”之间的窗口迭代-需要更多的剧变”往往是窄
奔Bolker

3
加文,我击败了你25秒。我要求您删除过于有用的答案,并停止窃取我的投票。;-)
Joshua Ulrich 2010年

@本大点。如果David的问题是分离,那么增加迭代次数就无济于事,它仍然无法收敛。那时,查看估算值和标准误差可能表明存在问题。如果出现分隔或类似问题,我还希望看到有关数值0或1的拟合值的警告。如果增加迭代次数没有帮助,David可以发布另一个Q来寻求帮助,我可以窃取更多@Joshua的支持;-)
Gavin Simpson 2010年

1
@约书亚,没有办法击败他。我不再计算可能因他而失去的投票数。但是无论如何,他提供的帮助到目前为止是可以解释的。当你击败他时,一定会找到自己的位置。我建议在此为每次击键投票... :)
Matt Bannert 2010年

1
该死的@ ran2,你挫败了我愚蠢的,愚蠢的接管世界的计划,姆瓦哈哈哈哈哈!
加文·辛普森

21

所以browser()traceback()debug()走进一个酒吧,但trace()在门外等候,并保持电机运转。

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

使用debug,您将获得与浏览器相同的效果,但是这会从一开始就停止执行功能。相同的快捷方式适用。该功能将一直处于“调试”模式,直到您使用来关闭它为止undebug(也就是说debug(foo),运行后,foo每次运行该功能都会进入“调试”模式,直到您运行undebug(foo))。

一个更短暂的选择是debugonce,它将在下次评估该功能时从该功能中删除“调试”模式。

traceback 会一直向您提供函数执行流程,直至出现问题的地方(实际错误)。

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



15

通过所有这里提出的步骤去后,我刚刚得知,设置.verbose = TRUEforeach()也给了我很多实用的信息。特别是foreach(.verbose=TRUE)是准确显示了在foreach循环内发生错误的位置,而traceback()没有显示在foreach循环内发生的错误。


13

Mark Bravington的调试器debug非常好,非常直接,因为CRAN上的软件包提供了该调试器。

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()在其他函数中代码。

高温超导


11

我喜欢加文的回答:我不知道期权(错误=恢复)。我还喜欢使用“调试”包,该包提供了直观的方法来逐步执行代码。

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

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

mtrace.off()

5

根据我在这里收到的答案,您绝对应该检查一下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退出。


4

我将此答案提供给了一个最近的问题,但是为了完整起见,在此添加了它。

我个人倾向于不使用函数进行调试。我经常发现这会带来解决的麻烦。另外,来自Matlab的背景,我希望能够在集成开发环境(IDE)中执行此操作,而不是在代码中执行此操作。使用IDE可使您的代码简洁明了。

对于R,我使用一个名为“ RStudio”(http://www.rstudio.com)的IDE,该IDE 可用于Windows,Mac和Linux,并且非常易于使用。

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

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

您可能还会发现具有类似功能的其他IDE。

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


1

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

ClassName$trace(methodName, browser)

0

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.