- Le blog participatif de bioinformatique francophone depuis 2012 -

Maîtrisez le cache de Rmarkdown !

Pour des rai­sons de repro­duc­tion de la science, il est impor­tant de conser­ver une trace de tout ce que l'on fait sur son ordi­na­teur. Pour cela, faire des rap­ports est la meilleure manière que je connaisse qui per­mette d'inclure le code et les résul­tats d'une ana­lyse. Pour faire ça bien avec R, on a déjà vu dans un article pré­cé­dant que les rap­ports Rmark­down étaient une très bonne solu­tion.

License : CC0 Public Domain

Le but de cet article va être de vous per­mettre de gagner du temps avec le cache de Rmark­down et quelques autres petites astuces. Pour cela, vous aurez besoin de 2 choses : avoir les bases du Rmark­down et avoir des bases géné­rales en infor­ma­tiques. J'utilise RStu­dio pour faire mes codes Rmark­down, je vous encou­rage à en faire de même.

Allez, c'est par­ti !

Alors si vous avez sui­vi l'article de notre cher ami jéro, vous avez un rap­port Rmark­down tout fait, qui date du 22 mars 2005 et qui pro­duit un PDF. Je vais com­men­cer par le com­men­ce­ment de votre fichier et vous don­ner 3 astuces du début de cha­cun de vos rap­ports.

Per­son­nel­le­ment, je fais très rare­ment des rap­ports PDF, pour des rai­sons de com­pa­ti­bi­li­té du code entre Win­dows et Mac OS ou GNU/​Linux. En effet, je suis obli­gé d'utiliser Win­dows au bou­lot et il y a beau­coup de pro­blèmes pour com­pi­ler les PDF. Je ne vais pas ren­trer dans les détails, mais rete­nez donc que je ne fais qua­si­ment que des fichiers HTML. Donc déjà, dans mon entête en YAML, je rem­place pdf_document par html_document. J'ajoute d'ailleurs l'option number_sections: true en des­sous et au même niveau d'indentation que toc: true, ce qui va auto­ma­ti­que­ment mettre un numé­ro à mes sec­tions.

Deuxième astuce, on peut mettre du code R dans l'entête YAML. L'intérêt est qu'on peut donc uti­li­ser des fonc­tions, comme la fonc­tion Sys.time(), qui nous donne la date et l'heure. J'ai donc dans cha­cune de mes entêtes à la place de date: une date entrée manuel­le­ment le code sui­vant : date: "r substr(Sys.time(), 1, 10)" , qui vous met­tra auto­ma­ti­que­ment à chaque fois que vous allez com­pi­ler votre rap­port, la date de cette com­pi­la­tion.

Der­nière astuce avant de vrai­ment par­ler de cache, on peut défi­nir en début de rap­port des options qui vont s'appliquer à tous les chunks par la suite, par défaut. Donc je vous donne mes para­mètres par défaut et je vous les com­mente ensuite :

Comme vous pou­vez le voir, je les mets dans leur propre chunk que je nomme "setup". Le nom d'un chunk est très impor­tant pour se repé­rer lors de la com­pi­la­tion. On s'en ser­vi­ra en plus à la suite de cet article. Le nom est auto­ma­ti­que­ment com­pris par RStu­dio comme les carac­tères qui se trouvent après `{r  et avant la vir­gule. Après la vir­gule se trouvent les options de ce chunk, qui le rendent silen­cieux (echo=FALSE) et qui sortent les mes­sages vers la console de com­pi­la­tion et pas dans le rap­port.

Ensuite on a donc le code R, je charge la libra­ry kni­tr sur la pre­mière ligne et je défi­nis les options de mes chunks sui­vants sur les lignes sui­vantes. Les options sont pas­sées par la fonc­tion opts_chunk$set. Je choi­sis l'alignement de mes figures au centre de la page, une taille de figures dou­blée quand elle s'affiche sur un écran à très haute réso­lu­tion, une lar­geur de figure de 10, et enfin la créa­tion d'un cache pour tous mes chunks, qui n'utilisera pas le lazy loa­ding (je ne vais pas expli­quer ce que c'est, mais uti­li­sez cette option ou vous aurez des pro­blèmes). On aura ain­si tous nos chunks sui­vant celui-ci qui uti­li­se­ront ces options.

J'en pro­fite pour ouvrir une paren­thèse pour les plus avan­cés, si vous vou­lez avoir du code de sui­vi de l'avancement de votre com­pi­la­tion qui s'affiche dans votre console de com­pi­la­tion, il suf­fit de faire impri­mer à votre code R un mes­sage ou un war­ning, et de mettre les options du chunk cor­res­pon­dantes en FALSE, ce qui fera que l'impression du mes­sage ou du war­ning ne se feront pas dans le rap­port mais dans la console.

Voi­là, main­te­nant 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 par­ti­cu­liè­re­ment utile lorsque l'on fait des ana­lyses qui prennent beau­coup de temps à tour­ner. En effet, quand vous tra­vaillez sur votre rap­port, si l'un de vos chunks prend 2 jours à tour­ner, vous serez bien contents de ne pas avoir à le refaire à chaque fois que vous modi­fiez l'un des autres chunks. Ain­si, lorsqu'un chunk crée un cache, dans le dos­sier où vous avez sto­cké votre rap­port, un sous-dos­sier se crée por­tant le nom de votre rap­port + _​cache. Dans ce der­nier, un dos­sier html/​pdf/​autre se crée en fonc­tion du type de rap­port que vous pro­dui­sez, puis enfin à l'intérieur vous trou­ve­rez les fichiers qui portent comme nom nom de votre chunk_unCodeBienCompliqué.RData.

Le code bien com­pli­qué est un hash, en gros un code, qui garan­tie que votre chunk n'a pas chan­gé depuis qu'il a été sau­vé en cache. Si le chunk a chan­gé, alors auto­ma­ti­que­ment un nou­veau cache sera pro­duit, rem­pla­çant celui qui est deve­nu inutile. Un petit point de détail, on peut avoir des longues ana­lyses qui pro­duisent des petits caches et des courtes qui en pro­duisent des gros (et vice-ver­sa). Plus un fichier de cache est gros plus il pren­dra de temps à être char­gé. il peut donc être utile d'enlever le cache d'un chunk (donc mettre cache=FALSE dans les options du chunk) qui ferait une ana­lyse rapide mais pro­dui­rait un gros cache, si votre disque dur n'est pas très per­for­mant. Je ne mets pas en cache non plus mon chunk qui charge mes librai­ries et mes fichiers, ça peut cau­ser des sou­cis. Enfin, je ne mets pas non plus en cache les chunks qui ini­tia­lisent un sous pro­gramme, ou une méthode de mul­ti­threa­ding. Dans tous les autres cas, gar­dez le cache.

J'en entends déjà me dire "Bon ok, mais tu dois avoir une autre idée der­riè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 pro­duit sur un clus­ter peut tout à fait être rapa­trié sur votre ordi­na­teur. Donc vous pou­vez faire le gros d'un rap­port sur un clus­ter, puis figno­ler sur votre machine locale. Ensuite, et c'est là pour moi la plus grande beau­té de la chose, on peut les char­ger direc­te­ment comme n'importe quel fichier RDa­ta (avec la fonc­tion load). L'intérêt étant que si on veut reprendre toute une ana­lyse et bri­co­ler dans la console R, c'est pos­sible. Il suf­fit de char­ger cha­cun des chunks dans l'ordre !

"Mais pour­quoi on ne charge pas que le der­nier ???" Ah, bonne ques­tion ! (Et oui j'aime me faire des dia­logues dans ma tête…)

Chaque chunk ne contient QUE ce qu'il a pro­duit. Il ne contient donc aucun objet créé avant. Il faut donc bien tous les char­ger pour tout avoir. Et c'est là qu'on va poin­ter le plus gros pro­blème de cette méthode : Si on change un chunk qui a des réper­cus­sions sur la suite, ce n'est pas auto­ma­ti­que­ment pro­pa­gé. Dans ce cas, je vous conseille soit de sup­pri­mer le fichier cache des chunks qui doivent être modi­fiés, soit de sup­pri­mer tout le cache.

"Ouh­la mais c'est galère de char­ger chaque chunk dans le bon ordre !"

Bon, vu que je suis sym­pa avec vous, j'ai écrit une petite fonc­tion R qui va le faire pour vous. Pour qu'elle fonc­tionne, vous aurez besoin d'avoir déjà ins­tal­lé la librai­rie dev­tools, puis de lire mon script en fichier source :

Ensuite, il vous suf­fi­ra d'appeler ma fonc­tion et de lui don­ner en argu­ment le nom du rap­port pour lequel vous vou­lez char­ger le cache :

Il char­ge­ra auto­ma­ti­que­ment et dans l'ordre de votre rap­port le cache qu'il trou­ve­ra dans le pre­mier dos­sier exis­tant qui cor­res­pond à "Rapport_​cache/​html/​" ou "Rapport_​cache/​pdf/​" ou "Rapport_​cache/​word/​". Vous pou­vez aus­si lui spé­ci­fier en deuxième argu­ment le dos­sier où vous vou­lez qu'il prennent le cache. C'est utile par exemple si vous avez du cache dans le dos­sier "Rapport_​cache/​html/​" mais que vous vou­lez celui qui est dans "Rapport_​cache/​pdf/​". Il fau­dra aus­si pour qu'il fonc­tionne que vous ayez nom­mé vos chunks, il ne fonc­tion­ne­ra pas avec les noms auto­ma­tiques. Je pour­rais mettre ça en place, mais je n'encourage pas cette mau­vaise pra­tique. Flem­mard oui, mais flem­mard avec classe !

Voi­là pour mes astuces sur le cache des rap­ports Rmark­down. Je pense que le cache est le plus gros inté­rêt de faire des rap­ports dès le début du déve­lop­pe­ment de n'importe quel pro­jet. Je vous conseille une fois que vous avez fini de déve­lop­per votre rap­port de sup­pri­mer tout le cache et de refaire tour­ner votre ana­lyse entiè­re­ment. On n'est jamais à l'abris d'une erreur qui se pro­page.

Enfin, et pour finir, je vais faire un peu de pub pour mes librai­ries et autres scripts qui pour­raient vous être utiles :

J'ai amé­lio­ré la fonc­tion ipak que j'ai trou­vé sur le github de Ste­ven Wor­thing­ton qui per­met main­te­nant en une seule fonc­tion de char­ger une liste de librai­ries, et de les ins­tal­ler du CRAN ou de bio­con­duc­tor direc­te­ment si elles ne sont pas ins­tal­lées. Elles seront char­gées après leur ins­tal­la­tion. Pour l'utiliser, encore une fois il vous faut dev­tools d'installé, puis char­ger cette fonc­tion :

J'ai créé le package aper­cu, dont la fonc­tion prin­ci­pale, ap(), vous per­met d'afficher… rou­le­ment de tam­bour… un aper­çu de vos objets. Je m'explique : de la même manière que head() affiche les 6 pre­mieres lignes d'une matrice par défaut, ou les 6 pre­miers élé­ments d'un vec­teur ou d'une liste, ap() en affiche 5, à la dif­fé­rence que pour une matrice ou un data.frame, il n'affiche aus­si que les 5 pre­mières colonnes. Cette fonc­tion vous sera par­ti­cu­liè­re­ment utile en cours de déve­lop­pe­ment, pour voir rapi­de­ment vos grosses matrices, data frames, listes, vec­teurs et même des objets plus com­pli­qués et imbri­qués. Ce package est dis­po­nible sur le CRAN, il s'installe donc nor­ma­le­ment avec install.packages("apercu") ou avec ipak("apercu").

Voi­là ! Sur ce je vous laisse en remer­ciant mes relec­teurs, Yoann M. et Kum­qua­tum pour leurs com­men­taires !

Vous avez aimé ? Dites-le nous !

Moyenne : 0 /​ 5. Nb de votes : 0

Pas encore de vote pour cet article.

Partagez cet article :




Commentaires

Laisser un commentaire