R中调试的一般建议

Dav*_*uer 119 debugging r r-faq

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

Warning messages:
1: glm.fit: algorithm did not converge 
2: glm.fit: algorithm did not converge 
Run Code Online (Sandbox Code Playgroud)

我做了什么:

  1. 逐步完成该功能
  2. 添加打印以找出错误发生在哪一行表示不应使用的两个函数glm.fit.他们是window()save().

我的一般方法包括添加printstop命令,逐行逐步执行功能,直到找到异常.

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

Sha*_*ane 166

我说调试是一种艺术形式,所以没有明确的银弹.在任何语言中都有很好的调试策略,它们也适用于此(例如阅读这篇精彩的文章).例如,第一件事是重现问题 ......如果你不能这样做,那么你需要获得更多信息(例如,使用日志记录).一旦你能复制它,你需要减少下来的来源.

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

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

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

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

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

  • 您也可以将debugonce()添加到debug(). (8认同)
  • 虽然不仅在调试时有用,但修复(df1)打开图形R编辑器,其中加载了数据框df1,您可以动态编辑或只是一目了然. (2认同)

Chr*_*ois 38

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

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

有人同意/不同意吗?


Ari*_*man 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)此问题中的详细信息: 错误没有抛出错误


Rei*_*son 29

在某些时候,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.
Run Code Online (Sandbox Code Playgroud)

所以,如果你跑

options(warn = 2)
Run Code Online (Sandbox Code Playgroud)

然后运行你的代码,R会抛出一个错误.在那时,你可以跑

traceback()
Run Code Online (Sandbox Code Playgroud)

看到调用堆栈.这是一个例子.

> 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)
Run Code Online (Sandbox Code Playgroud)

在这里,您可以忽略标记的帧4:和更高的帧.我们看到foo被调用barbar产生了警告.这应该会告诉你哪些函数正在调用glm.fit.

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

options(error = recover)
Run Code Online (Sandbox Code Playgroud)

这是一个例子:

> 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:
Run Code Online (Sandbox Code Playgroud)

然后,您可以进入任何这些框架,以查看抛出警告时发生的情况.

要将上述选项重置为默认值,请输入

options(error = NULL, warn = 0)
Run Code Online (Sandbox Code Playgroud)

至于您引用的特定警告,您很可能需要在代码中允许更多迭代.一旦你发现了什么是调用glm.fit,找出如何control使用glm.control- 参见传递参数?glm.control.

  • 很好的答案.悲观主义的一个注意事项是,这些收敛错误经常发生在不稳定/不稳定的数据集(完全分离等),以及"收敛正常"和"非收敛"之间的窗口,但不能通过增加数量来修复迭代 - 需要一些更激烈的变化'往往是狭隘的 (4认同)
  • 加文,我击败了你25秒.我要求你删除过度有用的答案,并停止窃取我的赞成票.;-) (3认同)

Rom*_*rik 21

所以browser(),traceback()debug()走进一个酒吧,但trace()等待之外,并保持电机运转.

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

使用debug,您可以获得与浏览器相同的效果,但这会在一开始就停止执行某个功能.相同的快捷方式适用.此功能将处于"调试"模式,直到您将其关闭undebug(也就是说debug(foo),运行后,foo每次运行该功能都将进入"调试"模式,直到您运行undebug(foo)).

更简单的替代方案是debugonce,在下次评估之后将从函数中删除"调试"模式.

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

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


Jos*_*ich 18

我的总体策略如下:

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


Mic*_*der 15

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


Dav*_*ler 13

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
Run Code Online (Sandbox Code Playgroud)

代码会弹出一个突出显示的Tk窗口,这样你就可以看到正在发生的事情,当然你可以mtrace()在另一个函数中调用另一个.

HTH


Pra*_*ani 11

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

require(debug)
mtrace(foo)
foo(1)
Run Code Online (Sandbox Code Playgroud)

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

mtrace.off()
Run Code Online (Sandbox Code Playgroud)


eyk*_*nal 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:
Run Code Online (Sandbox Code Playgroud)

此时您可以选择输入哪个"框架".进行选择后,您将进入browser()模式:

Selection: 4
Called from: stop(gettextf("replacement has %d rows, data has %d", N, n), 
    domain = NA)
Browse[1]> 
Run Code Online (Sandbox Code Playgroud)

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


And*_*ton 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 的解决方案可能正是您所需要的。