R中调试的一般建议

时间:2010-12-14 18:07:16

标签: r debugging r-faq

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

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

我做了什么:

  1. 单步执行功能
  2. 添加print以查明错误发生在哪一行表示不应使用glm.fit的两个函数。它们是window()save()
  3. 我的一般方法包括添加printstop命令,逐行逐步执行功能,直到我找到异常。

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

13 个答案:

答案 0 :(得分:166)

我说调试是一种艺术形式,所以没有明确的银弹。有很好的使用任何语言进行调试的策略,它们也适用于此处(例如read this nice article)。例如,第一件事是重现问题 ...如果你不能这样做,那么你需要获得更多信息(例如,使用日志记录)。一旦你可以重现它,你需要减少它到源。

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

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

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

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

答案 1 :(得分:38)

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

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

有人同意/不同意吗?

答案 2 :(得分:32)

正如在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 <- 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

答案 3 :(得分:28)

在某些时候,正在调用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的{​​{1}}和bar生成了警告。这应该会告诉你哪些函数正在调用bar

如果您现在想要调试它,我们可以转向另一个选项,告诉R在遇到错误时进入调试器,并且由于我们发出警告错误,我们将在触发原始警告时获得调试器。为此你应该运行:

glm.fit

以下是一个例子:

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

答案 4 :(得分:20)

所以browser()traceback()debug()走进一个小节,但是trace()在外面等待并保持电机运转。

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

使用debug,您可以获得与浏览器相同的效果,但这会在开始时停止执行某个功能。相同的快捷方式适用。这个函数将在&#34; debug&#34;模式,直到你使用undebug将其关闭(也就是说,在debug(foo)之后,运行函数foo每次都会进入&#34; debug&#34;模式,直到你运行{{1} }})。

更短暂的替代方案是undebug(foo),它将删除&#34; debug&#34;在下一次评估后,该功能将从该功能开始。

debugonce将为您提供函数执行流程,直到出错(实际错误)。

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

答案 5 :(得分:18)

我的总体策略如下:

  1. 运行traceback()以查看明显的问题
  2. 设置options(warn=2)以处理错误等警告
  3. 设置options(error=recover)以在错误
  4. 时进入调用堆栈

答案 6 :(得分:15)

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

答案 7 :(得分:13)

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

答案 8 :(得分:11)

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

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

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

mtrace.off()

答案 9 :(得分:5)

根据我收到的答案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退出。

答案 10 :(得分:4)

我将此答案提交给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的解决方案可能就是您所需要的。

答案 11 :(得分:1)

调试引用类方法而不使用实例引用

ClassName$trace(methodName, browser)

答案 12 :(得分:0)

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