web-dev-qa-db-ja.com

Rでのデバッグに関する一般的な提案

私が書いたR関数を使用するとエラーが発生します:

_Warning messages:
1: glm.fit: algorithm did not converge 
2: glm.fit: algorithm did not converge 
_

私がやったこと:

  1. 機能をステップスルーする
  2. エラーが発生した行を調べるためにprintを追加すると、_glm.fit_を使用しない2つの関数が提案されます。それらはwindow()save()です。

私の一般的なアプローチには、printコマンドとstopコマンドを追加し、例外が見つかるまで関数を1行ずつステップ実行することが含まれます。

ただし、このエラーがコード内で発生する技術を使用することは明確ではありません。コード内のどの関数が_glm.fit_に依存するかさえわかりません。この問題を診断するにはどうすればよいですか?

119
David LeBauer

デバッグはアート形式であると言えますので、明確な特効薬はありません。あらゆる言語でデバッグするための優れた戦略があり、それらもここに適用されます(例 このニースの記事を読む )。たとえば、最初のことは、問題を再現することです...それができない場合は、より多くの情報を取得する必要があります(例:ロギング)。再現できたら、ソースまでreduceする必要があります。

「トリック」ではなく、お気に入りのデバッグルーチンがあると言えます。

  1. エラーが発生した場合、私が通常行う最初のことは、エラーが発生した場所を示すtraceback():を呼び出してスタックトレースを調べることです。これは、複数のネストされた関数がある場合に特に便利です。
  2. 次にoptions(error=recover);を設定しますこれにより、エラーが発生したブラウザーモードにすぐに切り替わるため、そこからワークスペースを参照できます。
  3. それでも十分な情報がない場合は、通常debug()関数を使用して、スクリプトを1行ずつステップ実行します。

R 2.10で最も優れた新しいトリック(スクリプトファイルを使用する場合)は、findLineNum()およびsetBreakpoint()関数を使用することです。

最後のコメント:エラーに応じて、外部関数呼び出しの周りにtry()またはtryCatch()ステートメントを設定することも非常に役立ちます(特にS4クラスを処理する場合)。これにより、さらに多くの情報が得られる場合があり、実行時にエラーを処理する方法をより詳細に制御できます。

これらの関連する質問には多くの提案があります。

166
Shane

私が今まで見た中で最高のウォークスルーは:

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

誰もが賛成/反対ですか?

38

別の質問 で指摘されたように、Rprof()summaryRprof()プログラムの遅い部分を見つける 高速化またはC/C++実装への移行が役立つ場合があります。これはおそらく、シミュレーション作業やその他の計算集約型またはデータ集約型のアクティビティを実行している場合により当てはまります。 profr package は、結果の視覚化に役立ちます。

私は少しデバッグについて学ぶキックをしているので、 別のスレッド からの別の提案:

  • options(warn=2)を設定して、エラーのような警告を処理します

また、optionsを使用して、エラーまたは警告が発生したときに、好みのデバッグ機能を使用してアクションの熱中にあなたをドロップすることができます。例えば:

  • シェーンが指摘したように(および Rデバッグガイド に記載されているように)、エラーが発生したときにoptions(error=recover)を実行するようにrecover()を設定します。実行しておくと便利です。

そして、@ Shaneのいずれかの links からの別の2つのメソッド:

  • 内部関数呼び出しを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() a.Rファイルで行#nでエラーを返すときに役立ちますが、行#nにある関数を知る必要があります。
  • setBreakpoint()はソースファイル名と行番号を取り、そこにブレークポイントを設定します

codetools パッケージ、および特にそのcheckUsage関数は、コンパイラーが通常報告する構文およびスタイルエラー(未使用のローカル、未定義のグローバル関数および変数、部分的引数の照合など)。

setBreakpoint()は、trace()へのよりユーザーフレンドリーなフロントエンドです。これがどのように機能するかの内部の詳細は、 最近のR Journalの記事 にあります。

他の人のパッケージをデバッグしようとしている場合、問題を特定したら それらの関数を上書きfixInNamespaceassignInNamespaceを使用できますが、これは使用しないでください本番コードで。

このいずれも、実証済みの 標準Rデバッグツール を排除するものではありません。一部は上にあり、他はそうではありません。特に、 事後デバッグツール は、再実行しないで時間のかかるコードの束がある場合に便利です。

最後に、エラーメッセージがスローされないように見えるトリッキーな問題については、この質問で詳述されているoptions(error=dump.frames)を使用できます。 エラーがスローされないエラー

32
Ari B. Friedman

ある時点で、glm.fitが呼び出されています。つまり、呼び出す関数の1つ、またはそれらの関数によって呼び出される関数の1つがglmglm.fitのいずれかを使用していることを意味します。

また、上記の私のコメントで述べたように、それはwarningではなくerrorであり、大きな違いがあります。警告から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:以上とマークされたフレームを無視できます。 foobarを呼び出し、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)

引用する特定の警告に関しては、コード内でより多くの反復を許可する必要がある可能性が高いです。 glm.fitを呼び出しているものがわかったら、glm.controlを使用してcontrol引数を渡す方法を見つけます-?glm.controlを参照してください。

28
Gavin Simpson

したがって、browser()traceback()、およびdebug()はバーに入りますが、trace()は外で待機し、モーターを作動させ続けます。

関数のどこかにbrowserを挿入すると、実行が停止し、入力を待機します。を使用して前に進むことができます n (または Enter)、チャンク全体(反復)を実行します c、現在のループ/関数を終了します f、またはで終了 Q; _?browser_を参照してください。

debugを使用すると、ブラウザと同じ効果が得られますが、これにより関数の実行が最初から停止します。同じショートカットが適用されます。この関数は、undebugを使用してオフにするまで「デバッグ」モードになります(つまり、debug(foo)の後、関数fooを実行すると「デバッグ」モードに入ります。 undebug(foo))を実行するまで毎回。

より一時的な代替手段はdebugonceです。これは、次回の評価後に関数から「デバッグ」モードを削除します。

tracebackは、何かがうまくいかなかった場所(実際のエラー)までの関数の実行フローを提供します。

traceなどのbrowserを使用して、関数にコードビット(カスタム関数)を挿入できます。これはパッケージからの関数に便利であり、あなたはうまく折りたたまれたソースコードを取得するのが面倒です。

20
Roman Luštrik

私の一般的な戦略は次のようになります。

  1. traceback()を実行して、明らかな問題を探します
  2. options(warn=2)を設定して、エラーのような警告を処理します
  3. エラー時に呼び出しスタックにステップインするようにoptions(error=recover)を設定します
18
Joshua Ulrich

ここで提案されているすべての手順を実行した後、foreach()に_.verbose = TRUE_を設定すると、多くの有用な情報が得られることを学びました。特に、foreach(.verbose=TRUE)はforeachループ内でエラーが発生した場所を正確に示しますが、traceback()はforeachループ内を調べません。

15

CRANのパッケージdebugとして入手可能なMark Bravingtonのデバッガーは非常に優れており、非常に簡単です。

_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

13

Gavinの答えが好きです。オプションについては知りませんでした(エラー=回復)。また、コードをステップ実行する視覚的な方法を提供する「デバッグ」パッケージを使用することも好きです。

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

この時点で、関数を表示する別のデバッグウィンドウが開き、コード内の現在位置を示す黄色の線が表示されます。メインウィンドウでコードがデバッグモードになり、Enterキーを押してコードをステップ実行し(他のコマンドもあります)、変数値などを調べることができます。デバッグウィンドウの黄色の線は移動して、あなたはコードの中にいます。デバッグが完了したら、次の方法でトレースをオフにできます。

mtrace.off()
11

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_と入力して終了します。

5
eykanal

最近の質問 にこの回答をしましたが、完全を期すためにここに追加しています。

個人的には、デバッグに関数を使用しない傾向があります。私はしばしば、これが解決するのと同じくらい多くの問題を引き起こすとわかります。また、Matlabのバックグラウンドから来て、コードでこれを行うよりも、統合開発環境(IDE)でこれを行うことができるのが好きです。 IDEを使用すると、コードが簡潔でシンプルになります。

Rの場合、IDEと呼ばれる "RStudio"( http://www.rstudio.com )を使用します。これは、Windows、Mac、およびLinuxで使用でき、とても使いやすいです。

2013年10月頃以降のRstudioのバージョン(0.98ish?)には、スクリプトと関数にブレークポイントを追加する機能があります。これを行うには、ファイルの左マージンをクリックしてブレークポイントを追加します。ブレークポイントを設定してから、そのポイントからステップスルーできます。また、その環境内のすべてのデータにアクセスできるため、コマンドを試すことができます。

詳細については、 http://www.rstudio.com/ide/docs/debugging/overview を参照してください。既にRstudioがインストールされている場合、アップグレードが必要な場合があります-これは比較的新しい(2013年後半)機能です。

同様の機能を備えた他のIDEもあります。

確かに、それが組み込み関数である場合は、この議論で他の人が行った提案に頼らなければならない場合があります。ただし、修正が必要なのが独自のコードである場合は、IDEベースのソリューションが必要な場合があります。

4
Andy Clifton

参照クラスインスタンス参照なしのメソッドをデバッグするには

ClassName$trace(methodName, browser)
1
shiva

エラー行番号を印刷しないこと-最も基本的な要件-BY DEFAILT-は、R/Rstudioの一種の冗談であると考え始めています。エラーが発生した場所を見つけるために私が見つけた唯一の信頼できる方法は、traceback()を呼び出して一番上の行を見るという追加の努力をすることです。

0
user9669128