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

j'obtiens une erreur en utilisant une fonction R que j'ai écrit:

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

Ce que j'ai fait:

  1. Étape par le biais de la fonction
  2. Ajout d'impression pour savoir à 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 comprennent l'ajout Commandes print et stop , et passage d'une fonction ligne par ligne jusqu'à ce que je trouve l'exception.

cependant, il ne me semble pas évident d'utiliser ces techniques pour déterminer d'où vient cette erreur dans le code. Je ne sais même pas quelles fonctions du code dépendent de glm.fit . Comment dois-je aller sur le diagnostic de ce problème?

115
demandé sur David LeBauer 2010-12-14 21:07:16

13 réponses

je dirais que le débogage est une forme d'art, donc il n'y a pas de solution claire. Il existe de bonnes stratégies pour déboguer dans n'importe quelle langue, et elles s'appliquent ici aussi (par exemple lire cet article agréable ). Par exemple, la première chose est de reproduire le problème ...si vous ne pouvez pas faire cela, alors vous devez obtenir plus d'informations (par exemple avec la journalisation). Une fois que vous pouvez le reproduire, vous devez réduire jusqu'à 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 se produit, la première chose que je fais habituellement est de regarder la trace de la pile en appelant traceback() : cela vous montre où l'erreur s'est produite, ce qui est particulièrement utile si vous avez plusieurs fonctions imbriquées.
  2. ensuite, je vais mettre options(error=recover) ; cela passe immédiatement en mode navigateur où l'erreur se produit, donc vous on peut parcourir l'espace de travail à partir de là.
  3. si je n'ai toujours pas assez d'informations, j'utilise habituellement la fonction debug() et je passe le script ligne par ligne.

la meilleure nouvelle astuce dans R 2.10 (quand on travaille avec des fichiers de script) est d'utiliser les fonctions findLineNum() et setBreakpoint() .

comme commentaire final: selon l'erreur, il est également très utile de définir try() ou tryCatch() des instructions concernant les appels de fonction externes (en particulier lorsqu'il s'agit de classes S4). Cela fournira parfois encore plus d'informations, et vous donnera également plus de contrôle sur la façon dont les erreurs sont traitées au moment de l'exécution.

ces questions connexes ont beaucoup de suggestions:

161
répondu Shane 2017-05-23 11:55:19

La meilleure soluce que j'ai vu jusqu'à présent est:

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

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

37
répondu Christopher DuBois 2009-07-23 03:48:02

comme cela m'a été signalé dans une autre question , Rprof() et summaryRprof() sont de bons outils pour trouver les parties lentes de votre programme qui pourraient bénéficier d'accélérer ou de passer à une implémentation C/C++. Cela s'applique probablement davantage si vous faites du travail de simulation ou d'autres activités de calcul ou de données intensives. Le profr package peut aider à visualiser les résultats.

Je suis sur un coup de pied d'apprendre-sur-débogage, donc une autre suggestion de un autre fil :

  • Set options(warn=2) pour traiter les avertissements comme des erreurs

vous pouvez également utiliser options pour vous déposer directement dans le feu de l'action lorsqu'une erreur ou un avertissement se produit, en utilisant votre fonction de débogage préféré de votre choix. Par exemple:

  • Set options(error=recover) pour exécuter recover() quand une erreur se produit, comme Shane l'a noté (et comme est documenté dans le R debugging guide . Ou toute autre fonction pratique que vous jugeriez utile d'avoir exécutée.

et deux autres méthodes de l'une des de @Shane" liens :

  • envelopper un appel de fonction interne avec try() pour retourner plus d'informations sur elle.
  • pour * appliquer des fonctions, utiliser .inform=TRUE (du paquet plyr) en option de la commande apply

@JoshuaUlrich a également souligné une façon soignée d'utiliser les capacités conditionnelles de la commande classique browser() pour activer/désactiver le débogage:

  • mettez à l'intérieur de la fonction que vous pourriez vouloir déboguer browser(expr=isTRUE(getOption("myDebug")))
  • et définit l'option globale par options(myDebug=TRUE)
  • vous pourriez même envelopper l'appel de navigateur: myBrowse <- browser(expr=isTRUE(getOption("myDebug"))) et puis appeler avec myBrowse() car il utilise des globals.

puis 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 retourne la fonction et l'environnement. Cela semble être utile lorsque vous source() un .R et il retourne une erreur à la ligne #n, mais vous devez savoir quelle fonction est située à la ligne #N.
  • setBreakpoint() prend un nom de fichier source et un numéro de ligne et y fixe un point de rupture

le paquet codetools , et en particulier sa fonction checkUsage peut être particulièrement utile pour détecter rapidement les erreurs de syntaxe et de style qu'un compilateur signalerait typiquement (locaux inutilisés, fonctions et variables globales non définies, correspondance partielle des arguments, etc.).

setBreakpoint() est une partie avant plus conviviale que trace() . Des détails sur l'intérieur de la façon dont cela fonctionne sont disponibles dans un R récent article de Journal .

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

rien de cela ne devrait empêcher les standard R debugging tools , dont certains sont ci-dessus et d'autres pas. En particulier, les outils de débogage post-mortem sont pratiques lorsque vous avez un paquet de code qui prend beaucoup de temps et que vous préférez ne pas relancer.

enfin, pour les problèmes délicats qui ne semblent pas envoyer de message d'erreur, vous pouvez utiliser options(error=dump.frames) comme détaillé dans cette question: erreur sans qu'une erreur soit lancée

32
répondu Ari B. Friedman 2017-05-23 12:34:59

à 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 est d'utiliser soit glm , glm.fit .

aussi, comme je le mentionne dans mon commentaire ci-dessus, c'est un avertissement pas 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 des options par défaut avant que quelqu'un me dit que je suis mauvais.)-;

si nous changeons les options pour transformer les avertissements en erreurs, alors 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 va lancer une erreur. À ce moment, vous pourriez courir

traceback()

pour voir la pile d'appel. Ici est 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 le cadres marqués 4: et plus. Nous voyons que foo 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 nous tourner vers une autre option pour dire à R d'entrer dans le débogueur quand il rencontre une erreur, et comme nous avons fait des erreurs d'Avertissements nous obtiendrons un débogueur quand l'avertissement original est déclenché. Pour cela, vous devez exécuter:

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 alors entrer dans l'un de ces cadres pour voir ce qui se passait lorsque l'avertissement a été lancé.

pour réinitialiser les options ci-dessus à leur valeur par défaut, entrer

options(error = NULL, warn = 0)

quant à l'avertissement spécifique que vous citez, il est très probable que vous devez autoriser plus d'itérations dans le code. Une fois que vous avez trouvé ce qui s'appelle glm.fit , travailler sur la façon de passer le "1519170920 argument de" l'aide glm.control - voir ?glm.control .

27
répondu Gavin Simpson 2010-12-14 18:45:05

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

en insérant browser quelque part dans votre fonction, l'exécution s'arrêtera et attendra votre entrée. Vous pouvez aller de l'avant en utilisant n (ou entrer ), exécuter le morceau entier (itération) avec c , terminer la boucle/Fonction actuelle avec f , ou arrêter avec Q ; voir ?browser .

Avec debug , vous obtenez le même effet qu'avec le navigateur, mais cela s'arrête l'exécution d'une fonction à son début. Les mêmes raccourcis s'appliquent. Cette fonction sera en mode" debug "jusqu'à ce que vous l'arrêtiez en utilisant undebug (c'est-à-dire, après debug(foo) , la fonction foo entrera en mode" debug "à chaque fois jusqu'à ce que vous exécutiez undebug(foo) ).

A une alternative plus transitoire est debugonce , qui supprimera le mode" debug " de la fonction après la prochaine évaluation.

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

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

20
répondu Roman Luštrik 2018-09-04 02:20:25

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

  1. Exécuter traceback() pour voir regarder pour d'évidentes questions de
  2. Set options(warn=2) pour traiter les avertissements comme des erreurs
  3. Set options(error=recover) à l'étape dans la pile d'appel sur l'erreur
18
répondu Joshua Ulrich 2010-12-14 18:44:51

après avoir passé en revue toutes les étapes suggérées ici, je viens d'apprendre que le réglage .verbose = TRUE dans foreach() me donne aussi des tonnes d'informations utiles. En particulier foreach(.verbose=TRUE) montre exactement où une erreur se produit à l'intérieur de la boucle foreach, tandis que traceback() ne regarde pas à l'intérieur de la boucle foreach.

15
répondu Michael Schneider 2010-10-29 19:15:45

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 en surbrillance de Tk de sorte que vous pouvez voir ce qui se passe et, bien sûr, vous pouvez appeler un autre mtrace() alors que dans une fonction différente.

HTH

13
répondu David Lawrence Miller 2009-07-23 08:04: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 point il ouvre une fenêtre de débogage séparée montrant votre fonction, avec une ligne jaune montrant où vous êtes dans le code. Dans la fenêtre principale le code entre en mode debug, et vous pouvez continuer à frapper enter pour passer à travers le code( et il y a aussi d'autres commandes), et examiner valeurs variables, etc. La ligne jaune dans la fenêtre de débogage continue de bouger pour montrer où vous êtes dans le code. Lorsque le débogage, vous pouvez désactiver le traçage avec:

mtrace.off()
11
répondu Prasad Chalasani 2010-12-15 15:57:04

basé sur la réponse que j'ai reçue ici , vous devriez certainement vérifier le réglage options(error=recover) . Lorsque cette option est activée, en cas d'erreur, vous verrez sur la console un texte similaire à celui qui suit ( 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:

, À quel point vous pouvez choisir "image" pour entrer. Lorsque vous faites une sélection, vous serez placé 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 des cadres. Quand vous avez terminé, comme il vous dit, tapez 0 pour sortir.

5
répondu eykanal 2017-05-23 11:33:24

j'ai donné cette réponse à une question plus récente, mais je l'ajoute ici pour l'exhaustivité.

personnellement, J'ai tendance à ne pas utiliser de fonctions pour déboguer. Je trouve souvent que cela cause autant de problèmes que cela résout. En outre, venant d'un contexte Matlab, j'aime pouvoir le faire dans un environnement de développement intégré (IDE) plutôt que de le faire dans le code. Utiliser 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.

Versions de Rstudio depuis environ octobre 2013 (0,98 ish?) ont la possibilité d'ajouter des points de rupture dans les scripts et les fonctions: pour ce faire, il suffit de cliquer sur la marge gauche du fichier pour ajouter un point de rupture. Vous pouvez définir un point d'arrêt et ensuite étape à partir de ce point. Vous avez également accès à toutes les données que environnement, donc vous pouvez 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 faire une mise à niveau-il s'agit d'une fonctionnalité relativement nouvelle (fin 2013).

vous pouvez également trouver d'autres IDEs qui ont des fonctionnalités similaires.

Certes, si c'est une fonction intégrée vous pouvez avoir recours à certaines des suggestions faites par d'autres des gens dans cette discussion. Mais, si c'est votre propre code qui doit être corrigé, une solution basée sur L'IDE pourrait être exactement ce dont vous avez besoin.

4
répondu Andy Clifton 2017-05-23 11:33:24

debug Classe de Référence méthodes sans instance de référence

ClassName$trace(methodName, browser)
1
répondu shiva 2017-04-26 11:57:17

je commence à penser que ne pas imprimer le numéro de ligne d'erreur - une exigence la plus élémentaire - par défaut - est une sorte de blague dans R/Rstudio . La seule méthode fiable que j'ai trouvé pour trouver où une erreur s'est produite est de faire l'effort supplémentaire de Callo traceback() et voir la ligne supérieure.

0
répondu user9669128 2018-04-19 10:47:08