在 R 中进行调试的一般建议

General suggestions for debugging in R

提问人:David LeBauer 提问时间:12/15/2010 最后编辑:David LeBauer 更新时间:2/8/2022 访问量:47533

问:

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

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

我做了什么:

  1. 单步执行函数
  2. 添加 print 以找出错误发生在哪一行建议两个不应使用 .他们是 和 .glm.fitwindow()save()

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

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

调试 R-FAQ

评论

11赞 Gavin Simpson 12/15/2010
好的,我会说一个显而易见的事实:这是一个警告,而不是一个错误
10赞 David LeBauer 12/15/2010
@gavin辛普森:我没有意识到存在技术差异,感谢您指出这一点。但归根结底,这表明我以前的功能功能失调了。
11赞 Joshua Ulrich 12/15/2010
@David +1 表示“......我以前的功能功能失调了。
5赞 Gavin Simpson 12/15/2010
@David:回复你的p.s.。这为如果没有这个例子就会错过的问题增加了一个维度;即,当只产生警告时,如何让 R 步入调试模式?如果你把这个细节省略了,我们都不会给你指出。因此,在这种情况下,细节对于回答您的一般问题至关重要。+1 来自我。options(warn = 2)
5赞 Rob Hyndman 7/24/2009
查看 Duncan Murdoch 关于 R 调试的页面

答:

40赞 Christopher DuBois 7/23/2009 #1

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

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

有人同意/不同意吗?

评论

0赞 Sharpie 7/23/2009
非常详尽的指南 - 描述了 R 核心中包含的基本工具:debug()、traceback() 和 recover()。
14赞 David Lawrence Miller 7/23/2009 #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型

171赞 Shane 12/11/2009 #3

我想说的是,调试是一种艺术形式,所以没有明确的灵丹妙药。在任何语言中都有很好的调试策略,它们也适用于这里(例如,阅读这篇不错的文章)。例如,第一件事是重现问题......如果你做不到这一点,那么你需要获得更多的信息(例如,关于日志记录)。一旦你可以重现它,你就需要把它减少到源头。

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

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

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

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

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

评论

9赞 Joris Meys 2/28/2011
您也可以将 debugonce() 添加到 debug() 中。
2赞 Dmitrii I. 12/23/2012
虽然 fix(df1) 不仅在调试时有用,但它会打开图形 R 编辑器,其中加载了数据帧 df1,您可以即时编辑或浏览一下。
0赞 Tomas 8/16/2013
在 R 中调试似乎非常困难,例如,没有简单的解决方案来查看警告的代码行
0赞 PatrickT 6/25/2016
browser()当存在未触发警告/错误的错误时(图片来源:本页的 Roman Luštrik)。任何其他工具,例如?browser()
16赞 Michael Schneider 10/30/2010 #4

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

24赞 Roman Luštrik 12/15/2010 #5

于是,走进一家酒吧,但在外面等着,让电机继续运转。browser()traceback()debug()trace()

通过在函数的某个位置插入,执行将停止并等待您的输入。您可以使用 (或 ) 向前移动,使用 运行整个块(迭代),使用 完成当前循环/函数 ,或退出 ;看。browsernEntercfQ?browser

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

更瞬态的替代方法是 ,它将在下次评估函数后从函数中删除“调试”模式。debugonce

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

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

19赞 Joshua Ulrich 12/15/2010 #6

我的总体策略是这样的:

  1. 跑去看看,寻找明显的问题traceback()
  2. 设置为将警告视为错误options(warn=2)
  3. 设置为在出错时单步执行调用堆栈options(error=recover)
31赞 Gavin Simpson 12/15/2010 #7

在某个时候,正在被调用。这意味着您调用的函数之一或这些函数调用的函数之一正在使用 、 。glm.fitglmglm.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:foobarbarglm.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)

至于你引用的具体警告,你很可能需要在代码中允许更多的迭代。一旦你发现了什么在调用,就弄清楚如何使用 - see 来传递参数。glm.fitcontrolglm.control?glm.control

评论

4赞 Ben Bolker 12/15/2010
很好的答案。悲观主义的一个特点是,这种收敛错误经常发生在不稳定/不稳定的数据集(完全分离等)中,并且“收敛很好”和“非收敛但无法通过增加迭代次数来修复——需要一些更剧烈的改变”之间的窗口通常很窄
3赞 Joshua Ulrich 12/15/2010
加文,我以 25 秒的优势击败了你。我要求你删除你过于有用的答案,并停止窃取我的赞成票。;-)
0赞 Gavin Simpson 12/15/2010
@Ben很好的观点。如果 David 的问题是分离,那么增加迭代次数应该无济于事,它仍然应该无法收敛。在这一点上,查看估计值和标准误差可能表明存在问题。如果分离或类似问题是一个问题,我还希望看到有关数字 0 或 1 拟合值的警告。如果增加迭代次数没有帮助,David 可以发布另一个 Q 寻求帮助,我可以窃取更多@Joshua的赞成票;
1赞 Matt Bannert 12/15/2010
@Joshua,没有办法打败他。我不再计算我可能因为他而失去的赞成票。但无论如何,到目前为止,他提供的帮助说明了这一点。如果你打败了他,就必须找到你自己的利基市场。我建议在这里每次击键都投赞成票...... :)
1赞 Gavin Simpson 12/15/2010
该死的@ran2,你挫败了我卑鄙、狡猾的接管世界的计划,呜呜!!!
12赞 Prasad Chalasani 12/15/2010 #8

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

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

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

mtrace.off()
34赞 Ari B. Friedman 3/1/2011 #9

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

我正在学习调试,所以来自另一个线程的另一个建议:

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

您还可以使用自己喜欢的调试功能,在发生错误或警告时直接投入到操作的热潮中。例如:options

  • 设置为在发生错误时运行,如 Shane 所述(以及 R 调试指南中所述)。或者您发现运行有用的任何其他方便的功能。options(error=recover)recover()

以及来自@Shane链接之一的另外两种方法:

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

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

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

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

  • findLineNum()获取源文件名和行号,并返回函数和环境。当您 .R 文件,它会在第 #n 行返回错误,但您需要知道第 #n 行位于哪个函数。source()
  • setBreakpoint()获取源文件名和行号,并在其中设置断点

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

setBreakpoint()是一个更加人性化的前端。有关其工作原理的内部详细信息,请参阅最近的 R Journal 文章trace()

如果您尝试调试其他人的包,一旦找到问题,就可以用 和 覆盖他们的函数,但不要在生产代码中使用它。fixInNamespaceassignInNamespace

这些都不应排除久经考验的标准 R 调试工具,其中一些工具高于此,而另一些则不然。特别是,当您有一堆耗时的代码而不想重新运行时,事后调试工具非常方便。

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

评论

1赞 GSee 7/20/2012
+1 感谢您为将这些问题合并为一个问题而付出的所有努力,然后保持开放!
6赞 eykanal 1/20/2012 #10

根据我在这里收到的答案,您绝对应该检查选项(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]> 

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

5赞 Andy Clifton 12/19/2013 #11

我给出了这个答案,但为了完整起见我在这里添加它。

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

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

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

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

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

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

1赞 Siva 4/26/2017 #12

调试不带实例引用的引用类方法

ClassName$trace(methodName, browser)
0赞 user9669128 4/19/2018 #13

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