Pour des raisons de reproduction de la science, il est important de conserver une trace de tout ce que l'on fait sur son ordinateur. Pour cela, faire des rapports est la meilleure manière que je connaisse qui permette d'inclure le code et les résultats d'une analyse. Pour faire ça bien avec R, on a déjà vu dans un article précédant que les rapports Rmarkdown étaient une très bonne solution.
Le but de cet article va être de vous permettre de gagner du temps avec le cache de Rmarkdown et quelques autres petites astuces. Pour cela, vous aurez besoin de 2 choses : avoir les bases du Rmarkdown et avoir des bases générales en informatiques. J'utilise RStudio pour faire mes codes Rmarkdown, je vous encourage à en faire de même.
Allez, c'est parti !
Alors si vous avez suivi l'article de notre cher ami jéro, vous avez un rapport Rmarkdown tout fait, qui date du 22 mars 2005 et qui produit un PDF. Je vais commencer par le commencement de votre fichier et vous donner 3 astuces du début de chacun de vos rapports.
Personnellement, je fais très rarement des rapports PDF, pour des raisons de compatibilité du code entre Windows et Mac OS ou GNU/Linux. En effet, je suis obligé d'utiliser Windows au boulot et il y a beaucoup de problèmes pour compiler les PDF. Je ne vais pas rentrer dans les détails, mais retenez donc que je ne fais quasiment que des fichiers HTML. Donc déjà, dans mon entête en YAML, je remplace
1 |
pdf_document par html_document |
. J'ajoute d'ailleurs l'option
1 |
number_sections : true |
en dessous et au même niveau d'indentation que
1 |
toc : true |
, ce qui va automatiquement mettre un numéro à mes sections.
Deuxième astuce, on peut mettre du code R dans l'entête YAML. L'intérêt est qu'on peut donc utiliser des fonctions, comme la fonction
1 |
Sys.time() |
, qui nous donne la date et l'heure. J'ai donc dans chacune de mes entêtes à la place de
1 |
date : |
une date entrée manuellement le code suivant :
1 |
date : "r substr(Sys.time(), 1, 10)" |
, qui vous mettra automatiquement à chaque fois que vous allez compiler votre rapport, la date de cette compilation.
Dernière astuce avant de vraiment parler de cache, on peut définir en début de rapport des options qui vont s'appliquer à tous les chunks par la suite, par défaut. Donc je vous donne mes paramètres par défaut et je vous les commente ensuite :
1 2 3 4 5 6 7 8 9 10 11 12 13 |
[crayon-670c5feed08e7415602619 ]{r setup, echo=FALSE, message=FALSE} library(knitr) opts_chunk$set(fig.align = "center", fig.retina = 2, fig.width = 10, cache = TRUE, cache.lazy = FALSE) |
Comme vous pouvez le voir, je les mets dans leur propre chunk que je nomme "setup". Le nom d'un chunk est très important pour se repérer lors de la compilation. On s'en servira en plus à la suite de cet article. Le nom est automatiquement compris par RStudio comme les caractères qui se trouvent après
1 |
`{r |
et avant la virgule. Après la virgule se trouvent les options de ce chunk, qui le rendent silencieux (
1 |
echo=FALSE |
) et qui sortent les messages vers la console de compilation et pas dans le rapport.
Ensuite on a donc le code R, je charge la library knitr sur la première ligne et je définis les options de mes chunks suivants sur les lignes suivantes. Les options sont passées par la fonction
1 |
opts_chunk$set |
. Je choisis l'alignement de mes figures au centre de la page, une taille de figures doublée quand elle s'affiche sur un écran à très haute résolution, une largeur de figure de 10, et enfin la création d'un cache pour tous mes chunks, qui n'utilisera pas le lazy loading (je ne vais pas expliquer ce que c'est, mais utilisez cette option ou vous aurez des problèmes). On aura ainsi tous nos chunks suivant celui-ci qui utiliseront ces options.
J'en profite pour ouvrir une parenthèse pour les plus avancés, si vous voulez avoir du code de suivi de l'avancement de votre compilation qui s'affiche dans votre console de compilation, il suffit de faire imprimer à votre code R un message ou un warning, et de mettre les options du chunk correspondantes en FALSE, ce qui fera que l'impression du message ou du warning ne se feront pas dans le rapport mais dans la console.
Voilà, maintenant que c'est fait pour les astuces, je vais vous en dire plus sur le cache.
Le cache c'est une copie sur votre disque dur de ce qui a été fait dans un chunk. C'est particulièrement utile lorsque l'on fait des analyses qui prennent beaucoup de temps à tourner. En effet, quand vous travaillez sur votre rapport, si l'un de vos chunks prend 2 jours à tourner, vous serez bien contents de ne pas avoir à le refaire à chaque fois que vous modifiez l'un des autres chunks. Ainsi, lorsqu'un chunk crée un cache, dans le dossier où vous avez stocké votre rapport, un sous-dossier se crée portant le nom de votre rapport + _cache. Dans ce dernier, un dossier html/pdf/autre se crée en fonction du type de rapport que vous produisez, puis enfin à l'intérieur vous trouverez les fichiers qui portent comme nom
1 |
nom de votre chunk_unCodeBienCompliqué.RData |
.
Le code bien compliqué est un hash, en gros un code, qui garantie que votre chunk n'a pas changé depuis qu'il a été sauvé en cache. Si le chunk a changé, alors automatiquement un nouveau cache sera produit, remplaçant celui qui est devenu inutile. Un petit point de détail, on peut avoir des longues analyses qui produisent des petits caches et des courtes qui en produisent des gros (et vice-versa). Plus un fichier de cache est gros plus il prendra de temps à être chargé. il peut donc être utile d'enlever le cache d'un chunk (donc mettre
1 |
cache=FALSE |
dans les options du chunk) qui ferait une analyse rapide mais produirait un gros cache, si votre disque dur n'est pas très performant. Je ne mets pas en cache non plus mon chunk qui charge mes librairies et mes fichiers, ça peut causer des soucis. Enfin, je ne mets pas non plus en cache les chunks qui initialisent un sous programme, ou une méthode de multithreading. Dans tous les autres cas, gardez le cache.
J'en entends déjà me dire "Bon ok, mais tu dois avoir une autre idée derrière la tête avec ces caches". Ils me connaissent bien, en effet. Alors tout d'abord, sachez que les caches s'exportent très bien. Un cache produit sur un cluster peut tout à fait être rapatrié sur votre ordinateur. Donc vous pouvez faire le gros d'un rapport sur un cluster, puis fignoler sur votre machine locale. Ensuite, et c'est là pour moi la plus grande beauté de la chose, on peut les charger directement comme n'importe quel fichier RData (avec la fonction load). L'intérêt étant que si on veut reprendre toute une analyse et bricoler dans la console R, c'est possible. Il suffit de charger chacun des chunks dans l'ordre !
"Mais pourquoi on ne charge pas que le dernier ???" Ah, bonne question ! (Et oui j'aime me faire des dialogues dans ma tête…)
Chaque chunk ne contient QUE ce qu'il a produit. Il ne contient donc aucun objet créé avant. Il faut donc bien tous les charger pour tout avoir. Et c'est là qu'on va pointer le plus gros problème de cette méthode : Si on change un chunk qui a des répercussions sur la suite, ce n'est pas automatiquement propagé. Dans ce cas, je vous conseille soit de supprimer le fichier cache des chunks qui doivent être modifiés, soit de supprimer tout le cache.
"Ouhla mais c'est galère de charger chaque chunk dans le bon ordre !"
Bon, vu que je suis sympa avec vous, j'ai écrit une petite fonction R qui va le faire pour vous. Pour qu'elle fonctionne, vous aurez besoin d'avoir déjà installé la librairie devtools, puis de lire mon script en fichier source :
1 2 |
[crayon-670c5feed08fd877304552 ]library(devtools)<br> source_url("https://gist.github.com/achateigner/e3f905d9fc98d34c8ecee93275c80a07/raw/loadAllChunksCache.R") |
Ensuite, il vous suffira d'appeler ma fonction et de lui donner en argument le nom du rapport pour lequel vous voulez charger le cache :
1 |
[crayon-670c5feed0903867342488 ]loadAllChunksCache("Rapport.Rmd") |
Il chargera automatiquement et dans l'ordre de votre rapport le cache qu'il trouvera dans le premier dossier existant qui correspond à "Rapport_cache/html/" ou "Rapport_cache/pdf/" ou "Rapport_cache/word/". Vous pouvez aussi lui spécifier en deuxième argument le dossier où vous voulez qu'il prennent le cache. C'est utile par exemple si vous avez du cache dans le dossier "Rapport_cache/html/" mais que vous voulez celui qui est dans "Rapport_cache/pdf/". Il faudra aussi pour qu'il fonctionne que vous ayez nommé vos chunks, il ne fonctionnera pas avec les noms automatiques. Je pourrais mettre ça en place, mais je n'encourage pas cette mauvaise pratique. Flemmard oui, mais flemmard avec classe !
Voilà pour mes astuces sur le cache des rapports Rmarkdown. Je pense que le cache est le plus gros intérêt de faire des rapports dès le début du développement de n'importe quel projet. Je vous conseille une fois que vous avez fini de développer votre rapport de supprimer tout le cache et de refaire tourner votre analyse entièrement. On n'est jamais à l'abris d'une erreur qui se propage.
Enfin, et pour finir, je vais faire un peu de pub pour mes librairies et autres scripts qui pourraient vous être utiles :
J'ai amélioré la fonction ipak que j'ai trouvé sur le github de Steven Worthington qui permet maintenant en une seule fonction de charger une liste de librairies, et de les installer du CRAN ou de bioconductor directement si elles ne sont pas installées. Elles seront chargées après leur installation. Pour l'utiliser, encore une fois il vous faut devtools d'installé, puis charger cette fonction :
1 2 3 4 5 6 7 |
[crayon-670c5feed090d027363918 ]library(devtools) source_url("https://gist.github.com/achateigner/f7948d43f34c1d1bcd83097036338601/raw/ipak.R") packagesNeeded < ;- c("captioner", "apercu", "viridis") ipak(packagesNeeded) |
J'ai créé le package apercu, dont la fonction principale, ap(), vous permet d'afficher… roulement de tambour… un aperçu de vos objets. Je m'explique : de la même manière que head() affiche les 6 premieres lignes d'une matrice par défaut, ou les 6 premiers éléments d'un vecteur ou d'une liste, ap() en affiche 5, à la différence que pour une matrice ou un data.frame, il n'affiche aussi que les 5 premières colonnes. Cette fonction vous sera particulièrement utile en cours de développement, pour voir rapidement vos grosses matrices, data frames, listes, vecteurs et même des objets plus compliqués et imbriqués. Ce package est disponible sur le CRAN, il s'installe donc normalement avec install.packages("apercu") ou avec ipak("apercu").
Voilà ! Sur ce je vous laisse en remerciant mes relecteurs, Yoann M. et Kumquatum pour leurs commentaires !
Laisser un commentaire