Sugerencias generales para la depuración en R

Aparece un error cuando uso una función R que escribí:

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

Que he hecho:

  1. Paso a través de la función
  2. Agregar impresión para averiguar en qué línea se produce el error sugiere dos funciones que no deberían usar glm.fit . Son window() y save() .

Mis enfoques generales incluyen agregar comandos de print y stop , y recorrer una función línea por línea hasta que pueda ubicar la excepción.

Sin embargo, no está claro para mí usar esas técnicas de donde proviene este error en el código. Ni siquiera estoy seguro de qué funciones del código dependen de glm.fit . ¿Cómo hago para diagnosticar este problema?

Yo diría que la depuración es una forma de arte, por lo que no hay una bala de plata clara. Hay buenas estrategias para la depuración en cualquier idioma, y ​​se aplican aquí también (por ejemplo, lea este buen artículo ). Por ejemplo, lo primero es reproducir el problema … si no puede hacer eso, entonces necesita obtener más información (por ejemplo, con el registro). Una vez que pueda reproducirlo, debe reducirlo a la fuente.

En lugar de un “truco”, diría que tengo una rutina de depuración favorita:

  1. Cuando se produce un error, lo primero que suelo hacer es mirar el seguimiento de la stack llamando a traceback() : eso muestra dónde ocurrió el error, lo que es especialmente útil si tiene varias funciones anidadas.
  2. A continuación, estableceré las options(error=recover) ; esto cambia inmediatamente al modo de navegador donde ocurre el error, para que pueda navegar por el espacio de trabajo desde allí.
  3. Si aún no tengo suficiente información, generalmente uso la función debug() y paso por el script línea por línea.

El mejor truco nuevo en R 2.10 (cuando se trabaja con archivos de script) es usar las findLineNum() y setBreakpoint() .

Como comentario final: dependiendo del error, también es muy útil establecer declaraciones try() o tryCatch() alrededor de llamadas a funciones externas (especialmente cuando se trata de clases S4). A veces, esto proporcionará aún más información, y también le brinda más control sobre cómo se manejan los errores en tiempo de ejecución.

Estas preguntas relacionadas tienen muchas sugerencias:

  • Herramientas de depuración para el lenguaje R
  • Depuración de las llamadas a lapply / sapply
  • Obtener el estado de las variables después de que ocurra un error en R
  • R números de línea de script en el error?

El mejor tutorial que he visto hasta ahora es:

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

¿Alguien está de acuerdo / en desacuerdo?

Como se me señaló en otra pregunta , Rprof() y summaryRprof() son buenas herramientas para encontrar partes lentas de su progtwig que podrían beneficiarse de la aceleración o el cambio a una implementación de C / C ++. Esto probablemente se aplica más si está haciendo un trabajo de simulación u otras actividades de uso intensivo de datos o de cómputo. El paquete profr puede ayudar a visualizar los resultados.

Estoy en una especie de aprende sobre la eliminación de errores, por lo que otra sugerencia de otro hilo :

  • Establecer options(warn=2) para tratar advertencias como errores

También puede usar options para lanzarlo directamente al calor de la acción cuando ocurre un error o advertencia, usando su función favorita de depuración. Por ejemplo:

  • Establezca options(error=recover) para ejecutar recover() cuando se produce un error, como anotó Shane (y como está documentado en la guía de depuración de R. O cualquier otra función útil que le resulte útil ejecutar).

Y otros dos métodos de uno de los enlaces de @ Shane:

  • Envuelva una llamada de función interna con try() para devolver más información sobre ella.
  • Para * aplicar funciones, use .inform=TRUE (del paquete plyr) como una opción para el comando apply

@JoshuaUlrich también señaló una forma clara de usar las capacidades condicionales del clásico comando browser() para activar / desactivar la depuración:

  • Ponga dentro de la función que desea depurar el browser(expr=isTRUE(getOption("myDebug")))
  • Y configure la opción global por options(myDebug=TRUE)
  • Incluso podría envolver la llamada del navegador: myBrowse <- browser(expr=isTRUE(getOption("myDebug"))) y luego llamar con myBrowse() ya que usa globales.

Luego están las nuevas funciones disponibles en R 2.10:

  • findLineNum() toma un nombre de archivo fuente y un número de línea, y devuelve la función y el entorno. Esto parece ser útil cuando obtiene source() un archivo .R y devuelve un error en la línea #n, pero necesita saber qué función se encuentra en la línea #n.
  • setBreakpoint() toma un nombre de archivo fuente y un número de línea y establece un punto de interrupción allí

El paquete codetools , y particularmente su función checkUsage , puede ser particularmente útil para recoger rápidamente la syntax y los errores estilísticos que un comstackdor normalmente informaría (locales no utilizados, funciones y variables globales indefinidas, coincidencia parcial de argumentos, etc.).

setBreakpoint() es un front-end más fácil de usar para trace() . Los detalles sobre las partes internas de cómo funciona esto están disponibles en un artículo reciente de R Journal .

Si está tratando de depurar el paquete de otra persona, una vez que haya localizado el problema, puede fixInNamespace sus funciones con fixInNamespace y assignInNamespace , pero no use esto en el código de producción.

Nada de esto debería excluir las herramientas estándar de depuración de errores de R , algunas de las cuales están arriba y otras no. En particular, las herramientas de depuración post-mortem son útiles cuando tiene un grupo de código que consume mucho tiempo y que prefiere no volver a ejecutar.

Finalmente, para problemas complicados que no parecen arrojar un mensaje de error, puede usar options(error=dump.frames) como se detalla en esta pregunta: Error sin que se haya options(error=dump.frames) un error.

En algún momento, se llama a glm.fit . Eso significa que una de las funciones que llamas o una de las funciones llamadas por esas funciones es usar glm , glm.fit .

Además, como mencioné en mi comentario anterior, esa es una advertencia, no un error , lo que hace una gran diferencia. No se puede activar ninguna de las herramientas de depuración de R a partir de una advertencia (con las opciones predeterminadas antes de que alguien me diga que estoy equivocado ;-).

Si cambiamos las opciones para convertir advertencias en errores, entonces podemos comenzar a utilizar las herramientas de depuración de R. Desde ?options tenemos:

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

Entonces si corres

 options(warn = 2) 

luego ejecuta tu código, R lanzará un error. En ese punto, podrías correr

 traceback() 

para ver la stack de llamadas. Aquí hay un ejemplo.

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

Aquí puede ignorar los cuadros marcados con 4: y más. Vemos que foo llamó a la bar y esa bar generó la advertencia. Eso debería mostrarle qué funciones llamaban glm.fit .

Si ahora quiere depurar esto, podemos recurrir a otra opción para decirle a R que ingrese el depurador cuando encuentre un error, y como hemos hecho errores de advertencias, obtendremos un depurador cuando se active la advertencia original. Para eso debes correr:

 options(error = recover) 

Aquí hay un ejemplo:

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

Luego puede ingresar a cualquiera de esos cuadros para ver qué sucedía cuando se lanzó la advertencia.

Para restablecer las opciones anteriores a sus valores predeterminados, ingrese

 options(error = NULL, warn = 0) 

En cuanto a la advertencia específica que cita, es muy probable que necesite permitir más iteraciones en el código. Una vez que haya descubierto lo que está llamando a glm.fit , glm.fit cómo pasarle el argumento de control usando glm.control – vea ?glm.control .

Así que browser() , traceback() y debug() entran en una barra, pero trace() espera afuera y mantiene el motor en funcionamiento.

Al insertar el browser en alguna parte de su función, la ejecución se detendrá y esperará su entrada. Puede avanzar utilizando n (o Enter ), ejecutar todo el fragmento (iteración) con c , finalizar el bucle / función actual con f , o salir con Q ; ver ?browser .

Con la debug , obtienes el mismo efecto que con el navegador, pero esto detiene la ejecución de una función desde el principio. Se aplican los mismos atajos. Esta función estará en un modo de “depuración” hasta que la desactives usando undebug (es decir, después de la debug(foo) , ejecutando la función foo ingresará al modo “depuración” cada vez hasta que undebug(foo) ).

Una alternativa más transitoria es la debugonce , que eliminará el modo “depuración” de la función después de la próxima vez que se evalúe.

traceback le dará el flujo de ejecución de funciones hasta donde algo salió mal (un error real).

Puede insertar bits de código (es decir, funciones personalizadas) en funciones que utilizan trace , por ejemplo, browser . Esto es útil para las funciones de los paquetes y eres demasiado flojo para obtener el código fuente bien doblado.

Mi estrategia general se ve así:

  1. Ejecute traceback() para ver si hay problemas obvios
  2. Establecer options(warn=2) para tratar advertencias como errores
  3. Establecer options(error=recover) para entrar en la stack de llamadas por error

Después de seguir todos los pasos sugeridos, aprendí que configurar .verbose = TRUE en foreach() también me da toneladas de información útil. En particular, foreach(.verbose=TRUE) muestra exactamente dónde se produce un error dentro del bucle foreach, mientras que traceback() no se ve dentro del bucle foreach.

El depurador de Mark Bravington, que está disponible como debug del paquete en CRAN, es muy bueno y sencillo.

 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 

El código aparece en una ventana Tk resaltada para que pueda ver lo que está pasando y, por supuesto, puede llamar a otro mtrace() mientras está en una función diferente.

HTH

Me gusta la respuesta de Gavin: no sabía sobre opciones (error = recuperar). También me gusta utilizar el paquete ‘debug’ que proporciona una forma visual de recorrer tu código.

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

En este punto, abre una ventana de depuración separada que muestra su función, con una línea amarilla que muestra dónde se encuentra en el código. En la ventana principal, el código ingresa al modo de depuración, y puede seguir presionando enter para recorrer el código (y también hay otros comandos), y examinar los valores de las variables, etc. La línea amarilla en la ventana de depuración se sigue moviendo para mostrar dónde estás en el código Cuando haya terminado con la depuración, puede desactivar el rastreo con:

 mtrace.off() 

De acuerdo con la respuesta que recibí aquí , definitivamente debería verificar la configuración de options(error=recover) . Cuando esto se establece, al encontrar un error, verá texto en la consola similar al siguiente (salida de traceback ):

 > source() 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() 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: 

En ese momento puede elegir qué “marco” debe ingresar. Cuando haga una selección, se lo colocará en el modo de browser() :

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

Y puede examinar el entorno tal como estaba en el momento del error. Cuando termines, escribe c para volver al menú de selección de fotogtwigs. Cuando hayas terminado, como te dice, escribe 0 para salir.

Di esta respuesta a una pregunta más reciente, pero la agrego aquí para completarla.

Personalmente, tiendo a no usar funciones para depurar. A menudo encuentro que esto causa tantos problemas como lo resuelve. Además, viniendo de un fondo de Matlab, me gusta poder hacer esto en un entorno de desarrollo integrado (IDE) en lugar de hacer esto en el código. El uso de un IDE mantiene su código limpio y simple.

Para R, uso un IDE llamado “RStudio” ( http://www.rstudio.com ), que está disponible para Windows, Mac y Linux, y es bastante fácil de usar.

Las versiones de Rstudio desde aproximadamente octubre de 2013 (0.98ish?) Tienen la capacidad de agregar puntos de corte en scripts y funciones: para hacer esto, simplemente haga clic en el margen izquierdo del archivo para agregar un punto de corte. Puede establecer un punto de interrupción y luego pasar desde ese punto. También tiene acceso a todos los datos en ese entorno, por lo que puede probar los comandos.

Ver http://www.rstudio.com/ide/docs/debugging/overview para más detalles. Si ya tiene instalado Rstudio, es posible que deba actualizar: esta es una función relativamente nueva (finales de 2013).

También puede encontrar otros IDEs que tengan una funcionalidad similar.

Es cierto que, si se trata de una función incorporada, es posible que deba recurrir a algunas de las sugerencias que otras personas hicieron en esta discusión. Pero, si es su propio código el que debe solucionarse, una solución basada en IDE puede ser justo lo que necesita.

Para depurar métodos de clase de referencia sin referencia de instancia

 ClassName$trace(methodName, browser) 

Estoy empezando a pensar que no imprimir el número de línea de error, un requisito básico, POR DEFAILT, es una especie de broma en R / Rstudio . El único método confiable que he encontrado para encontrar dónde ocurrió un error es realizar el esfuerzo adicional de retener traceback () y ver la línea superior.