Đề xuất chung để gỡ lỗi trong R


120

Tôi gặp lỗi khi sử dụng hàm R mà tôi đã viết:

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

Những gì tôi đã làm xong:

  1. Bước qua chức năng
  2. Thêm chữ in để tìm ra lỗi xảy ra ở dòng nào, gợi ý hai hàm không nên sử dụng glm.fit. Họ đang window()save().

Các phương pháp tiếp cận chung của tôi bao gồm thêm printstoplệnh, và lướt qua từng dòng một hàm cho đến khi tôi có thể xác định được ngoại lệ.

Tuy nhiên, tôi không rõ bằng cách sử dụng các kỹ thuật đó lỗi này xuất phát từ đâu trong mã. Tôi thậm chí không chắc chắn các chức năng trong mã phụ thuộc vào glm.fit. Làm cách nào để chẩn đoán vấn đề này?


5
Kiểm tra trang Duncan Murdoch về Debugging trong R
Rob Hyndman

10
Ok, tôi sẽ nói rõ ràng: đó là một cảnh báo không phải là một lỗi .
Gavin Simpson

10
@ gavin-simpson Tôi không nhận ra rằng có sự khác biệt về kỹ thuật, cảm ơn bạn đã chỉ ra điều đó. Nhưng cuối cùng, nó chỉ ra rằng chức năng hoạt động trước đây của tôi đang bị rối loạn.
David LeBauer

11
@David +1 cho "... chức năng hoạt động trước đây của tôi bị rối loạn."
Joshua Ulrich

5
@David: là ps của bạn. Điều này thêm một chiều cho câu hỏi mà lẽ ra sẽ bị bỏ sót nếu không có ví dụ; cụ thể là làm thế nào để R chuyển sang chế độ gỡ lỗi khi chỉ có cảnh báo được tạo ra? Nếu bạn đã bỏ qua chi tiết này, tất cả chúng tôi sẽ không chỉ cho bạn options(warn = 2). Vì vậy, trong trường hợp này, chi tiết là điều cần thiết để trả lời câu hỏi chung của bạn. +1 từ tôi.
Gavin Simpson

Câu trả lời:


167

Tôi muốn nói rằng gỡ lỗi là một hình thức nghệ thuật, vì vậy không có viên đạn bạc rõ ràng. Có những chiến lược tốt để gỡ lỗi bằng bất kỳ ngôn ngữ nào và chúng cũng áp dụng ở đây (ví dụ: đọc bài viết hay này ). Ví dụ, điều đầu tiên là tái tạo vấn đề ... nếu bạn không thể làm điều đó, thì bạn cần phải lấy thêm thông tin (ví dụ: ghi nhật ký). Một khi bạn có thể tái tạo nó, bạn cần giảm nó xuống nguồn.

Thay vì một "mẹo", tôi muốn nói rằng tôi có một thói quen gỡ lỗi yêu thích:

  1. Khi có lỗi xảy ra, điều đầu tiên tôi thường làm là xem xét dấu vết ngăn xếp bằng cách gọi traceback(): điều này cho bạn biết lỗi xảy ra ở đâu, điều này đặc biệt hữu ích nếu bạn có nhiều hàm lồng nhau.
  2. Tiếp theo tôi sẽ thiết lập options(error=recover); điều này ngay lập tức chuyển sang chế độ trình duyệt nơi xảy ra lỗi, vì vậy bạn có thể duyệt không gian làm việc từ đó.
  3. Nếu tôi vẫn không có đủ thông tin, tôi thường sử dụng debug()hàm và thực hiện từng dòng một.

Thủ thuật mới tốt nhất trong R 2.10 (khi làm việc với các tệp script) là sử dụng các hàm findLineNum()setBreakpoint().

Nhận xét cuối cùng: tùy thuộc vào lỗi, nó cũng rất hữu ích để đặt try()hoặc các tryCatch()câu lệnh xung quanh các lệnh gọi hàm bên ngoài (đặc biệt là khi xử lý các lớp S4). Điều đó đôi khi sẽ cung cấp nhiều thông tin hơn và nó cũng cho phép bạn kiểm soát nhiều hơn cách xử lý lỗi trong thời gian chạy.

Những câu hỏi liên quan này có rất nhiều gợi ý:


8
Bạn cũng có thể thêm debugonce () vào debug ().
Joris Meys

2
Mặc dù không chỉ hữu ích khi gỡ lỗi, fix (df1) mở R Editor đồ họa với khung dữ liệu df1 được tải trong đó mà bạn có thể chỉnh sửa nhanh hoặc chỉ cần xem qua.
Dmitrii I.

gỡ lỗi trong R có vẻ là rất khó khăn, ví dụ như không có giải pháp dễ dàng để xem dòng mã cảnh báo
TMS

browser()khi có lỗi không kích hoạt cảnh báo / lỗi (tín dụng: Roman Luštrik trên trang này). Bất kỳ công cụ khác như thế browser()nào?
PatrickT


32

Như đã được chỉ ra cho tôi trong một câu hỏi khác , Rprof()summaryRprof()là những công cụ tuyệt vời để tìm các phần chậm trong chương trình của bạn có thể có lợi từ việc tăng tốc hoặc chuyển sang triển khai C / C ++. Điều này có thể áp dụng nhiều hơn nếu bạn đang thực hiện công việc mô phỏng hoặc các hoạt động sử dụng máy tính hoặc dữ liệu khác. Các profrgói có thể giúp hình dung kết quả.

Tôi đang muốn tìm hiểu về cách gỡ lỗi, vì vậy một đề xuất khác từ một chủ đề khác :

  • Đặt options(warn=2)để coi các cảnh báo như lỗi

Bạn cũng có thể sử dụng optionsđể thả mình vào nhiệt độ của hành động khi xảy ra lỗi hoặc cảnh báo, bằng cách sử dụng chức năng gỡ lỗi yêu thích của bạn. Ví dụ:

  • Đặt options(error=recover)để chạy recover()khi xảy ra lỗi, như Shane đã lưu ý (và như được ghi trong hướng dẫn gỡ lỗi R. Hoặc bất kỳ chức năng tiện dụng nào khác mà bạn thấy hữu ích khi chạy.

Và hai phương pháp khác từ một trong các liên kết của @ Shane :

  • Kết thúc một lệnh gọi hàm bên trong với try()để trả về thêm thông tin về nó.
  • Đối với các hàm * áp dụng, hãy sử dụng .inform=TRUE(từ gói plyr) làm tùy chọn cho lệnh áp dụng

@JoshuaUlrich cũng chỉ ra một cách gọn gàng để sử dụng các khả năng có điều kiện của browser()lệnh cổ điển để bật / tắt gỡ lỗi:

  • Đặt bên trong chức năng bạn có thể muốn gỡ lỗi browser(expr=isTRUE(getOption("myDebug")))
  • Và đặt tùy chọn chung bằng cách options(myDebug=TRUE)
  • Bạn thậm chí có thể kết thúc cuộc gọi trình duyệt: myBrowse <- browser(expr=isTRUE(getOption("myDebug")))và sau đó gọi với myBrowse()vì nó sử dụng hình cầu.

Sau đó, có các chức năng mới có sẵn trong R 2.10:

  • findLineNum()lấy tên và số dòng của tệp nguồn và trả về hàm và môi trường. Điều này có vẻ hữu ích khi bạn tạo source()tệp .R và nó trả về lỗi ở dòng #n, nhưng bạn cần biết hàm nào nằm ở dòng #n.
  • setBreakpoint() lấy tên tệp nguồn và số dòng và đặt điểm ngắt ở đó

Các codetools gói, và đặc biệt là nó checkUsagechức năng có thể đặc biệt hữu ích trong việc nhanh chóng nhặt cú pháp và lỗi phong cách mà một trình biên dịch thường sẽ báo cáo (người dân địa phương không sử dụng, không xác định chức năng toàn cầu và biến, phù hợp với lập luận một phần, và vân vân).

setBreakpoint()là giao diện người dùng thân thiện hơn với trace(). Chi tiết về các bên trong của cách thức hoạt động này có sẵn trong một bài viết R Journal gần đây .

Nếu bạn đang cố gắng gỡ lỗi gói của người khác, khi bạn đã xác định được vấn đề, bạn có thể ghi đè các hàm của họ với fixInNamespaceassignInNamespace, nhưng không sử dụng điều này trong mã sản xuất.

Không điều nào trong số này nên loại trừ các công cụ gỡ lỗi R tiêu chuẩn đã được thử và đúng , một số công cụ này ở trên và những công cụ khác thì không. Đặc biệt, các công cụ gỡ lỗi sau khi giết mổ rất hữu ích khi bạn có một đống mã tốn thời gian mà bạn không muốn chạy lại.

Cuối cùng, đối với các vấn đề phức tạp mà dường như không đưa ra thông báo lỗi, bạn có thể sử dụng options(error=dump.frames)chi tiết trong câu hỏi này: Lỗi mà không có lỗi được đưa ra


1
+1 cho tất cả công việc bạn đã thực hiện để hợp nhất các câu hỏi này thành một và sau đó giữ nó ở trạng thái mở!
GSee

29

Tại một số điểm, glm.fitđang được gọi. Điều đó có nghĩa một trong những chức năng bạn gọi điện thoại hoặc một trong những chức năng được gọi bằng những chức năng đang sử dụng một trong hai glm, glm.fit.

Ngoài ra, như tôi đã đề cập trong nhận xét của mình ở trên, đó là một cảnh báo không phải là một lỗi , điều này tạo ra sự khác biệt lớn. Bạn không thể kích hoạt bất kỳ công cụ gỡ lỗi nào của R từ một cảnh báo (với các tùy chọn mặc định trước khi ai đó nói với tôi rằng tôi sai ;-).

Nếu chúng ta thay đổi các tùy chọn để biến cảnh báo thành lỗi thì chúng ta có thể bắt đầu sử dụng các công cụ gỡ lỗi của R. Từ ?optionschúng tôi có:

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

Vì vậy, nếu bạn chạy

options(warn = 2)

sau đó chạy mã của bạn, R sẽ báo lỗi. Tại thời điểm đó, bạn có thể chạy

traceback()

để xem ngăn xếp cuộc gọi. Đây là một ví dụ.

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

Ở đây bạn có thể bỏ qua các khung được đánh dấu 4:và cao hơn. Chúng tôi thấy rằng foođược gọi barvà điều đó đã bartạo ra cảnh báo. Điều đó sẽ cho bạn thấy những chức năng nào đang gọi glm.fit.

Nếu bây giờ bạn muốn gỡ lỗi này, chúng tôi có thể chuyển sang tùy chọn khác để yêu cầu R nhập trình gỡ lỗi khi nó gặp lỗi và vì chúng tôi đã thực hiện các lỗi cảnh báo, chúng tôi sẽ nhận được trình gỡ lỗi khi cảnh báo ban đầu được kích hoạt. Đối với điều đó, bạn nên chạy:

options(error = recover)

Đây là một ví dụ:

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

Sau đó, bạn có thể bước vào bất kỳ khung nào trong số đó để xem điều gì đang xảy ra khi cảnh báo được đưa ra.

Để đặt lại các tùy chọn trên về mặc định, hãy nhập

options(error = NULL, warn = 0)

Đối với cảnh báo cụ thể mà bạn trích dẫn, rất có thể bạn cần cho phép nhiều lần lặp lại trong mã. Khi bạn đã tìm ra cái gì đang gọi glm.fit, hãy tìm cách chuyển nó vào controlđối số bằng cách sử dụng glm.control- see ?glm.control.


4
câu trả lời chính xác. Một lưu ý của sự bi quan là các loại lỗi hội tụ này thường xảy ra với các tập dữ liệu không ổn định / không ổn định (tách biệt hoàn toàn, v.v.) và cửa sổ giữa 'hội tụ tốt' và 'không hội tụ nhưng không thể sửa bằng cách tăng số lượng lặp - cần một số thay đổi mạnh mẽ hơn' thường hẹp
Bến Bolker

3
Gavin, tôi đánh bại bạn 25 giây. Tôi yêu cầu bạn xóa câu trả lời quá hữu ích của mình và ngừng ăn cắp phiếu ủng hộ của tôi. ;-)
Joshua Ulrich

@Ben điểm tuyệt vời. Nếu vấn đề của David là sự tách biệt thì việc tăng số lần lặp lại không hữu ích, nó vẫn sẽ không hội tụ. Tại thời điểm đó, nhìn vào các ước tính và sai số tiêu chuẩn có thể cho thấy có vấn đề. Tôi cũng sẽ thấy cảnh báo về các giá trị được lắp bằng số 0 hoặc 1 nếu sự phân tách hoặc tương tự là vấn đề. Nếu upping số lần lặp lại không giúp đỡ, David có thể đăng bài khác Q để được giúp đỡ và tôi có thể ăn cắp hơn @ upvotes Joshua ;-)
Gavin Simpson

1
@Joshua, không có cách nào để đánh bại anh ta. Tôi đã ngừng đếm số phiếu ủng hộ mà tôi có thể đã mất vì anh ấy. Nhưng dù sao sự giúp đỡ mà anh ấy cung cấp cho các tài khoản cho đến nay. Gotta tìm thấy hốc của riêng bạn khi bạn đánh bại anh ta. Tôi đề xuất số
phiếu ủng hộ

1
Chết tiệt @ ran2, bạn đã làm hỏng kế hoạch thống trị thế giới của tôi , Mwahahahahaha !!!!
Gavin Simpson

21

Vì vậy browser(), traceback()debug()đi bộ vào một quán bar, nhưng trace()đợi bên ngoài và tiếp tục chạy động cơ.

Bằng cách chèn vào browsermột nơi nào đó trong hàm của bạn, quá trình thực thi sẽ tạm dừng và chờ bạn nhập. Bạn có thể tiếp tục bằng cách sử dụng n(hoặc Enter), chạy toàn bộ đoạn (lặp lại) với c, kết thúc vòng lặp / chức năng hiện tại bằng f, hoặc thoát bằng Q; xem ?browser.

Với debug, bạn sẽ có được hiệu ứng tương tự như với trình duyệt, nhưng điều này sẽ dừng việc thực thi một chức năng khi bắt đầu. Các phím tắt tương tự cũng được áp dụng. Chức năng này sẽ ở chế độ "gỡ lỗi" cho đến khi bạn tắt nó bằng cách sử dụng undebug(nghĩa là sau khi debug(foo)chạy, chức năng foonày sẽ vào chế độ "gỡ lỗi" mọi lúc cho đến khi bạn chạy undebug(foo)).

Một giải pháp thay thế tạm thời hơn là debugonce, sẽ loại bỏ chế độ "gỡ lỗi" khỏi hàm sau lần đánh giá tiếp theo.

traceback sẽ cung cấp cho bạn luồng thực thi các chức năng cho đến khi có sự cố xảy ra (một lỗi thực tế).

traceVí dụ, bạn có thể chèn các bit mã (tức là các hàm tùy chỉnh) vào các hàm bằng cách sử dụng browser. Điều này rất hữu ích cho các chức năng từ các gói và bạn quá lười để có được mã nguồn được gấp lại độc đáo.


18

Chiến lược chung của tôi giống như sau:

  1. Chạy traceback()để xem tìm kiếm các vấn đề rõ ràng
  2. Đặt options(warn=2)để coi các cảnh báo như lỗi
  3. Đặt options(error=recover)để bước vào ngăn xếp cuộc gọi khi bị lỗi

15

Sau khi đi qua tất cả các bước gợi ý ở đây tôi chỉ biết được rằng thiết .verbose = TRUEtrong foreach()cũng mang lại cho tôi tấn thông tin hữu ích. Cụ thể là foreach(.verbose=TRUE)hiển thị chính xác vị trí xảy ra lỗi bên trong vòng lặp foreach, trong khi traceback()không nhìn vào bên trong vòng lặp foreach.


13

Trình gỡ lỗi của Mark Bravington có sẵn dưới dạng gói debugtrên CRAN rất tốt và khá dễ dàng.

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

Mã bật lên trong cửa sổ Tk được đánh dấu để bạn có thể xem những gì đang xảy ra và tất nhiên bạn có thể gọi mtrace() khi đang ở một chức năng khác.

HTH


11

Tôi thích câu trả lời của Gavin: Tôi không biết về các tùy chọn (lỗi = khôi phục). Tôi cũng thích sử dụng gói 'gỡ lỗi' cung cấp một cách trực quan để xem qua mã của bạn.

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

Tại thời điểm này, nó sẽ mở ra một cửa sổ gỡ lỗi riêng biệt hiển thị chức năng của bạn, với một dòng màu vàng cho biết bạn đang ở đâu trong mã. Trong cửa sổ chính, mã vào chế độ gỡ lỗi và bạn có thể tiếp tục nhấn enter để xem qua mã (và có các lệnh khác) và kiểm tra các giá trị biến, v.v. Dòng màu vàng trong cửa sổ gỡ lỗi tiếp tục di chuyển để hiển thị vị trí bạn đang ở trong mã. Khi hoàn tất việc gỡ lỗi, bạn có thể tắt tính năng theo dõi bằng:

mtrace.off()

5

Dựa trên câu trả lời tôi nhận được ở đây , bạn chắc chắn nên kiểm tra options(error=recover)cài đặt. Khi điều này được đặt, khi gặp lỗi, bạn sẽ thấy văn bản trên bảng điều khiển tương tự như sau ( tracebackđầu ra):

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

Tại thời điểm đó, bạn có thể chọn "khung" để nhập. Khi bạn thực hiện một lựa chọn, bạn sẽ được đưa vào browser()chế độ:

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

Và bạn có thể kiểm tra môi trường như ban đầu tại thời điểm xảy ra lỗi. Khi bạn hoàn tất, hãy nhập cđể đưa bạn trở lại menu chọn khung. Khi bạn hoàn tất, như nó cho bạn biết, hãy nhập 0để thoát.


4

Tôi đã đưa ra câu trả lời này cho một câu hỏi gần đây hơn, nhưng tôi sẽ thêm nó vào đây cho hoàn chỉnh.

Cá nhân tôi có xu hướng không sử dụng các chức năng để gỡ lỗi. Tôi thường thấy rằng điều này gây ra nhiều rắc rối như nó đã giải quyết được. Ngoài ra, xuất thân từ nền tảng Matlab, tôi thích có thể làm điều này trong môi trường phát triển tích hợp (IDE) hơn là làm điều này trong mã. Sử dụng IDE giữ cho mã của bạn sạch sẽ và đơn giản.

Đối với R, tôi sử dụng IDE có tên "RStudio" ( http://www.rstudio.com ), có sẵn cho windows, mac và linux và khá dễ sử dụng.

Các phiên bản của Rstudio kể từ khoảng tháng 10 năm 2013 (0.98ish?) Có khả năng thêm điểm ngắt trong các tập lệnh và chức năng: để thực hiện việc này, chỉ cần nhấp vào lề trái của tệp để thêm điểm ngắt. Bạn có thể đặt một điểm ngắt và sau đó chuyển qua từ điểm đó. Bạn cũng có quyền truy cập vào tất cả dữ liệu trong môi trường đó, vì vậy bạn có thể thử các lệnh.

Xem http://www.rstudio.com/ide/docs/debugging/overview để biết chi tiết. Nếu bạn đã cài đặt Rstudio, bạn có thể cần phải nâng cấp - đây là một tính năng tương đối mới (cuối năm 2013).

Bạn cũng có thể tìm thấy các IDE khác có chức năng tương tự.

Phải thừa nhận rằng, nếu đó là một chức năng tích hợp, bạn có thể phải dùng đến một số đề xuất của những người khác trong cuộc thảo luận này. Tuy nhiên, nếu đó là mã của riêng bạn cần sửa, giải pháp dựa trên IDE có thể chính là thứ bạn cần.


1

Để gỡ lỗi các phương thức Lớp tham chiếu mà không có tham chiếu phiên bản

ClassName$trace(methodName, browser)

0

Tôi bắt đầu nghĩ rằng việc không in số dòng lỗi - một yêu cầu cơ bản nhất - THEO DEFAILT- là một trò đùa nào đó trong R / Rstudio . Phương pháp đáng tin cậy duy nhất mà tôi đã tìm thấy để tìm ra nơi xảy ra lỗi là thực hiện thêm nỗ lực của việc ghi lại dấu vết () và xem dòng trên cùng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.