Общие предложения по отладке в R

120

Я получаю сообщение об ошибке при использовании написанной мной функции R:

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

Что я наделал:

  1. Пройдите через функцию
  2. Добавление print, чтобы узнать, в какой строке происходит ошибка, предлагает две функции, которые не следует использовать glm.fit. Они есть window()и save().

Мои общие подходы включают добавление printи stopкоманды, а также пошаговое выполнение функции построчно, пока я не найду исключение.

Однако мне неясно, используя эти методы, откуда эта ошибка в коде. Я даже не уверен, от каких функций в коде зависит glm.fit. Как мне диагностировать эту проблему?

Дэвид Лебауэр
источник
5
Посетите страницу Дункана Мердока об отладке в R
Роб Хиндман,
10
Хорошо, я заявлю очевидное: это предупреждение, а не ошибка .
Гэвин Симпсон,
10
@ gavin-simpson Я не понимал, что есть техническая разница, спасибо, что указали на это. Но, в конце концов, это указывает на то, что моя прежняя функциональная функция нарушена.
Дэвид ЛеБауэр
11
@David +1 за "... моя прежняя функциональная функция не работает".
Джошуа Ульрих,
5
@ Дэвид: повтори свой пс. Это добавляет измерение к вопросу, которое было бы упущено без примера; а именно, как заставить R перейти в режим отладки, когда выдаются только предупреждения? Если бы вы упустили эту деталь, мы бы не указали вам на это options(warn = 2). Так что в этом случае детали важны для ответа на ваш общий вопрос. +1 от меня.
Гэвин Симпсон,

Ответы:

167

Я бы сказал, что отладка - это искусство, поэтому нет чистой серебряной пули. Есть хорошие стратегии для отладки на любом языке, и они применимы и здесь (например, прочтите эту хорошую статью ). Например, первое, что нужно сделать, - это воспроизвести проблему ... если вы не можете этого сделать, вам нужно получить дополнительную информацию (например, с помощью регистрации). Как только вы сможете воспроизвести его, вам нужно свести его к источнику.

Вместо «уловки» я бы сказал, что у меня есть любимая процедура отладки:

  1. Когда возникает ошибка, первое, что я обычно делаю, это просматриваю трассировку стека, вызывая traceback(): который показывает вам, где произошла ошибка, что особенно полезно, если у вас есть несколько вложенных функций.
  2. Далее я установлю options(error=recover); это немедленно переключается в режим браузера, где возникает ошибка, поэтому вы можете просматривать рабочее пространство оттуда.
  3. Если у меня все еще недостаточно информации, я обычно использую эту debug()функцию и шаг за шагом просматриваю скрипт.

Лучший новый трюк в R 2.10 (при работе с файлами сценариев) заключается в использовании findLineNum()и setBreakpoint()функции.

В качестве заключительного комментария: в зависимости от ошибки также очень полезно установить операторы try()or tryCatch()вокруг вызовов внешних функций (особенно при работе с классами S4). Иногда это дает даже больше информации, а также дает вам больше контроля над обработкой ошибок во время выполнения.

В этих связанных вопросах есть много предложений:

Шейн
источник
8
Вы также можете добавить debugonce () в debug ().
Джорис Мейс
2
Хотя fix (df1) полезен не только при отладке, он открывает графический редактор R с загруженным в него фреймом данных df1, который вы можете редактировать на лету или просто взглянуть.
Дмитрий I.
отладка в R кажется очень сложной, например, нет простого решения, чтобы увидеть строки кода предупреждений
TMS
browser()когда есть ошибки, которые не вызывают предупреждений / ошибок (кредит: Роман Луштрик на этой странице). Любой другой инструмент нравится browser()?
PatrickT
38

Лучшее прохождение, которое я видел до сих пор:

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

Кто-нибудь согласен / не согласен?

Кристофер Дюбуа
источник
Очень подробное руководство - описывает основные инструменты, включенные в ядро ​​R: debug (), traceback () и recovery ().
Sharpie
32

Как было отмечено, для меня в другом вопросе , Rprof()и summaryRprof()есть хорошие инструменты , чтобы найти медленные части вашей программы , которые могли бы извлечь выгоду от ускорения или перехода к реализации С / С ++. Это, вероятно, больше применимо, если вы выполняете симуляцию или другую деятельность, требующую большого объема вычислений или данных. profrПакет может помочь визуализировать результаты.

Я немного начинаю изучать отладку, поэтому еще одно предложение из другого потока :

  • Установите options(warn=2)для обработки предупреждений как ошибок

Вы также можете использовать optionsэту функцию, чтобы сразу погрузиться в бой при возникновении ошибки или предупреждения, используя вашу любимую функцию отладки по вашему выбору. Например:

  • Набор options(error=recover)для запуска recover()при возникновении ошибки, как отметил Шейн (и , как документирован в R отладка руководства . Или любая другая удобная функцию , которую вы нашли бы полезно иметь бежать.

И еще два метода из одной из ссылок @ Shane :

  • Оберните вызов внутренней функции, 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()есть файл .R, и он возвращает ошибку в строке #n, но вам нужно знать, какая функция находится в строке #n.
  • setBreakpoint() берет имя исходного файла и номер строки и устанавливает там точку останова

Пакет codetools и, в частности, его checkUsageфункция могут быть особенно полезны для быстрого выявления синтаксических и стилистических ошибок, о которых обычно сообщает компилятор (неиспользуемые локальные переменные, неопределенные глобальные функции и переменные, частичное сопоставление аргументов и т. Д.).

setBreakpoint()- это более удобный интерфейс для trace(). Подробности о том, как это работает, доступны в недавней статье R Journal .

Если вы пытаетесь отлаживать чужой пакет, обнаружив проблему, вы можете перезаписать его функции с помощью fixInNamespaceи assignInNamespace, но не используйте это в производственном коде.

Ничто из этого не должно препятствовать использованию проверенных временем стандартных инструментов отладки R , некоторые из которых указаны выше, а другие - нет. В частности, инструменты посмертной отладки удобны, когда у вас есть трудоемкий набор кода, который вы не хотели бы запускать повторно.

Наконец, для сложных проблем, которые, похоже, не вызывают сообщения об ошибке, вы можете использовать, options(error=dump.frames)как описано в этом вопросе: Ошибка без появления ошибки

Ари Б. Фридман
источник
1
+1 за всю работу, которую вы вложили, чтобы объединить эти вопросы в один, а затем оставить его открытым!
GSee
29

В какой-то момент glm.fitвызывается. Это означает , что одна из функций вы называете или одной из функций , вызываемых этими функциями используется либо glm, glm.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вызван barи 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, подумайте, как передать ему controlаргумент, используя glm.control- см ?glm.control.

Гэвин Симпсон
источник
4
отличный ответ. одно замечание пессимизма заключается в том, что такого рода ошибки сходимости часто возникают с нестабильными / нестабильными наборами данных (полное разделение и т. д.), а окно между «очень хорошо сходится» и «неконвергентно, но не может быть исправлено путем увеличения числа. итераций - нуждается в более радикальных изменениях 'часто узко
Бен Болкер
3
Гэвин, я опередил тебя на 25 секунд. Я требую, чтобы вы удалили свой излишне полезный ответ и прекратили воровать мои голоса. ;-)
Джошуа Ульрих
@Ben отличный момент. Если проблема Дэвида в разделении, то увеличение числа итераций не должно помочь, оно все равно не должно сойтись. В этот момент просмотр оценок и стандартных ошибок может указывать на наличие проблемы. Я также ожидал бы увидеть предупреждение о числовых значениях 0 или 1, если разделение или подобное было проблемой. Если увеличение количества итераций не помогает, Дэвид может опубликовать еще один вопрос о помощи, и я могу украсть больше голосов @Joshua ;-)
Гэвин Симпсон
1
@ Джошуа, его невозможно победить. Я перестал подсчитывать голоса, которые я мог потерять из-за него. Но в любом случае помощь, которую он оказывает, однозначно объясняет это. Надо найти свои собственные ниши, где бы ты его победил. Я предлагаю проголосовать за каждое нажатие здесь ... :)
Мэтт Баннерт,
1
Проклятье @ ran2, ты сорвал мой подлый коварный план по захвату мира , Мвахахахахаха !!!!
Гэвин Симпсон
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 = TRUEв foreach()тоже дает мне тонны полезной информации. В частности, foreach(.verbose=TRUE)показывает, где именно происходит ошибка внутри цикла foreach, но traceback()не заглядывает внутрь цикла foreach.

Майкл Шнайдер
источник
13

Отладчик Марка Бравингтона, доступный в виде пакета debugна CRAN, очень хорош и довольно прост.

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() находясь в другой функции.

НТН

Дэвид Лоуренс Миллер
источник
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чтобы выйти.

eykanal
источник
4

Я дал этот ответ на более свежий вопрос, но добавляю его здесь для полноты картины.

Лично я не использую функции для отладки. Я часто обнаруживаю, что это доставляет столько же проблем, сколько и решает. Кроме того, исходя из опыта работы с Matlab, мне нравится делать это в интегрированной среде разработки (IDE), а не в коде. Использование IDE делает ваш код чистым и простым.

Для R я использую среду IDE под названием «RStudio» ( http://www.rstudio.com ), которая доступна для Windows, Mac и Linux и довольно проста в использовании.

Версии Rstudio, выпущенные примерно с октября 2013 года (0.98ish?), Имеют возможность добавлять точки останова в сценарии и функции: для этого просто щелкните левое поле файла, чтобы добавить точку останова. Вы можете установить точку останова, а затем перейти к ней. У вас также есть доступ ко всем данным в этой среде, поэтому вы можете опробовать команды.

Подробнее см. Http://www.rstudio.com/ide/docs/debugging/overview . Если у вас уже установлен Rstudio, вам может потребоваться обновление - это относительно новая функция (конец 2013 года).

Вы также можете найти другие IDE с аналогичной функциональностью.

По общему признанию, если это встроенная функция, вам, возможно, придется прибегнуть к некоторым предложениям, сделанным другими людьми в этом обсуждении. Но если это ваш собственный код, который требует исправления, решение на основе IDE может быть именно тем, что вам нужно.

Энди Клифтон
источник
1

Отладка методов ссылочного класса без ссылки на экземпляр

ClassName$trace(methodName, browser)
Сива
источник
0

Я начинаю думать, что не печатать номер строки ошибки - самое основное требование - ПО УМОЛЧАНИЮ - это какая-то шутка в R / Rstudio . Единственный надежный метод, который я нашел, чтобы определить, где произошла ошибка, - это приложить дополнительные усилия для вызова функции traceback () и увидеть верхнюю строку.

user9669128
источник