web-dev-qa-db-fra.com

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

Je reçois 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. Parcourez la fonction
  2. Ajouter print pour savoir sur quelle ligne l’erreur se produit 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 print et stop et l’exploration ligne par ligne d’une fonction jusqu’à ce que je puisse localiser l’exception.

Cependant, l'utilisation de ces techniques pour expliquer l'origine de cette erreur dans le code n'est pas claire. Je ne suis même pas certain que les fonctions dans le code dépendent de glm.fit. Comment puis-je diagnostiquer ce problème?

117
David LeBauer

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 ici aussi (par exemple, lisez cet article de Nice ). Par exemple, la première chose à faire est de reproduire le problème ... si vous ne pouvez pas le faire, vous devez obtenir plus d'informations (par exemple, en vous connectant). 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 favorite:

  1. Quand une erreur se produit, la première chose que je fais habituellement est de regarder la trace de la pile en appelant traceback(): elle vous indique où l’erreur est survenue, ce qui est particulièrement utile si vous avez plusieurs fonctions imbriquées.
  2. Ensuite, je vais définir options(error=recover); cela bascule immédiatement en mode navigateur où l'erreur se produit, vous permettant ainsi de 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 parcourt le script ligne par ligne. 

Le meilleur nouveau truc dans R 2.10 (lorsque vous travaillez avec des fichiers de script) est d'utiliser les fonctions findLineNum() et setBreakpoint()

Dernier commentaire: en fonction de l'erreur, il est également très utile de définir des instructions try() ou tryCatch() autour d'appels de fonctions externes (en particulier pour les classes S4). Cela fournira parfois même plus d'informations, et vous donnera également plus de contrôle sur la manière dont les erreurs sont gérées au moment de l'exécution.

Ces questions connexes ont beaucoup de suggestions:

164
Shane

La meilleure solution que j'ai vue jusqu'à présent est:

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

Quelqu'un est d'accord/pas d'accord?

37
Christopher DuBois

Comme il m’a été signalé dans autre question , Rprof() et summaryRprof() sont de bons outils pour trouver les parties lentes de votre programme qui pourraient bénéficier d’une accélération ou d’une transition vers une implémentation C/C++. Cela s'applique probablement davantage si vous effectuez des travaux de simulation ou d'autres activités gourmandes en ressources informatiques ou informatiques. Le profr package peut aider à visualiser les résultats.

Je suis sur le coup d'apprendre quelque chose sur le débogage, donc une autre suggestion de un autre fil :

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

Vous pouvez également utiliser options pour vous plonger dans le feu de l'action lorsqu'une erreur ou un avertissement se produit, à l'aide de la fonction de débogage de votre choix. Par exemple:

  • Configurez options(error=recover) pour exécuter recover() lorsqu'une erreur se produit, comme Shane l'a noté (et comme indiqué dans le guide de débogage R) . Ou toute autre fonction utile que vous jugeriez utile d'exécuter.

Et deux autres méthodes de l'une des liens } de @ Shane:

  • Emballez un appel de fonction interne avec try() pour renvoyer plus d’informations à son sujet.
  • Pour * apply, utilisez .inform=TRUE (du paquet plyr) comme option de la commande apply

@JoshuaUlrich a également fait remarquer une manière élégante d'utiliser les capacités conditionnelles de la commande browser() classique pour activer/désactiver le débogage: 

  • Mettez à l'intérieur de la fonction que vous voudrez peut-être déboguer 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() puisqu'il utilise des globales.

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 y définit un point d'arrêt 

Le package codetools , et en particulier sa fonction checkUsage, peut être particulièrement utile pour détecter rapidement les erreurs de syntaxe et de style que le compilateur devrait généralement signaler (locals inutilisés, fonctions et variables globales non définies, correspondance partielle d'arguments, etc.). ).

setBreakpoint() est un frontal plus convivial pour trace(). Des détails sur le fonctionnement interne de ce processus sont disponibles dans un article récent du journal R .

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

Rien de tout cela ne devrait exclure les outils de débogage R standard qui ont fait leurs preuves, dont certains sont au-dessus et d'autres pas. En particulier, les outils de débogage post-mortem sont pratiques lorsque vous avez une pile de code qui prend du temps et que vous préférez ne pas réexécuter.

Enfin, pour les problèmes complexes qui ne semblent pas générer de message d'erreur, vous pouvez utiliser options(error=dump.frames) comme indiqué dans cette question: Erreur sans qu'une erreur ne soit générée

32
Ari B. Friedman

glm.fit est appelé à un moment donné. 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.

En outre, comme je l’ai mentionné dans mon commentaire ci-dessus, c’est un avertissement pas une erreur , ce qui fait toute la différence. Vous ne pouvez déclencher aucun des outils de débogage de R par un avertissement (avec les options par défaut avant que quelqu'un ne me dise que je me trompe ;-).

Si nous changeons les options pour transformer les avertissements en erreurs, nous pourrons 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 tu cours

options(warn = 2)

puis lancez votre code, R va lancer une erreur. A quel point, vous pourriez courir

traceback()

pour voir la pile 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 cadres marqués 4: et plus. Nous voyons que foo a appelé bar et que bar a généré l'avertissement. Cela devrait vous montrer quelles fonctions appelaient glm.fit.

Si vous voulez maintenant déboguer ceci, nous pouvons utiliser une autre option pour demander à R d'entrer dans le débogueur lorsqu'il rencontre une erreur. Comme nous avons commis des erreurs d'avertissement, nous aurons un débogueur lorsque l'avertissement d'origine est déclenché. Pour cela, vous devriez lancer:

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 n'importe lequel de ces cadres pour voir ce qui se passait lorsque l'avertissement a été lancé.

Pour réinitialiser les options ci-dessus à leurs valeurs 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 deviez autoriser davantage d'itérations dans le code. Une fois que vous avez découvert l'appel de glm.fit, déterminez comment passer l'argument control à l'aide de glm.control - voir ?glm.control.

27
Gavin Simpson

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

En insérant browser quelque part dans votre fonction, l'exécution sera interrompue et attendra votre entrée. Vous pouvez avancer en utilisant n (ou Enter), lancez le morceau entier (itération) avec c, terminez la boucle/fonction en cours avec fou 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 à son début. Les mêmes raccourcis s'appliquent. Cette fonction sera en mode "débogage" jusqu'à ce que vous la désactiviez avec undebug (c'est-à-dire qu'après debug(foo), l'exécution de la fonction foo entrera en mode "debug" jusqu'à ce que vous exécutiez undebug(foo)).

Une alternative plus transitoire est debugonce, qui supprimera le mode "débogage" de la fonction lors de sa prochaine évaluation.

traceback vous donnera le flux d'exécution des fonctions jusqu'à l'endroit où quelque chose s'est mal passé (une erreur réelle).

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

20
Roman Luštrik

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

  1. Exécutez traceback() pour rechercher des problèmes évidents
  2. Définissez options(warn=2) pour traiter les avertissements comme des erreurs
  3. Configurez options(error=recover) pour entrer dans la pile d'appels en cas d'erreur
18
Joshua Ulrich

Après avoir suivi toutes les étapes suggérées ici, je viens d’apprendre que le réglage de .verbose = TRUE dans foreach() me donne également une tonne d’informations utiles. En particulier, foreach(.verbose=TRUE) indique exactement où une erreur se produit dans la boucle foreach, alors que traceback() ne regarde pas à l'intérieur de la boucle foreach.

15
Michael Schneider

Le débogueur de Mark Bravington, qui est disponible sous le paquet debug sur CRAN, est très bon et assez 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 afin que vous puissiez voir ce qui se passe et, bien sûr, vous pouvez appeler une autre mtrace() dans une fonction différente.

HTH

13

J'aime la réponse de Gavin: je ne connaissais pas les options (erreur = récupérer). J'aime aussi utiliser le paquet '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 affichant votre fonction, avec une ligne jaune indiquant votre position dans le code. Dans la fenêtre principale, le code entre en mode débogage et vous pouvez appuyer sur entrée pour faire défiler le code (et d'autres commandes également), et examiner les valeurs de variables, etc. La ligne jaune dans la fenêtre de débogage continue de défiler vous êtes dans le code. Une fois le débogage terminé, vous pouvez désactiver le traçage avec:

mtrace.off()
11
Prasad Chalasani

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 cette option est définie, en cas d'erreur, le texte affiché sur la console est similaire à ce qui suit (sortie 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:

A quel point vous pouvez choisir quel "cadre" entrer. Lorsque vous effectuez une sélection, vous passez en 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 vous ramener au menu de sélection de cadre. Lorsque vous avez terminé, comme indiqué, tapez 0 pour quitter.

5
eykanal

J'ai donné cette réponse à une question plus récente, mais l'ajoute ici par souci d'exhaustivité.

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

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

Les versions de Rstudio depuis octobre 2013 environ (0.98?) Permettent d'ajouter des points d'arrêt dans les scripts et les fonctions: il suffit pour cela de cliquer sur la marge de gauche du fichier pour ajouter un point d'arrêt. Vous pouvez définir un point d'arrêt, puis passer à partir de ce point. Vous avez également accès à toutes les données de cet environnement, vous pouvez donc essayer des 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 mettre à 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. Toutefois, si votre propre code doit être corrigé, une solution basée sur l'EDI est peut-être ce dont vous avez besoin.

4
Andy Clifton

Pour déboguer Classe de référence méthodes sans référence d'instance 

ClassName$trace(methodName, browser)
1
shiva

Je commence à penser que ne pas imprimer le numéro de ligne d'erreur - une exigence fondamentale - 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 l'effort supplémentaire de calloing traceback () et de voir la ligne du haut.

0
user9669128