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

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

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

Что я сделал:

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

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

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

Ответ 1

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

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

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

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

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

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

Ответ 3

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

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

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

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

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

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

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

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

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

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

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

Ответ 4

В какой-то момент вызывается 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:

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

В reset приведенные выше параметры по умолчанию, введите

options(error = NULL, warn = 0)

Что касается специального предупреждения, которое вы цитируете, очень вероятно, что вам нужно разрешить больше итераций в коде. Когда вы узнаете, что вызывает glm.fit, продумайте, как передать ему аргумент control, используя glm.control - см. ?glm.control.

Ответ 5

So browser(), traceback() и debug() входят в панель, но trace() ждет снаружи и удерживает двигатель.

Вставив browser где-нибудь в вашу функцию, выполнение остановится и дождитесь ввода. Вы можете двигаться вперед, используя n (или Enter), запустите весь фрагмент (итерацию) с помощью c или закройте с помощью Q.

С debug вы получаете тот же эффект, что и в браузере, но это останавливает выполнение функции в ее начале. Используются те же ярлыки. Эта функция находится в режиме "отладки", пока вы не выключите usig undebug. Начиная с 2.10 вы также можете сделать debugonce.

traceback даст вам поток выполнения функций вплоть до места, где что-то пошло не так (фактическая ошибка).

Вы можете вставлять биты кода (например, пользовательские функции) в функции с помощью trace, например browser. Это полезно для функций из пакетов, и вы слишком ленивы, чтобы получить красиво сложенный исходный код.

Ответ 6

Моя общая стратегия выглядит так:

  • Запустите traceback(), чтобы увидеть очевидные проблемы.
  • Установите options(warn=2) для обработки предупреждений, таких как ошибки
  • Установите options(error=recover) для входа в стек вызовов при ошибке

Ответ 7

Пройдя все шаги, предлагаемые здесь, я просто узнал, что установка .verbose = TRUE в foreach() также дает мне массу полезной информации. В частности, foreach(.verbose=TRUE) точно показывает, где ошибка возникает внутри цикла foreach, а traceback() не смотрит внутрь цикла foreach.

Ответ 8

Отметчик Mark Bravington, который доступен как пакет 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() в другой функции.

НТН

Ответ 9

Мне нравится ответ Гэвина: я не знал о вариантах (ошибка = восстановление). Я также хотел бы использовать пакет "debug", который дает визуальный способ перехода через ваш код.

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

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

mtrace.off()

Ответ 10

В соответствии с ответом, который я получил здесь, вам обязательно нужно проверить options(error=recover). Когда это будет установлено, после возникновения ошибки вы увидите текст на консоли, подобный следующему (traceback output):

> 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 для выхода.

Ответ 11

Я дал этот ответ более позднему вопросу, но добавляю его здесь для полноты.

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

Для R я использую IDE под названием "RStudio" (http://www.rstudio.com), который доступен для окон, mac и linux и довольно проста в использовании.

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

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

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

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

Ответ 12

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

ClassName$trace(methodName, browser)

Ответ 13

Я начинаю думать, что не печатать номер строки ошибки - самое основное требование - BY DEFAILT- - это своего рода шутка в R/Rstudio. Единственный надежный метод, который я нашел, чтобы найти, где произошла ошибка, - это сделать дополнительные усилия для calloing traceback () и увидеть верхнюю строку.