Suggestions générales pour le débogage en R

J’ai une erreur en utilisant une fonction R que j’ai écrite:

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

Ce que j’ai fait:

  1. Passer à travers la fonction
  2. L’ajout de print pour savoir à quelle ligne se produit l’erreur suggère deux fonctions qui ne devraient pas utiliser glm.fit . Ils sont window() et save() .

Mes approches générales incluent l’ajout de commandes d’ print et d’ stop , et l’ stop par ligne jusqu’à ce que je puisse localiser l’exception.

Cependant, pour moi, il n’est pas clair d’utiliser les techniques pour lesquelles cette erreur provient du code. Je ne suis même pas certain quelles fonctions du code dépendent de glm.fit . Comment faire pour diagnostiquer ce problème?

Je dirais que le débogage est une forme d’art, il n’y a donc pas de solution miracle. Il existe de bonnes stratégies de débogage dans toutes les langues, et elles s’appliquent également ici (par exemple, lisez cet article intéressant ). Par exemple, la première chose à faire est de reproduire le problème … si vous ne pouvez pas faire cela, vous devez obtenir plus d’informations (par exemple, avec la journalisation). Une fois que vous pouvez le reproduire, vous devez le réduire à la source.

Plutôt qu’un “truc”, je dirais que j’ai une routine de débogage préférée:

  1. Lorsqu’une erreur survient, la première chose que je fais habituellement est d’examiner la trace de la stack en appelant traceback() : cela vous montre où l’erreur est survenue, ce qui est particulièrement utile si vous avez plusieurs fonctions nestedes.
  2. Ensuite, je définirai les options(error=recover) ; Cela commute immédiatement en mode navigateur où l’erreur se produit, vous pouvez donc parcourir l’espace de travail à partir de là.
  3. Si je n’ai toujours pas assez d’informations, j’utilise généralement la fonction debug() et parcoure le script ligne par ligne.

La meilleure nouvelle astuce dans R 2.10 (lorsque vous travaillez avec des fichiers de script) consiste à utiliser les fonctions findLineNum() et setBreakpoint() .

En guise de dernier commentaire: en fonction de l’erreur, il est également très utile de définir des instructions try() ou tryCatch() autour des appels de fonctions externes (en particulier lorsqu’il s’agit de classes S4). Cela fournira parfois encore plus d’informations et vous permettra également de mieux contrôler la manière dont les erreurs sont traitées au moment de l’exécution.

Ces questions connexes ont beaucoup de suggestions:

  • Outils de débogage pour le langage R
  • Débogage des appels lapply / sapply
  • Obtenir l’état des variables après une erreur dans R
  • R numéros de ligne de script à l’erreur?

La meilleure solution que j’ai jamais vue est:

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

Quelqu’un est-il d’accord / pas d’accord?

Comme je l’ai souligné dans une autre question , Rprof() et summaryRprof() sont de bons outils pour trouver les parties lentes de votre programme susceptibles d’être accélérées ou déplacées vers une implémentation C / C ++. Cela s’applique probablement plus si vous faites du travail de simulation ou d’autres activités à forte intensité de calcul ou de données. Le package profr peut aider à visualiser les résultats.

Je suis sur un coup de pied de débogage, donc une autre suggestion d’ un autre sujet :

  • Définir les options(warn=2) pour traiter les avertissements comme des erreurs

Vous pouvez également utiliser les options pour vous laisser tomber dans le feu de l’action lorsqu’une erreur ou un avertissement se produit, en utilisant la fonction de débogage de votre choix. Par exemple:

  • Définissez les options(error=recover) pour exécuter recover() lorsqu’une erreur se produit, comme Shane l’a noté (et comme cela est documenté dans le guide de débogage R. Ou toute autre fonction pratique que vous jugeriez utile d’exécuter).

Et deux autres méthodes de l’un des liens de @ Shane:

  • Enveloppez un appel de fonction interne avec try() pour renvoyer plus d’informations.
  • Pour les fonctions * apply, utilisez .inform=TRUE (à partir du package plyr) en tant qu’option pour la commande apply

@JoshuaUlrich a également mis en avant un moyen pratique d’utiliser les capacités conditionnelles de la commande classique browser() pour activer / désactiver le débogage:

  • Mettre à l’intérieur de la fonction, vous pouvez vouloir déboguer le browser(expr=isTRUE(getOption("myDebug")))
  • Et définissez l’option globale par options(myDebug=TRUE)
  • Vous pouvez même envelopper l’appel du navigateur: myBrowse < - browser(expr=isTRUE(getOption("myDebug"))) puis appeler avec myBrowse() car il utilise des globals.

Ensuite, il y a les nouvelles fonctions disponibles dans R 2.10:

  • findLineNum() prend un nom de fichier source et un numéro de ligne et renvoie la fonction et l'environnement. Cela semble être utile lorsque vous source() un fichier .R et qu'il renvoie une erreur à la ligne #n, mais vous devez savoir quelle fonction se trouve à la ligne #n.
  • setBreakpoint() prend un nom de fichier source et un numéro de ligne et définit un point d'arrêt

Le paquet codetools , et en particulier sa fonction checkUsage , peuvent être particulièrement utiles pour checkUsage rapidement les erreurs de syntaxe et de style généralement signalées par un compilateur (sections locales non utilisées, fonctions et variables globales indéfinies, correspondance partielle des arguments, etc.).

setBreakpoint() est une interface utilisateur plus conviviale à trace() . Des détails sur le fonctionnement de cette méthode sont disponibles dans un article récent du R Journal .

Si vous essayez de déboguer le package de quelqu'un d'autre, une fois que vous avez localisé le problème, vous pouvez fixInNamespace leurs fonctions avec fixInNamespace et assignInNamespace , mais ne les utilisez pas dans le code de production.

Rien de tout cela ne devrait exclure les outils de débogage R standard éprouvés, dont certains sont au-dessus et d'autres non. En particulier, les outils de débogage post-mortem sont pratiques lorsque vous avez un tas de code fastidieux que vous préférez ne pas ré-exécuter.

Enfin, pour les problèmes délicats qui ne semblent pas options(error=dump.frames) message d'erreur, vous pouvez utiliser les options(error=dump.frames) détaillées dans cette question: Erreur sans erreur

À un moment donné, glm.fit est appelé. Cela signifie que l’une des fonctions que vous appelez ou l’une des fonctions appelées par ces fonctions utilise soit glm , glm.fit .

De plus, comme je le mentionne dans mon commentaire ci-dessus, il s’agit d’un avertissement et non d’une erreur , ce qui fait une grande différence. Vous ne pouvez déclencher aucun des outils de débogage de R à partir d’un avertissement (avec les options par défaut avant que quelqu’un ne me dise que je me trompe ;-).

Si nous modifions les options pour transformer les avertissements en erreurs, nous pouvons commencer à utiliser les outils de débogage de R. De ?options nous avons:

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

Donc, si vous courez

 options(warn = 2) 

puis lancez votre code, R lancera une erreur. À quel point, vous pourriez courir

 traceback() 

pour voir la stack d’appels. Voici un exemple.

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

Ici, vous pouvez ignorer les images marquées 4: et supérieures. Nous voyons ce foo appelé bar et cette bar généré l’avertissement. Cela devrait vous montrer quelles fonctions appelaient glm.fit .

Si vous voulez maintenant déboguer ceci, nous pouvons nous tourner vers une autre option pour indiquer à R d’entrer le débogueur lorsqu’il rencontre une erreur, et comme nous avons fait des erreurs d’avertissement, nous obtiendrons un débogueur lorsque l’avertissement d’origine sera déclenché. Pour cela vous devriez courir:

 options(error = recover) 

Voici un exemple:

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

Vous pouvez ensuite entrer dans l’une de ces images pour voir ce qui se passait lorsque l’avertissement a été lancé.

Pour réinitialiser les options ci-dessus à leur valeur par défaut, entrez

 options(error = NULL, warn = 0) 

En ce qui concerne l’avertissement spécifique que vous citez, il est fort probable que vous ayez besoin de plus d’itérations dans le code. Une fois que vous avez trouvé ce qui appelle glm.fit , déterminez comment lui transmettre l’argument de control aide de glm.control – see ?glm.control .

Ainsi, browser() , traceback() et debug() entrent dans une barre, mais trace() attend à l’extérieur et maintient le moteur en marche.

En insérant un browser quelque part dans votre fonction, l’exécution s’arrêtera et attendra votre saisie. Vous pouvez avancer avec n (ou Enter ), exécuter l’intégralité du bloc (itération) avec c , terminer la boucle / fonction en cours avec f ou quitter avec Q ; voir ?browser .

Avec debug , vous obtenez le même effet qu’avec le navigateur, mais cela arrête l’exécution d’une fonction au début. Les mêmes raccourcis s’appliquent. Cette fonction sera en mode “debug” jusqu’à ce que vous la désactiviez avec undebug (c’est-à-dire, après le debug(foo) , l’exécution de la fonction foo passera en mode “debug” jusqu’à ce que vous undebug(foo) ).

debugonce est une alternative plus transitoire, qui supprime le mode “debug” de la fonction après sa prochaine évaluation.

traceback vous donnera le stream d’exécution des fonctions jusqu’à ce que quelque chose se passe mal (une erreur réelle).

Vous pouvez insérer des bits de code (c.-à-d. Des fonctions personnalisées) dans des fonctions utilisant la trace , par exemple le browser . Ceci est utile pour les fonctions des paquets et vous êtes trop paresseux pour obtenir le code source bien plié.

Ma stratégie générale ressemble à:

  1. Exécutez traceback() pour voir les problèmes évidents
  2. Définir les options(warn=2) pour traiter les avertissements comme des erreurs
  3. Définir les options(error=recover) pour entrer dans la stack d’appels en cas d’erreur

Après avoir parcouru toutes les étapes suggérées ici, je viens d’apprendre que la définition de .verbose = TRUE dans foreach() me donne également des tonnes d’informations utiles. En particulier, foreach(.verbose=TRUE) montre exactement où une erreur se produit dans la boucle foreach, alors que traceback() ne regarde pas dans la boucle foreach.

Le débogueur de Mark Bravington, qui est disponible pour debug le paquet sur CRAN, est très bon et très simple.

 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 

Le code apparaît dans une fenêtre Tk en surbrillance pour que vous puissiez voir ce qui se passe et, bien sûr, vous pouvez appeler un autre mtrace() dans une fonction différente.

HTH

J’aime la réponse de Gavin: je ne connaissais pas les options (erreur = récupérer). J’aime aussi utiliser le paquetage ‘debug’ qui donne un moyen visuel de parcourir votre code.

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

À ce stade, il ouvre une fenêtre de débogage séparée montrant votre fonction, avec une ligne jaune indiquant où vous êtes dans le code. Dans la fenêtre principale, le code entre en mode débogage et vous pouvez continuer à appuyer sur Entrée pour parcourir le code (et il y a d’autres commandes), et examiner les valeurs des variables. La ligne jaune dans la fenêtre de débogage vous êtes dans le code. Une fois le débogage terminé, vous pouvez désactiver le suivi avec:

 mtrace.off() 

Sur la base de la réponse que j’ai reçue ici , vous devez absolument vérifier le paramètre options(error=recover) . Lorsque ceci est défini, lorsque vous rencontrez une erreur, vous verrez du texte sur la console similaire à celui-ci (sortie 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: 

À quel point vous pouvez choisir quelle “image” entrer. Lorsque vous effectuez une sélection, vous serez placé dans le mode browser() :

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

Et vous pouvez examiner l’environnement tel qu’il était au moment de l’erreur. Lorsque vous avez terminé, tapez c pour revenir au menu de sélection des images. Lorsque vous avez terminé, comme cela vous dit, tapez 0 pour quitter.

J’ai donné cette réponse à une question plus récente, mais je l’ajoute ici pour être complet.

Personnellement, j’ai tendance à ne pas utiliser les fonctions pour déboguer. Je trouve souvent que cela cause autant de problèmes que cela résout. Aussi, venant d’un fond Matlab, j’aime pouvoir le faire dans un environnement de développement intégré (IDE) plutôt que de le faire dans le code. L’utilisation d’un IDE permet de garder votre code propre et simple.

Pour R, j’utilise un IDE appelé “RStudio” ( http://www.rstudio.com ), qui est disponible pour Windows, Mac et Linux et est assez facile à utiliser.

Les versions de Rstudio depuis octobre 2013 (0.98ish?) Ont la capacité d’append des points d’arrêt dans les scripts et les fonctions: pour ce faire, il suffit de cliquer sur la marge gauche du fichier pour append un point d’arrêt. Vous pouvez définir un point d’arrêt, puis passer à l’étape suivante. Vous avez également access à toutes les données de cet environnement, vous pouvez donc essayer les commandes.

Voir http://www.rstudio.com/ide/docs/debugging/overview pour plus de détails. Si vous avez déjà installé Rstudio, vous devrez peut-être effectuer une mise à niveau – il s’agit d’une fonctionnalité relativement nouvelle (fin 2013).

Vous pouvez également trouver d’autres IDE ayant des fonctionnalités similaires.

Certes, s’il s’agit d’une fonction intégrée, vous devrez peut-être recourir à certaines des suggestions faites par d’autres personnes au cours de cette discussion. Mais si votre propre code doit être corrigé, une solution basée sur l’EDI peut être exactement ce dont vous avez besoin.

Pour déboguer les méthodes de la classe de référence sans référence à l’instance

 ClassName$trace(methodName, browser) 

Je commence à penser que ne pas imprimer le numéro de ligne d’erreur – une exigence de base – BY DEFAILT- est une sorte de blague dans R / Rstudio . La seule méthode fiable que j’ai trouvée pour trouver une erreur est de faire un effort supplémentaire de calloing traceback () et de voir la première ligne.