123

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

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

我做了什么:

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

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

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

4

13 回答 13

169

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

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

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

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

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

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

于 2009-12-10T17:51:20.497 回答
39

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

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

有人同意/不同意吗?

于 2009-07-23T03:48:02.063 回答
33

正如在另一个问题中向我指出的那样,Rprof()它是查找程序中可能受益于加速或迁移到 C/C++ 实现的慢速部分的summaryRprof()好工具。如果您正在进行模拟工作或其他计算或数据密集型活动,这可能更适用。该软件包可以帮助可视化结果。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")))然后调用 with,myBrowse()因为它使用全局变量。

然后是 R 2.10 中可用的新函数:

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

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

setBreakpoint()是一个对trace(). 最近的 R Journal 文章中提供了有关其工作原理的内部细节。

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

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

最后,对于似乎不会引发错误消息的棘手问题,您可以使用options(error=dump.frames)此问题中的详细说明: Error without an error being throwing

于 2011-03-01T15:12:05.603 回答
30

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

至于您引用的具体警告,您很可能需要在代码中允许更多迭代。一旦你发现调用的是什么,就可以使用-seeglm.fit来计算如何将control参数传递给它。glm.control?glm.control

于 2010-12-14T18:45:05.890 回答
23

So browser(), traceback() and debug() walk into a bar, but trace() waits outside and keeps the motor running.

By inserting browser somewhere in your function, the execution will halt and wait for your input. You can move forward using n (or Enter), run the entire chunk (iteration) with c, finish the current loop/function with f, or quit with Q; see ?browser.

With debug, you get the same effect as with browser, but this stops the execution of a function at its beginning. Same shortcuts apply. This function will be in a "debug" mode until you turn it off using undebug (that is, after debug(foo), running the function foo will enter "debug" mode every time until you run undebug(foo)).

A more transient alternative is debugonce, which will remove the "debug" mode from the function after the next time it's evaluated.

traceback will give you the flow of execution of functions all the way up to where something went wrong (an actual error).

You can insert code bits (i.e. custom functions) in functions using trace, for example browser. This is useful for functions from packages and you're too lazy to get the nicely folded source code.

于 2010-12-14T18:26:14.917 回答
19

我的总体策略如下:

  1. 运行traceback()以查看明显的问题
  2. 设置options(warn=2)将警告视为错误
  3. 设置options(error=recover)为出错时进入调用堆栈
于 2010-12-14T18:44:51.060 回答
16

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

于 2010-10-29T19:15:45.457 回答
14

Mark Bravington 的调试器可作为debugCRAN 上的软件包提供,非常好而且非常简单。

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()在不同的函数中调用另一个代码。

高温高压

于 2009-07-23T08:04:13.713 回答
12

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

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

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

mtrace.off()
于 2010-12-15T15:51:46.660 回答
6

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

于 2012-01-20T15:33:35.190 回答
5

我对最近的一个问题给出了这个答案,但为了完整起见,我在这里添加它。

我个人倾向于不使用函数进行调试。我经常发现这带来的麻烦和解决的麻烦一样多。此外,来自 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 的解决方案可能正是您所需要的。

于 2013-12-19T02:30:10.423 回答
1

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

ClassName$trace(methodName, browser)
于 2017-04-26T11:57:17.487 回答
0

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

于 2018-04-19T10:00:35.503 回答