Ogólne sugestie dotyczące debugowania w R

120

Otrzymuję błąd podczas używania funkcji R, którą napisałem:

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

Co zrobiłem:

  1. Przejdź przez funkcję
  2. Dodanie print, aby dowiedzieć się, w której linii występuje błąd, sugeruje dwie funkcje, których nie należy używać glm.fit. Są window()i save().

Moje ogólne podejście obejmuje dodawanie printi stoppolecenia oraz przechodzenie przez funkcję wiersz po wierszu, aż będę mógł zlokalizować wyjątek.

Jednak nie jest dla mnie jasne, jeśli korzystam z tych technik, skąd ten błąd w kodzie pochodzi. Nie jestem nawet pewien, od jakich funkcji w kodzie zależą glm.fit. Jak mam zdiagnozować ten problem?

David LeBauer
źródło
5
Sprawdź stronę Duncana Murdocha na temat debugowania w R
Rob Hyndman,
10
Ok, powiem oczywiste: to ostrzeżenie, a nie błąd .
Gavin Simpson
10
@ gavin-simpson Nie zdawałem sobie sprawy, że istnieje różnica techniczna, dzięki za zwrócenie uwagi. Ale ostatecznie wskazuje to, że moja wcześniej funkcjonalna funkcja jest dysfunkcyjna.
David LeBauer
11
@David +1 za „... moja poprzednia funkcja jest dysfunkcyjna”.
Joshua Ulrich
5
@David: re your ps. To dodaje wymiar do pytania, które zostałoby pominięte bez przykładu; mianowicie, jak sprawić, aby R wszedł w tryb debugowania, gdy generowane są tylko ostrzeżenia? Gdybyś pominął ten szczegół, nie wszyscy byśmy ci wskazali options(warn = 2). W tym przypadku szczegóły są niezbędne, aby odpowiedzieć na twoje ogólne pytanie. +1 ode mnie.
Gavin Simpson

Odpowiedzi:

167

Powiedziałbym, że debugowanie to forma sztuki, więc nie ma wyraźnej srebrnej kuli. Istnieją dobre strategie debugowania w dowolnym języku i mają one zastosowanie również tutaj (np. Przeczytaj ten fajny artykuł ). Na przykład, pierwszą rzeczą jest odtworzenie problemu ... jeśli nie możesz tego zrobić, musisz uzyskać więcej informacji (np. Z logowaniem). Kiedy będziesz mógł go odtworzyć, musisz zredukować go do źródła.

Zamiast „sztuczki” powiedziałbym, że mam ulubioną procedurę debugowania:

  1. Kiedy pojawia się błąd, pierwszą rzeczą, którą zwykle robię, jest spojrzenie na ślad stosu przez wywołanie traceback(): to pokazuje, gdzie wystąpił błąd, co jest szczególnie przydatne, jeśli masz kilka zagnieżdżonych funkcji.
  2. Następnie ustawię options(error=recover); powoduje to natychmiastowe przełączenie w tryb przeglądarki, w którym występuje błąd, dzięki czemu można stamtąd przeglądać obszar roboczy.
  3. Jeśli nadal nie mam wystarczających informacji, zwykle używam tej debug()funkcji i przechodzę przez skrypt wiersz po wierszu.

Najlepszą nową sztuczką w R 2.10 (podczas pracy z plikami skryptów) jest użycie funkcji findLineNum()i setBreakpoint().

Na koniec: w zależności od błędu bardzo pomocne jest również ustawienie try()lub tryCatch()instrukcje wokół wywołań funkcji zewnętrznych (szczególnie w przypadku klas S4). Czasami zapewnia to jeszcze więcej informacji, a także daje większą kontrolę nad sposobem obsługi błędów w czasie wykonywania.

Te powiązane pytania mają wiele sugestii:

Shane
źródło
8
Możesz również dodać debugonce () do debug ().
Joris Meys
2
Chociaż nie tylko przydatne podczas debugowania, fix (df1) otwiera graficzny edytor języka R z załadowaną ramką danych df1, którą można edytować w locie lub po prostu rzucić okiem.
Dmitrii I.
debugowanie w R wydaje się być bardzo trudne, na przykład nie ma łatwego rozwiązania, aby zobaczyć linie kodu z ostrzeżeniami
TMS
browser()gdy występują błędy, które nie powodują ostrzeżeń / błędów (źródło: Roman Luštrik na tej stronie). Jakieś inne narzędzie browser()?
PatrickT
38

Najlepsza solucja, jaką do tej pory widziałem, to:

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

Czy ktoś się zgadza / nie zgadza?

Christopher DuBois
źródło
Bardzo dokładny przewodnik - opisuje podstawowe narzędzia zawarte w rdzeniu R: debug (), traceback () i recovery ().
Sharpie
32

Jak wskazano mi się kolejne pytanie , Rprof()a summaryRprof()są ładne narzędzia znaleźć powolne części swojego programu , że korzyści może z przyspieszenia lub przeprowadzce do realizacji C / C ++. Prawdopodobnie ma to większe zastosowanie, jeśli wykonujesz prace symulacyjne lub inne działania wymagające dużej mocy obliczeniowej lub danych. profrPakiet może pomóc w wizualizacji wyników.

Jestem w trakcie nauki o debugowaniu, więc kolejna sugestia z innego wątku :

  • Ustawiono, options(warn=2)aby traktować ostrzeżenia jak błędy

Możesz również użyć optionsswojej ulubionej, wybranej funkcji debugowania, aby rzucić się w wir akcji, gdy wystąpi błąd lub ostrzeżenie. Na przykład:

  • Ustawiony options(error=recover)do uruchomienia, recover()gdy wystąpi błąd, jak zauważył Shane (i jak jest to udokumentowane w przewodniku po debugowaniu języka R.) Lub jakakolwiek inna przydatna funkcja, którą warto uruchomić.

I jeszcze dwie metody z jednego z linków @ Shane'a :

  • Zawiń wywołanie funkcji wewnętrznej, try()aby zwrócić więcej informacji na jej temat.
  • W przypadku funkcji * .inform=TRUEApply użyj (z pakietu plyr) jako opcji do polecenia Apply

@JoshuaUlrich wskazał również zgrabny sposób wykorzystania warunkowych możliwości klasycznego browser()polecenia do włączania / wyłączania debugowania:

  • Umieść wewnątrz funkcji, którą możesz chcieć debugować browser(expr=isTRUE(getOption("myDebug")))
  • I ustaw opcję globalną według options(myDebug=TRUE)
  • Możesz nawet zawinąć wywołanie przeglądarki: myBrowse <- browser(expr=isTRUE(getOption("myDebug")))a następnie zadzwonić z, myBrowse()ponieważ używa globals.

Następnie dostępne są nowe funkcje w R 2.10:

  • findLineNum()pobiera nazwę pliku źródłowego i numer linii oraz zwraca funkcję i środowisko. Wydaje się to być pomocne, gdy source()plik .R zwraca błąd w wierszu # n, ale musisz wiedzieć, jaka funkcja znajduje się w wierszu # n.
  • setBreakpoint() pobiera nazwę pliku źródłowego i numer linii i ustawia tam punkt przerwania

Codetools pakiet, a zwłaszcza jego checkUsagefunkcja może być szczególnie pomocny w szybko zbierając składni i błędy stylistyczne, że kompilator będzie typowo raportu (niewykorzystane mieszkańców, niezdefiniowane globalne funkcje i zmienne, częściowe dopasowanie argumentów, i tak dalej).

setBreakpoint()jest bardziej przyjaznym dla użytkownika interfejsem użytkownika trace(). Szczegółowe informacje na temat tego, jak to działa, można znaleźć w ostatnim artykule w R Journal .

Jeśli próbujesz debugować pakiet innej osoby, po zlokalizowaniu problemu możesz nadpisać jego funkcje za pomocą fixInNamespacei assignInNamespace, ale nie używaj tego w kodzie produkcyjnym.

Nic z tego nie powinno wykluczać wypróbowanych i prawdziwych standardowych narzędzi do debugowania języka R , z których niektóre są powyżej, a inne nie. W szczególności narzędzia do debugowania post-mortem są przydatne, gdy masz czasochłonny pakiet kodu, którego wolisz nie uruchamiać ponownie.

Wreszcie, w przypadku trudnych problemów, które nie wydają się generować komunikatu o błędzie, możesz użyć options(error=dump.frames)szczegółów w tym pytaniu: Błąd bez zgłaszania błędu

Ari B. Friedman
źródło
1
+1 za całą pracę, jaką włożyłeś w połączenie tych pytań w jedno i pozostawienie go otwartego!
GSee,
29

W pewnym momencie glm.fitjest wzywany. Oznacza to, że jedną z funkcji zadzwonić lub jedną z funkcji wywoływanych przez te funkcje korzystają albo glm, glm.fit.

Ponadto, jak wspomniałem w moim komentarzu powyżej, jest to ostrzeżenie, a nie błąd , co robi dużą różnicę. Nie możesz wywołać żadnego narzędzia debugowania języka R z ostrzeżenia (z domyślnymi opcjami, zanim ktoś mi powie, że się mylę ;-).

Jeśli zmienimy opcje, aby zamienić ostrzeżenia w błędy, możemy zacząć używać narzędzi do debugowania R. Od ?optionsmamy:

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

Więc jeśli uciekniesz

options(warn = 2)

następnie uruchom kod, R zgłosi błąd. W którym momencie możesz biec

traceback()

aby zobaczyć stos wywołań. Oto przykład.

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

Tutaj możesz zignorować ramki oznaczone 4:i wyższe. Widzimy, że foonazywa bar, a bargenerowane ostrzeżenie. To powinno pokazać, które funkcje wywoływały glm.fit.

Jeśli teraz chcesz to debugować, możemy skorzystać z innej opcji, aby powiedzieć R, aby wszedł do debugera, gdy napotka błąd, a ponieważ popełniliśmy błędy ostrzeżeń, po uruchomieniu oryginalnego ostrzeżenia otrzymamy debuger. W tym celu powinieneś uruchomić:

options(error = recover)

Oto przykład:

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

Następnie możesz wejść do dowolnej z tych ramek, aby zobaczyć, co się działo, gdy wyświetlono ostrzeżenie.

Aby zresetować powyższe opcje do wartości domyślnych, wprowadź

options(error = NULL, warn = 0)

Jeśli chodzi o konkretne ostrzeżenie, które cytujesz, jest wysoce prawdopodobne, że musisz zezwolić na więcej iteracji w kodzie. Kiedy już dowiesz się, co wywołuje glm.fit, zastanów się, jak przekazać controlargument, używając glm.control- patrz ?glm.control.

Gavin Simpson
źródło
4
świetna odpowiedź. pesymizmem jest to, że tego rodzaju błędy zbieżności często występują przy niestabilnych / chwiejnych zestawach danych (całkowita separacja itp.), a okno między `` zbieżność w porządku '' i `` niezbieżna, ale nie można jej naprawić poprzez zwiększenie liczby iteracji - wymaga bardziej drastycznej zmiany ”jest często wąska
Ben Bolker
3
Gavin, pokonałem cię o 25 sekund. Domagam się usunięcia nadmiernie pomocnej odpowiedzi i zaprzestania kradzieży moich głosów za. ;-)
Joshua Ulrich
@Ben świetna uwaga. Jeśli problemem Davida jest separacja, to zwiększenie liczby iteracji nie powinno pomóc, nadal nie powinno się zbiegać. W tym momencie spojrzenie na szacunki i standardowe błędy może sugerować, że jest problem. Spodziewałbym się również wyświetlenia ostrzeżenia o dopasowanych wartościach w postaci liczbowej 0 lub 1, jeśli separacja lub podobne byłyby problemem. Jeśli zwiększenie liczby iteracji nie pomoże, David może opublikować kolejne pytanie Q o pomoc, a ja mogę ukraść więcej głosów od @ Joshua ;-)
Gavin Simpson
1
@Joshua, nie ma sposobu, aby go pokonać. Przestałem liczyć pozytywne głosy, które mogłem przez niego stracić. Ale w każdym razie pomoc, którą zapewnia, wyjaśnia to zdecydowanie. Musisz znaleźć własne nisze, gdybyś go pokonał. Proponuję tutaj głosy za za naciśnięciem klawisza ... :)
Matt Bannert,
1
Cholera @ ran2, udaremniłeś mój nikczemny, przebiegły plan przejęcia władzy nad światem , Mwahahahahaha !!!!
Gavin Simpson
21

Tak browser(), traceback()i debug()iść do baru, ale trace()czeka na zewnątrz i utrzymuje pracę silnika.

Wstawiając browsergdzieś w swojej funkcji, wykonanie zatrzyma się i będzie czekać na dane wejściowe. Możesz przejść do przodu za pomocą n(lub Enter), uruchomić cały fragment (iterację) za pomocą c, zakończyć bieżącą pętlę / funkcję za pomocą flub zakończyć za pomocą Q; zobacz ?browser.

W przypadku debuguzyskuje się ten sam efekt, co w przypadku przeglądarki, ale powoduje to zatrzymanie wykonywania funkcji na jej początku. Obowiązują te same skróty. Ta funkcja będzie w trybie „debugowania”, dopóki nie wyłączysz jej za pomocą undebug(to znaczy, po debug(foo)uruchomieniu funkcji za fookażdym razem, aż do jej uruchomienia, wejdzie w tryb „debugowania” undebug(foo)).

Bardziej przejściową alternatywą jest debugonceusunięcie trybu „debugowania” z funkcji po jej następnym oszacowaniu.

traceback da ci przepływ wykonywania funkcji aż do miejsca, w którym coś poszło nie tak (rzeczywisty błąd).

Możesz wstawiać bity kodu (tj. Funkcje niestandardowe) w funkcjach używając tracena przykład browser. Jest to przydatne w przypadku funkcji z pakietów i jesteś zbyt leniwy, aby uzyskać ładnie zwinięty kod źródłowy.

Roman Luštrik
źródło
18

Moja ogólna strategia wygląda następująco:

  1. Biegnij, traceback()aby zobaczyć, szukać oczywistych problemów
  2. Ustawiono, options(warn=2)aby traktować ostrzeżenia jak błędy
  3. Ustawiono options(error=recover)wchodzenie na stos wywołań w przypadku błędu
Joshua Ulrich
źródło
15

Po przejściu przez wszystkie etapy proponowane tutaj Właśnie dowiedziałem się, że ustawienie .verbose = TRUEw foreach()również daje mi mnóstwo przydatnych informacji. W szczególności foreach(.verbose=TRUE)pokazuje dokładnie, gdzie występuje błąd w pętli foreach, a traceback()nie zagląda do pętli foreach.

Michael Schneider
źródło
13

Debugger Marka Bravingtona, który jest dostępny jako pakiet debugna CRAN, jest bardzo dobry i całkiem prosty.

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

Kod pojawia się w podświetlonym oknie Tk, więc możesz zobaczyć, co się dzieje i oczywiście możesz zadzwonić do innego mtrace() będąc w innej funkcji.

HTH

David Lawrence Miller
źródło
11

Podoba mi się odpowiedź Gavina: nie wiedziałem o opcjach (błąd = odzyskać). Lubię też używać pakietu „debug”, który zapewnia wizualny sposób przechodzenia przez kod.

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

W tym momencie otwiera się osobne okno debugowania pokazujące twoją funkcję, z żółtą linią pokazującą, gdzie jesteś w kodzie. W głównym oknie kod przechodzi w tryb debugowania i możesz wciskać enter, aby przechodzić przez kod (są też inne polecenia) i sprawdzać wartości zmiennych itp. Żółta linia w oknie debugowania przesuwa się, aby pokazać, gdzie jesteś w kodzie. Po zakończeniu debugowania możesz wyłączyć śledzenie za pomocą:

mtrace.off()
Prasad Chalasani
źródło
5

W oparciu o odpowiedź, którą tutaj otrzymałem , zdecydowanie powinieneś sprawdzić options(error=recover)ustawienie. Gdy ta opcja jest ustawiona, po napotkaniu błędu na konsoli zostanie wyświetlony tekst podobny do następującego ( tracebackdane wyjściowe):

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

W którym momencie możesz wybrać, do której „ramki” chcesz wejść. Kiedy dokonasz wyboru, przejdziesz do browser()trybu:

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

Możesz też zbadać środowisko, jakie było w momencie wystąpienia błędu. Kiedy skończysz, wpisz ctekst, aby wrócić do menu wyboru ramek. Jak skończysz, wpisz, 0aby zakończyć.

eykanal
źródło
4

Udzieliłem tej odpowiedzi na nowsze pytanie, ale dodam ją tutaj dla kompletności.

Osobiście nie używam funkcji do debugowania. Często stwierdzam, że powoduje to tyle samo problemów, co rozwiązuje. Ponadto, wywodząc się ze środowiska Matlab, wolę móc to robić w zintegrowanym środowisku programistycznym (IDE), zamiast robić to w kodzie. Korzystanie ze środowiska IDE sprawia, że ​​kod jest czysty i prosty.

W R używam IDE o nazwie „RStudio” ( http://www.rstudio.com ), które jest dostępne dla systemów Windows, Mac i Linux i jest dość łatwe w użyciu.

Wersje Rstudio od około października 2013 r. (0.98ish?) Mają możliwość dodawania punktów przerwania w skryptach i funkcjach: w tym celu wystarczy kliknąć lewy margines pliku, aby dodać punkt przerwania. Możesz ustawić punkt przerwania, a następnie przejść od tego punktu. Masz również dostęp do wszystkich danych w tym środowisku, więc możesz wypróbować polecenia.

Szczegółowe informacje można znaleźć pod adresem http://www.rstudio.com/ide/docs/debugging/overview . Jeśli masz już zainstalowane oprogramowanie Rstudio, może być konieczna aktualizacja - jest to stosunkowo nowa (koniec 2013 r.) Funkcja.

Możesz również znaleźć inne IDE, które mają podobną funkcjonalność.

Trzeba przyznać, że jeśli jest to funkcja wbudowana, być może będziesz musiał skorzystać z niektórych sugestii przedstawionych przez inne osoby w tej dyskusji. Ale jeśli jest to Twój własny kod, który wymaga naprawy, rozwiązanie oparte na IDE może być właśnie tym, czego potrzebujesz.

Andy Clifton
źródło
1

Aby debugować metody klasy referencyjnej bez odwołania do instancji

ClassName$trace(methodName, browser)
Siva
źródło
0

Zaczynam myśleć, że niewypisanie numeru linii błędu - najbardziej podstawowy wymóg - BY DEFAILT - to jakiś żart w R / Rstudio . Jedyną niezawodną metodą, jaką znalazłem, aby znaleźć miejsce wystąpienia błędu, jest wykonanie dodatkowego wysiłku w postaci wywołania funkcji traceback () i zobaczenie górnej linii.

user9669128
źródło