web-dev-qa-db-fra.com

Comment demander une sortie anticipée lors du tricotage d'un document Rmd?

Disons que vous avez un document de démarque R qui ne sera pas rendu correctement.

Je sais que vous pouvez définir l'option de bloc knitrerror sur TRUE pour demander que l'évaluation se poursuive, même en présence d'erreurs. Vous pouvez le faire pour un morceau individuel via error = TRUE Ou de manière plus globale via knitr::opts_chunk$set(error = TRUE).

Mais parfois, il y a des erreurs qui sont toujours fatales au processus de tricotage. J'ai récemment rencontré deux exemples: essayer de unlink() le répertoire de travail actuel (oups!) Et appeler rstudioapi::getVersion() à partir du code R en ligne lorsque RStudio n'est pas disponible. Existe-t-il une description générale de ces types d'erreurs, c'est-à-dire celles qui sont hors de portée de error = TRUE? Existe-t-il un moyen de tolérer les erreurs dans le code R en ligne vs dans les morceaux?

De plus, existe-t-il des moyens plus officiels d'arrêter le tricot tôt ou d'automatiser le débogage dans cette situation?

41
jennybryan

Pour quitter tôt le processus de tricotage, vous pouvez utiliser la fonction knitr::knit_exit() n'importe où dans le document source (dans un bloc de code ou une expression en ligne). Une fois que knit_exit() est appelée, knitr ignorera tout le reste du document et notera les résultats qu'il a collectés jusqu'à présent.

Il n'y a aucun moyen de tolérer les erreurs dans le code R en ligne pour le moment. Vous devez vous assurer que le code R en ligne s'exécute toujours sans erreur1. Si des erreurs se produisent, vous devriez voir la plage de lignes qui a provoqué l'erreur à partir du journal knitr dans la console, sous la forme Quitting from lines x1-x2 (filename.Rmd). Ensuite, vous pouvez aller dans le fichier filename.Rmd Et voir ce qui ne va pas avec les lignes de x1 À x2. La même chose s'applique aux blocs de code avec l'option de bloc error = FALSE.

Au-delà des types d'erreurs mentionnés ci-dessus, il peut être difficile de trouver la source du problème. Par exemple, lorsque vous involontairement unlink() le répertoire en cours, il ne doit pas arrêter le processus de tricotage, car unlink() a quand même réussi. Vous pouvez rencontrer des problèmes après le processus de tricotage, par exemple, LaTeX/HTML ne peut pas trouver les fichiers de figures de sortie. Dans ce cas, vous pouvez essayer d'appliquer knit_exit() à tous les morceaux de code du document un par un. Une façon d'y parvenir consiste à configurer un hook de segment pour exécuter knit_exit() après un certain segment. Voici un exemple d'utilisation de la recherche linéaire (vous pouvez l'améliorer en utilisant la bissection à la place):

#' Render an input document chunk by chunk until an error occurs
#' 
#' @param input the input filename (an Rmd file in this example)
#' @param compile a function to compile the input file, e.g. knitr::knit, or
#'   rmarkdown::render
knit_debug = function(input, compile = knitr::knit) {
  library(knitr)
  lines = readLines(input)
  chunk = grep(all_patterns$md$chunk.begin, lines)  # line number of chunk headers

  knit_hooks$set(debug = function(before) {
    if (!before) {
      chunk_current <<- chunk_current + 1
      if (chunk_current >= chunk_num) knit_exit()
    }
  })

  opts_chunk$set(debug = TRUE)

  # try to exit after the i-th chunk and see which chunk introduced the error
  for (chunk_num in seq_along(chunk)) {
    chunk_current = 0  # a chunk counter, incremented after each chunk
    res = try(compile(input))
    if (inherits(res, 'try-error')) {
      message('The first error came from line ', chunk[chunk_num])
      break
    }
  }
}

  1. C'est par conception. Je pense que c'est une bonne idée d'avoir error = TRUE Pour les morceaux de code, car parfois nous voulons montrer des erreurs, par exemple, à des fins d'enseignement. Cependant, si j'autorise également les erreurs pour le code en ligne, les auteurs peuvent ne pas reconnaître les erreurs fatales dans le code en ligne. Le code en ligne est normalement utilisé pour incorporer les valeurs en ligne, et je ne pense pas que cela ait beaucoup de sens si une valeur en ligne est une erreur. Imaginez une phrase dans un rapport comme The P-value of my test is ERROR, Et si knitr n'a pas signalé l'erreur, il faudra que les auteurs lisent très attentivement la sortie du rapport pour repérer ce problème. Je pense que c'est une mauvaise idée de devoir compter sur les yeux humains pour trouver de telles erreurs.
36
Yihui Xie

À mon humble avis, la difficulté à déboguer un document Rmd est un avertissement que quelque chose ne va pas. J'ai une règle d'or: faites le gros du travail extérieur le Rmd. Faites le rendu à l'intérieur du Rmd, et seulement rendu. Cela rend le code Rmd simple.

Mes gros programmes R ressemblent à ceci.

data <- loadData()
analytics <- doAnalytics(data)
rmarkdown::render("theDoc.Rmd", envir=analytics)

(Ici, doAnalytics renvoie une liste ou un environnement. Cette liste ou cet environnement est transmis au document Rmd via le paramètre envir, rendant les résultats des calculs analytiques disponibles dans le document .)

La fonction doAnalytics effectue les calculs compliqués. Je peux le déboguer à l'aide des outils habituels et je peux facilement vérifier sa sortie. Au moment où j'appelle rmarkdown :: render, je sais que le matériel dur fonctionne correctement. Le code Rmd est simplement "imprimer ceci" et "formater cela", facile à déboguer.

Cette répartition des responsabilités m'a bien servi et je peux la recommander. Surtout par rapport à la tâche hallucinante de déboguer des calculs compliqués enfouis dans un document rendu dynamiquement.

9
pteetor