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


120

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

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

私がやった事:

  1. 関数をステップ実行
  2. エラーが発生する行を見つけるためにprintを追加すると、使用すべきでない2つの関数が示唆されますglm.fit。彼らはあるwindow()save()

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

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


5
Duncan MurdochのDebugging in R
Rob Hyndman

10
わかりました、それはエラーではなく警告です。
Gavin Simpson、

10
@ gavin-simpson指摘してくれてありがとう、技術的な違いがあることに気づかなかった。しかし、結局、それは私の以前の機能的機能が機能不全であることを示しています。
David LeBauer

11
@David +1の「...以前に機能していた機能が機能不全になっている」
Joshua Ulrich

5
@デービッド:あなたのpsを再。これにより、例がなければ見逃されていたであろう質問に次元が追加されます。つまり、警告のみが生成されたときにRをデバッグモードにステップインさせる方法は?この詳細を省略した場合、私たちはあなたにを指摘しなかったでしょうoptions(warn = 2)。したがって、この場合、詳細は一般的な質問に答えるために不可欠です。私からの+1。
Gavin Simpson

回答:


167

デバッグは芸術的な形なので、明確な特効薬はありません。どの言語でもデバッグするための優れた戦略があり、それらもここに適用されます(たとえば、この素晴らしい記事を読んでください)。たとえば、最初に問題再現することです。それができない場合は、さらに多くの情報を取得する必要があります(ロギングなど)。それを再現できるようになったら、ソースにまで減らす必要があります。

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

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

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

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

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


8
debugonce()をdebug()に追加することもできます。
Joris Meys、2011

2
デバッグ時に役立つだけでなく、fix(df1)はデータフレームdf1が読み込まれたグラフィカルRエディターを開きます。これは、オンザフライで編集したり、一目で確認したりできます。
DmitriiI。12年

Rでのデバッグは非常に難しいようです。たとえば、警告のコード行を確認する簡単な解決策はありません
TMS

browser()警告/エラーを引き起こさないエラーがある場合(クレジット:このページのRomanLuštrik)。のような他のツールbrowser()
PatrickT


32

中に私に指摘された別の質問Rprof()summaryRprof()するための良いツールです、あなたのプログラムの遅い部分を見つけるスピードアップやC / C ++の実装に移動するからかもしれない給付という。これは、シミュレーション作業やその他の計算またはデータ集約型のアクティビティを実行している場合におそらく当てはまります。profrパッケージには、結果を可視化することができます。

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

  • options(warn=2)警告をエラーのように扱うように設定します

またoptions、お気に入りのデバッグ機能を使用して、エラーまたは警告が発生したときに、アクションの熱中状態に陥ることもできます。例えば:

  • Shaneが指摘したように(そしてRデバッグガイドに記載されoptions(error=recover)ているようrecover()に)、エラーが発生したときに実行するように設定します。

そして、@ Shaneのリンクの 1つからの別の2つの方法:

  • 内部関数呼び出しをラップする try()それに関する詳細情報を返します。
  • * apply関数.inform=TRUEの場合、applyコマンドのオプションとして(plyrパッケージから)を使用します

@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)については、この質問で詳しく説明されているように使用できます: エラーがスローされないエラー


1
これらの質問を1つにマージし、開いたままにしておくすべての作業の+1。
GSee

29

ある時点で、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呼び出され、警告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)

引用する特定の警告については、コードでより多くの反復を許可する必要がある可能性が非常に高いです。何が呼び出されているかがわかったらglm.fitcontrol-seeを使用して引数を渡す方法を考え出しglm.controlます?glm.control


4
素晴らしい答え。悲観論の注意点の1つは、これらの種類の収束エラーが不安定/不規則なデータセット(完全な分離など)で発生することが多く、「収束はうまくいく」と「収束しない」間のウィンドウは数値を増やすことで修正できないことですイテレーションの数-いくつかの大幅な変更が必要である」はしばしば狭い
Ben Bolker

3
ギャビン、25秒で君を倒した。あまりにも役立つ回答を削除して、私の賛成票を盗むのをやめてください。;-)
Joshua Ulrich

@ベンの素晴らしいポイント。デビッドの問題が分離である場合、反復回数を増やしても役に立たないはずですが、収束に失敗するはずです。その時点で推定値と標準誤差を見ると、問題がある可能性があります。分離などが問題であった場合、数値0または1でフィッティングされた値に関する警告が表示されることも期待しています。反復回数を増やしても効果がない場合は、Davidが別のQを投稿して助けを求め、@ Joshuaの賛成票をさらに盗むことができます;-)
Gavin Simpson

1
@ジョシュア、彼を倒す方法はありません。私は彼のために失ったかもしれない賛成票の数をやめました。しかし、とにかく彼が提供する助けはそれをはるかに説明しています。お奨めは、あなたが彼を倒した場合、自分のニッチを見つけます。ここで、キーストロークごとに賛成投票することをお勧めします... :)
Matt Bannert

1
@ ran2をダミット、あなたは世界を引き継ぐために私の卑劣な、欺瞞的な計画、ムワハハハハハを失敗しました!!!!
Gavin Simpson

21

だからbrowser()traceback()そしてdebug()バーに入るが、trace()待機外と走行用モータを保持します。

browser関数のどこかに挿入することにより、実行は停止し、入力を待ちます。n(またはEnter)を使用して先に進む、チャンク全体を実行する(反復)c、で現在のループ/関数をf終了する、またはで終了することができQます。見る?browser

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

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

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

traceたとえばを使用して、関数にコードビット(つまりカスタム関数)を挿入できますbrowser。これは、パッケージの関数に役立ち、面倒なソースコードを取得するのが面倒です。


18

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

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

15

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


13

debugCRAN のパッケージとして入手できる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


11

私はギャビンの答えが好きです:私はオプションについて知りませんでした(エラー=回復)。また、コードをステップごとに視覚的に確認できる「デバッグ」パッケージを使用することも好きです。

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

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

mtrace.off()

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:

その時点で、入力する「フレーム」を選択できます。選択すると、次のbrowser()モードになります。

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

また、エラー発生時の環境を確認できます。完了したら、入力cしてフレーム選択メニューに戻ります。完了したら、指示されているように入力0して終了します。


4

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

個人的には、デバッグに関数を使用しない傾向があります。これが解決するのと同じくらい多くの問題を引き起こすことがよくあります。また、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ベースのソリューションが必要なだけかもしれません。


1

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

ClassName$trace(methodName, browser)

0

エラー行番号を出力しない-最も基本的な要件-BY DEFAILT-は、R / Rstudioでの一種のジョークだと私は考え始めています。エラーが発生した場所を見つけるために私が見つけた唯一の信頼できる方法は、traceback ()を呼び出して追加の作業を行い、最初の行を確認することです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.