Saran umum untuk debugging di R


120

Saya mendapatkan kesalahan saat menggunakan fungsi R yang saya tulis:

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

Apa yang telah aku lakukan:

  1. Telusuri fungsinya
  2. Menambahkan print untuk mengetahui di baris mana kesalahan terjadi menunjukkan dua fungsi yang tidak boleh digunakan glm.fit. Mereka adalah window()dan save().

Pendekatan umum saya termasuk menambahkan printdan stopperintah, dan melangkah melalui baris fungsi demi baris sampai saya dapat menemukan pengecualian.

Namun, tidak jelas bagi saya menggunakan teknik-teknik dari mana kesalahan ini berasal dalam kode. Saya bahkan tidak yakin fungsi mana yang bergantung pada kode glm.fit. Bagaimana cara saya mendiagnosis masalah ini?


5
Lihat halaman Duncan Murdoch tentang Debugging di R
Rob Hyndman

10
Oke, saya akan menyatakan yang sudah jelas: itu peringatan, bukan kesalahan .
Gavin Simpson

10
@ gavin-simpson Saya tidak menyadari bahwa ada perbedaan teknis, terima kasih telah menunjukkannya. Tetapi pada akhirnya, ini menunjukkan bahwa fungsi saya yang sebelumnya berfungsi tidak berfungsi.
David LeBauer

11
@David +1 untuk "... fungsi saya yang sebelumnya berfungsi tidak berfungsi."
Joshua Ulrich

5
@David: re ps Anda. Ini menambah dimensi pada pertanyaan yang akan terlewatkan tanpa contoh; yaitu bagaimana membuat R masuk ke mode debugging ketika hanya peringatan yang dihasilkan? Jika Anda melewatkan detail ini, kami semua tidak akan mengarahkan Anda options(warn = 2). Jadi dalam kasus ini, detail penting untuk menjawab pertanyaan umum Anda. +1 dari saya.
Gavin Simpson

Jawaban:


167

Saya akan mengatakan bahwa debugging adalah bentuk seni, jadi tidak ada peluru perak yang jelas. Ada strategi bagus untuk debugging dalam bahasa apapun, dan mereka juga berlaku di sini (mis. Baca artikel bagus ini ). Misalnya, hal pertama adalah mereproduksi masalah ... jika Anda tidak dapat melakukannya, maka Anda perlu mendapatkan lebih banyak informasi (misalnya dengan logging). Setelah Anda dapat mereproduksinya, Anda perlu menguranginya ke sumbernya.

Daripada "trik", saya akan mengatakan bahwa saya memiliki rutinitas debugging favorit:

  1. Saat terjadi kesalahan, hal pertama yang biasanya saya lakukan adalah melihat pelacakan tumpukan dengan memanggil traceback(): yang menunjukkan tempat terjadinya kesalahan, yang sangat berguna jika Anda memiliki beberapa fungsi bersarang.
  2. Selanjutnya saya akan mengatur options(error=recover); ini segera beralih ke mode browser di mana kesalahan terjadi, sehingga Anda dapat menjelajahi ruang kerja dari sana.
  3. Jika saya masih tidak memiliki cukup informasi, saya biasanya menggunakan debug()fungsi tersebut dan menelusuri skrip baris demi baris.

Trik baru terbaik di R 2.10 (saat bekerja dengan file skrip) adalah menggunakan findLineNum()dan setBreakpoint()fungsi.

Sebagai komentar terakhir: tergantung pada kesalahannya, juga sangat membantu untuk mengatur try()atau tryCatch()pernyataan seputar pemanggilan fungsi eksternal (terutama saat berhadapan dengan kelas S4). Itu terkadang akan memberikan lebih banyak informasi, dan itu juga memberi Anda lebih banyak kontrol atas bagaimana kesalahan ditangani pada waktu proses.

Pertanyaan terkait ini memiliki banyak saran:


8
Anda juga bisa menambahkan debugonce () ke debug ().
Joris Meys

2
Meskipun tidak hanya berguna saat debugging, fix (df1) membuka R Editor grafis dengan bingkai data df1 dimuat di dalamnya yang dapat Anda edit dengan cepat atau hanya melihatnya sekilas.
Dmitrii I.

debugging di R tampaknya sangat sulit, misalnya tidak ada solusi mudah untuk melihat baris kode peringatan
TMS

browser()karena ketika ada kesalahan yang tidak memicu peringatan / kesalahan (kredit: Roman Luštrik di halaman ini). Ada alat lain seperti browser()?
PatrickT


32

Seperti yang ditunjukkan kepada saya di pertanyaan lain , Rprof()dan summaryRprof()merupakan alat yang bagus untuk menemukan bagian lambat dari program Anda yang mungkin mendapat manfaat dari mempercepat atau beralih ke implementasi C / C ++. Ini mungkin berlaku lebih jika Anda melakukan pekerjaan simulasi atau aktivitas komputasi atau data intensif lainnya. The profrpaket dapat membantu memvisualisasikan hasil.

Saya sedang mempelajari sedikit tentang men-debug, jadi saran lain dari utas lain :

  • Setel options(warn=2)untuk memperlakukan peringatan seperti kesalahan

Anda juga dapat menggunakan optionsuntuk membawa Anda langsung ke dalam panasnya tindakan ketika kesalahan atau peringatan terjadi, menggunakan fungsi debugging favorit pilihan Anda. Misalnya:

  • Setel options(error=recover)untuk berjalan recover()ketika terjadi kesalahan, seperti yang dicatat Shane (dan seperti yang didokumentasikan dalam panduan debugging R. Atau fungsi praktis lainnya yang menurut Anda berguna untuk dijalankan.

Dan dua metode lain dari salah satu tautan @ Shane :

  • Bungkus panggilan fungsi dalam dengan try()untuk mengembalikan informasi lebih lanjut tentang itu.
  • Untuk * fungsi terapkan, gunakan .inform=TRUE(dari paket plyr) sebagai opsi untuk perintah terapkan

@JoshuaUlrich juga menunjukkan cara yang rapi dalam menggunakan kemampuan bersyarat dari browser()perintah klasik untuk mengaktifkan / menonaktifkan debugging:

  • Masukkan ke dalam fungsi yang mungkin ingin Anda debug browser(expr=isTRUE(getOption("myDebug")))
  • Dan atur opsi global dengan options(myDebug=TRUE)
  • Anda bahkan bisa menggabungkan panggilan browser: myBrowse <- browser(expr=isTRUE(getOption("myDebug")))dan kemudian memanggil dengan myBrowse()karena menggunakan global.

Lalu ada fungsi baru yang tersedia di R 2.10:

  • findLineNum()mengambil nama file sumber dan nomor baris dan mengembalikan fungsi dan lingkungan. Ini tampaknya berguna ketika Anda source()memiliki file .R dan mengembalikan kesalahan pada baris #n, tetapi Anda perlu mengetahui fungsi apa yang terletak di baris #n.
  • setBreakpoint() mengambil nama file sumber dan nomor baris dan menetapkan breakpoint di sana

The codetools paket, dan khususnya yang checkUsagefungsinya dapat sangat membantu dalam cepat mengambil sintaks dan kesalahan gaya yang kompilator biasanya akan melaporkan (penduduk setempat yang tidak terpakai, tidak terdefinisi fungsi global dan variabel, parsial pencocokan argumen, dan sebagainya).

setBreakpoint()adalah front-end yang lebih ramah pengguna trace(). Detail tentang internal cara kerjanya tersedia di artikel R Journal terbaru .

Jika Anda mencoba men-debug paket orang lain, setelah Anda menemukan masalahnya, Anda dapat menulis ulang fungsinya dengan fixInNamespacedan assignInNamespace, tetapi jangan gunakan ini dalam kode produksi.

Semua ini tidak boleh menghalangi alat debugging R standar yang telah dicoba dan benar , beberapa di antaranya di atas dan yang lainnya tidak. Secara khusus, alat debugging post-mortem berguna ketika Anda memiliki banyak kode yang memakan waktu yang tidak ingin Anda jalankan kembali.

Akhirnya, untuk masalah rumit yang sepertinya tidak memunculkan pesan kesalahan, Anda dapat menggunakan options(error=dump.frames)seperti yang dijelaskan dalam pertanyaan ini: Kesalahan tanpa kesalahan dilempar


1
+1 untuk semua pekerjaan yang telah Anda lakukan untuk menggabungkan pertanyaan-pertanyaan ini menjadi satu dan kemudian membuatnya tetap terbuka!
GSee

29

Di beberapa titik, glm.fitsedang dipanggil. Itu berarti salah satu fungsi Anda menelepon atau salah satu fungsi yang disebut dengan fungsi-fungsi yang baik menggunakan glm, glm.fit.

Juga, seperti yang saya sebutkan dalam komentar saya di atas, itu adalah peringatan, bukan kesalahan , yang membuat perbedaan besar. Anda tidak dapat memicu alat debugging R dari peringatan (dengan opsi default sebelum seseorang memberi tahu saya bahwa saya salah ;-).

Jika kita mengubah opsi untuk mengubah peringatan menjadi kesalahan maka kita dapat mulai menggunakan alat debugging R. Dari ?optionskami memiliki:

 ‘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.

Jadi jika Anda lari

options(warn = 2)

lalu jalankan kode Anda, R akan memunculkan error. Pada titik mana, Anda bisa lari

traceback()

untuk melihat tumpukan panggilan. Berikut ini contohnya.

> 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)

Di sini Anda dapat mengabaikan bingkai yang ditandai 4:dan lebih tinggi. Kami melihat yang foodipanggil bardan itu barmenghasilkan peringatan. Itu akan menunjukkan kepada Anda fungsi mana yang memanggil glm.fit.

Jika Anda sekarang ingin men-debug ini, kita dapat beralih ke opsi lain untuk memberi tahu R untuk masuk ke debugger ketika menemui kesalahan, dan karena kita telah membuat kesalahan peringatan, kita akan mendapatkan debugger ketika peringatan asli dipicu. Untuk itu Anda harus menjalankan:

options(error = recover)

Berikut ini contohnya:

> 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:

Anda kemudian dapat masuk ke salah satu bingkai tersebut untuk melihat apa yang terjadi saat peringatan dilemparkan.

Untuk mengatur ulang opsi di atas ke defaultnya, masukkan

options(error = NULL, warn = 0)

Adapun peringatan khusus yang Anda kutip, sangat mungkin Anda perlu mengizinkan lebih banyak iterasi dalam kode. Setelah Anda menemukan apa yang memanggil glm.fit, cari tahu bagaimana menyampaikan controlargumen tersebut menggunakan glm.control- lihat ?glm.control.


4
jawaban yang bagus. satu catatan pesimisme adalah bahwa kesalahan konvergensi semacam ini sering terjadi dengan kumpulan data yang tidak stabil / miring (pemisahan lengkap, dll.), dan jendela antara 'konvergen baik-baik saja' dan 'non-konvergen tetapi tidak dapat diperbaiki dengan meningkatkan jumlah iterasi - perlu beberapa perubahan yang lebih drastis 'sering kali sempit
Ben Bolker

3
Gavin, aku mengalahkanmu dengan 25 detik. Saya meminta Anda menghapus jawaban Anda yang terlalu membantu dan berhenti mencuri suara positif saya. ;-)
Joshua Ulrich

@ Bagus. Jika masalah David adalah pemisahan maka meningkatkan jumlah iterasi tidak akan membantu, itu masih gagal untuk menyatu. Pada saat itu, melihat perkiraan dan kesalahan standar mungkin menunjukkan bahwa ada masalah. Saya juga berharap melihat peringatan tentang nilai yang dipasang secara numerik 0 atau 1 jika pemisahan atau serupa menjadi masalah. Jika menaikkan jumlah pengulangan tidak membantu, David dapat memposting Q lain untuk mendapatkan bantuan dan saya dapat mencuri lebih banyak suara positif @ Joshua ;-)
Gavin Simpson

1
@Joshua, tidak ada cara untuk mengalahkannya. Saya berhenti menghitung suara positif yang mungkin hilang karena dia. Tapi bagaimanapun bantuan yang dia berikan untuk itu sejauh ini. Harus menemukan ceruk Anda sendiri jika Anda mengalahkannya. Saya menyarankan suara positif per penekanan tombol di sini ... :)
Matt Bannert

1
Sialan @ ran2, Anda telah menggagalkan rencana licik dan licik saya untuk mengambil alih dunia , Mwahahahahaha !!!!
Gavin Simpson

21

Jadi browser(), traceback()dan debug()berjalanlah ke bar, tapi trace()menunggu di luar dan teruskan motornya.

Dengan memasukkan browsersuatu tempat di fungsi Anda, eksekusi akan berhenti dan menunggu masukan Anda. Anda dapat bergerak maju menggunakan n(atau Enter), menjalankan seluruh potongan (iterasi) dengan c, menyelesaikan loop / fungsi saat ini dengan f, atau berhenti dengan Q; lihat ?browser.

Dengan debug, Anda mendapatkan efek yang sama seperti browser, tetapi ini menghentikan eksekusi fungsi di awal. Pintasan yang sama berlaku. Fungsi ini akan berada dalam mode "debug" sampai Anda menonaktifkannya menggunakan undebug(yaitu, setelah debug(foo)menjalankan fungsi tersebut fooakan memasuki mode "debug" setiap kali hingga Anda menjalankannya undebug(foo)).

Alternatif yang lebih sementara adalah debugonce, yang akan menghapus mode "debug" dari fungsi setelah dievaluasi lagi.

traceback akan memberi Anda aliran eksekusi fungsi hingga ke tempat yang salah (kesalahan sebenarnya).

Anda dapat memasukkan bit kode (mis. Fungsi kustom) dalam fungsi menggunakan trace, misalnya browser. Ini berguna untuk fungsi dari paket dan Anda terlalu malas untuk mendapatkan kode sumber yang terlipat dengan baik.


18

Strategi umum saya terlihat seperti:

  1. Jalankan traceback()untuk melihat mencari masalah yang jelas
  2. Setel options(warn=2)untuk memperlakukan peringatan seperti kesalahan
  3. Setel options(error=recover)untuk masuk ke tumpukan panggilan saat terjadi kesalahan

15

Setelah melalui semua langkah yang disarankan di sini, saya baru mengetahui bahwa pengaturan .verbose = TRUEdi foreach()juga memberi saya banyak informasi berguna. Secara khusus foreach(.verbose=TRUE)menunjukkan persis di mana kesalahan terjadi di dalam loop foreach, sementara traceback()tidak melihat ke dalam loop foreach.


13

Debugger Mark Bravington yang tersedia sebagai paket debugdi CRAN sangat bagus dan sangat mudah.

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

Kode tersebut muncul di jendela Tk yang disorot sehingga Anda dapat melihat apa yang terjadi dan, tentu saja, Anda dapat memanggil yang lain mtrace()saat berada dalam fungsi yang berbeda.

HTH


11

Saya suka jawaban Gavin: Saya tidak tahu tentang opsi (error = memulihkan). Saya juga suka menggunakan paket 'debug' yang memberikan cara visual untuk menelusuri kode Anda.

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

Pada titik ini, ini akan membuka jendela debug terpisah yang menampilkan fungsi Anda, dengan garis kuning menunjukkan di mana Anda berada dalam kode. Di jendela utama kode memasuki mode debug, dan Anda dapat terus menekan enter untuk melangkah melalui kode (dan ada perintah lain juga), dan memeriksa nilai variabel, dll. Garis kuning di jendela debug terus bergerak untuk menunjukkan di mana Anda berada di dalam kode. Setelah selesai dengan debugging, Anda dapat mematikan pelacakan dengan:

mtrace.off()

5

Berdasarkan jawaban yang saya terima di sini , Anda pasti harus memeriksa options(error=recover)pengaturannya. Jika ini disetel, saat mengalami kesalahan, Anda akan melihat teks di konsol yang mirip dengan berikut ( tracebackkeluaran):

> 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:

Pada titik mana Anda dapat memilih "bingkai" mana yang akan dimasuki. Saat Anda membuat pilihan, Anda akan ditempatkan dalam browser()mode:

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

Dan Anda dapat memeriksa lingkungan seperti saat terjadi kesalahan. Setelah selesai, ketik cuntuk membawa Anda kembali ke menu pemilihan bingkai. Setelah selesai, seperti yang diberitahukan, ketik 0untuk keluar.


4

Saya memberikan jawaban ini untuk pertanyaan yang lebih baru, tetapi saya menambahkannya di sini untuk kelengkapan.

Secara pribadi saya cenderung tidak menggunakan fungsi untuk men-debug. Saya sering menemukan bahwa ini menyebabkan masalah sebanyak yang dapat dipecahkan. Juga, datang dari latar belakang Matlab saya suka bisa melakukan ini dalam lingkungan pengembangan terintegrasi (IDE) daripada melakukan ini dalam kode. Menggunakan IDE membuat kode Anda tetap bersih dan sederhana.

Untuk R, saya menggunakan IDE bernama "RStudio" ( http://www.rstudio.com ), yang tersedia untuk windows, mac, dan linux dan cukup mudah digunakan.

Versi Rstudio sejak sekitar Oktober 2013 (0.98ish?) Memiliki kemampuan untuk menambahkan breakpoint dalam skrip dan fungsi: untuk melakukan ini, cukup klik pada margin kiri file untuk menambahkan breakpoint. Anda dapat mengatur breakpoint dan kemudian melanjutkan dari titik itu. Anda juga memiliki akses ke semua data di lingkungan itu, sehingga Anda dapat mencoba perintah.

Lihat http://www.rstudio.com/ide/docs/debugging/overview untuk detailnya. Jika Anda sudah menginstal Rstudio, Anda mungkin perlu memutakhirkan - ini adalah fitur yang relatif baru (akhir 2013).

Anda juga dapat menemukan IDE lain yang memiliki fungsi serupa.

Memang, jika itu adalah fungsi bawaan Anda mungkin harus menggunakan beberapa saran yang dibuat oleh orang lain dalam diskusi ini. Namun, jika kode Anda sendiri yang perlu diperbaiki, solusi berbasis IDE mungkin yang Anda butuhkan.


1

Untuk men-debug metode Kelas Referensi tanpa referensi instance

ClassName$trace(methodName, browser)

0

Saya mulai berpikir bahwa tidak mencetak nomor baris kesalahan - persyaratan yang paling dasar - OLEH DEFAILT- adalah semacam lelucon di R / Rstudio . Satu-satunya metode yang dapat diandalkan yang saya temukan untuk menemukan di mana kesalahan terjadi adalah dengan melakukan upaya tambahan dari calloing traceback () dan melihat baris teratas.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.