提问人:David LeBauer 提问时间:12/15/2010 最后编辑:David LeBauer 更新时间:2/8/2022 访问量:47533
在 R 中进行调试的一般建议
General suggestions for debugging in R
问:
使用我编写的 R 函数时出现错误:
Warning messages:
1: glm.fit: algorithm did not converge
2: glm.fit: algorithm did not converge
我做了什么:
- 单步执行函数
- 添加 print 以找出错误发生在哪一行建议两个不应使用 .他们是 和 .
glm.fit
window()
save()
我的一般方法包括添加和命令,以及逐行执行函数,直到我能找到异常。print
stop
但是,我不清楚使用这些技术时,此错误在代码中来自何处。我什至不确定代码中的哪些函数依赖于.如何诊断此问题?glm.fit
答:
到目前为止,我见过的最好的演练是:
http://www.biostat.jhsph.edu/%7Erpeng/docs/R-debug-tools.pdf
有人同意/不同意吗?
评论
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型
我想说的是,调试是一种艺术形式,所以没有明确的灵丹妙药。在任何语言中都有很好的调试策略,它们也适用于这里(例如,阅读这篇不错的文章)。例如,第一件事是重现问题......如果你做不到这一点,那么你需要获得更多的信息(例如,关于日志记录)。一旦你可以重现它,你就需要把它减少到源头。
与其说是“技巧”,不如说我有一个最喜欢的调试例程:
- 当发生错误时,我通常做的第一件事是通过调用 来查看堆栈跟踪:,它显示错误发生的位置,如果您有多个嵌套函数,这尤其有用。
traceback()
- 接下来我将设置;这会立即切换到发生错误的浏览器模式,因此您可以从那里浏览工作区。
options(error=recover)
- 如果我仍然没有足够的信息,我通常会使用该函数并逐行执行脚本。
debug()
R 2.10 中最好的新技巧(使用脚本文件时)是使用 and 函数。findLineNum()
setBreakpoint()
最后要说的是:根据错误,围绕外部函数调用设置 or 语句也非常有帮助(尤其是在处理 S4 类时)。这有时会提供更多信息,并且还可以更好地控制在运行时处理错误的方式。try()
tryCatch()
这些相关问题有很多建议:
评论
browser()
当存在未触发警告/错误的错误时(图片来源:本页的 Roman Luštrik)。任何其他工具,例如?browser()
在完成此处建议的所有步骤后,我刚刚了解到设置也为我提供了大量有用的信息。特别是,显示 foreach 循环中发生错误的确切位置,而不查看 foreach 循环内部。.verbose = TRUE
foreach()
foreach(.verbose=TRUE)
traceback()
于是,走进一家酒吧,但在外面等着,让电机继续运转。browser()
traceback()
debug()
trace()
通过在函数的某个位置插入,执行将停止并等待您的输入。您可以使用 (或 ) 向前移动,使用 运行整个块(迭代),使用 完成当前循环/函数 ,或退出 ;看。browser
nEntercfQ?browser
使用 ,您可以获得与浏览器相同的效果,但这会在函数开始时停止执行函数。同样的快捷方式也适用。此功能将处于“调试”模式,直到您将其关闭(即,在 之后,运行该函数每次都会进入“调试”模式,直到您运行)。debug
undebug
debug(foo)
foo
undebug(foo)
更瞬态的替代方法是 ,它将在下次评估函数后从函数中删除“调试”模式。debugonce
traceback
将为您提供函数执行的流程,一直到出现问题(实际错误)的位置。
您可以使用 在函数中插入代码位(即自定义函数),例如 。这对于包中的函数很有用,而且你懒得获得折叠良好的源代码。trace
browser
我的总体策略是这样的:
- 跑去看看,寻找明显的问题
traceback()
- 设置为将警告视为错误
options(warn=2)
- 设置为在出错时单步执行调用堆栈
options(error=recover)
在某个时候,正在被调用。这意味着您调用的函数之一或这些函数调用的函数之一正在使用 、 。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
bar
bar
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)
至于你引用的具体警告,你很可能需要在代码中允许更多的迭代。一旦你发现了什么在调用,就弄清楚如何使用 - see 来传递参数。glm.fit
control
glm.control
?glm.control
评论
我喜欢 Gavin 的回答:我不知道选项(错误 = 恢复)。我还喜欢使用“debug”包,它提供了一种可视化的方式来逐步执行您的代码。
require(debug)
mtrace(foo)
foo(1)
此时,它会打开一个单独的调试窗口,显示您的函数,并用一条黄线显示您在代码中的位置。在主窗口中,代码进入调试模式,您可以继续按回车键单步执行代码(还有其他命令),并检查变量值等。调试窗口中的黄线不断移动,以显示您在代码中的位置。完成调试后,可以使用以下命令关闭跟踪:
mtrace.off()
正如在另一个问题中向我指出的那样,它们是很好的工具,可以找到程序中可能受益于加速或迁移到 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()
如果您尝试调试其他人的包,一旦找到问题,就可以用 和 覆盖他们的函数,但不要在生产代码中使用它。fixInNamespace
assignInNamespace
这些都不应排除久经考验的标准 R 调试工具,其中一些工具高于此,而另一些则不然。特别是,当您有一堆耗时的代码而不想重新运行时,事后调试工具非常方便。
最后,对于似乎没有抛出错误消息的棘手问题,您可以使用以下问题中的详细说明:没有抛出错误的错误options(error=dump.frames)
评论
根据我在这里收到的答案,您绝对应该检查选项(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
我给出了这个答案,但为了完整起见,我在这里添加它。
就我个人而言,我倾向于不使用函数进行调试。我经常发现,这带来的麻烦和解决的麻烦一样多。此外,我拥有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 的解决方案可能正是您所需要的。
调试不带实例引用的引用类方法
ClassName$trace(methodName, browser)
我开始认为不打印错误行号 - 一个最基本的要求 - BY DEFAILT - 在 R/Rstudio 中是一种笑话。我发现查找错误发生位置的唯一可靠方法是进行额外的调用 traceback() 并查看顶行。
评论
options(warn = 2)