La documentation c’est important ! Pensez à votre avenir et à celui des autres. Quel que soit le but de votre script et de vos analyses, pensez à la documentation. Dans ma tête, la structure du package R est faite pour ça. Je vais tenter de vous en convaincre.
À use’R 2019 à Toulouse, j’ai fait une présentation intitulée : ‘La méthode “Rmd first” : quand les projets commencent avec la documentation’’ (Vidéo sur Youtube: https://youtu.be/cB1BCxFbhtk). J’ai fini cette présentation en disant : Think package ! J’explique aussi à une audience un peu plus large dans l’open source en quoi collaborer à plusieurs sur ce genre de tâche peut être encourageant pour des débutants en création de packages : « Transforming scattered analyses into a documented, reproducible and shareable workflow – FOSDEM Brussels », 2020-02 .
Si vous avez vraiment peur de construire un package, vous devriez peut-être jeter un oeil à cette présentation d’abord. Mais si vous n’avez pas trop peur, vous pouvez commencer directement avec ce post de blog. Dans tous les cas, Paco
le package devrait rendre tout ça plus agréable ! J’espère en tout cas….
Cet article est un peu long, vous pouvez faire une lecture rapide si vous ne lisez pas les “bullet point” et revenir dessus plus tard lors d’un véritable projet.
Pourquoi la documentation est-elle importante ?
Table des matières
Éviter le “mal de code”
- N’avez vous jamais repris un de vos scripts R, 6 mois plus tard ? Vous vous souveniez parfaitement de comment fonctionnaient toutes les parties du code ? Les fonctions et leurs paramètres ?
- Vous n’avez jamais eu à améliorer/déboguer le code de quelqu’un d’autre ?
- On ne vous a jamais demandé d’ajouter des fonctionnalités dans une application Shiny écrite à l’origine par un stagiaire de master, puis modifiée ponctuellement par le superviseur pour “simplement” ajouter une ou deux fonctionnalités ?
Si c’est non pour tout, vous avez de la chance. Pour l’instant….
Dans tous les cas, pensez à votre futur vous et pensez aux futurs utilisateurs ou développeurs de votre travail. Ne pensez jamais que c’est un coup d’un jour. Un jour ou l’autre, ce script sera à nouveau utile. Il sera plus utile s’il est correctement documenté.
De multiples raisons peuvent rendre la documentation nécessaire :
- Vous partagez vos scripts avec votre jeune padawan et vous n’avez pas vraiment de temps à passer avec elle/lui pour expliquer chaque étape. Vous préférez consacrer votre temps à des discussions plus constructives autour du projet, plutôt qu’à l’utilisation d’une fonction spécifique.
- Un collègue doit effectuer votre analyse pendant votre arrêt maladie. Les arrêts maladie ne sont généralement pas quelque chose de prévisible.
- Votre client ou boss demande une mise à jour sur votre travail. Ne serait-il pas agréable de présenter quelque chose qui ressemble à un rapport avec des tableaux et des graphiques, au lieu d’exécuter du code en direct et d’essayer d’expliquer à la volée ce qu’ils sont en train de regarder ?
- Vous voulez partager votre travail avec le monde entier ! Un jour ou l’autre, on vous demandera de partager vos scripts parce qu’ils peuvent intéresser les gens de votre équipe. Ou peut-être pensez-vous que cela peut être utile de partager votre code avec cette belle communauté R qui vous donne tant de choses chaque jour….
Ne rendez pas (trop) malades les personnes qui devront comprendre et utiliser votre code… Donnez-leur le remède, donnez leur une documentation.
Si vous lisez cet article, c’est que vous avez ce qu’il faut pour créer un package
Vous n’êtes pas ici par hasard. Vous êtes ici parce qu’un jour, vous avez écrit une ligne de code en langage R. Du coup, faites-moi confiance, vous en savez assez pour créer un package.
Lorsqu’on dit à nos étudiants/stagiaires, après leurs deux premiers jours, que la prochaine étape est de construire un package, les premières réactions sont :
Je viens tout juste de commencer, je ne suis pas un développeur qui peut créer un package.
Je ne veux pas partager mon travail sur le CRAN avec le monde, pas même sur Github.
Nos réponses sont :
Le premier “copier-coller” de code nécessite une fonction, la première fonction nécessite un package.
Vous pouvez créer un paquet en six minutes et je vais vous le montrer dès maintenant : Créer un package en quelques minutes.
Vous ne savez pas de quoi vous êtes capable. Croyez-moi, dans les deux prochains jours de formation, vous pourrez créer votre propre package, au minimum pour un usage interne.
Le package force à une documentation standardisée
- Le package oblige à une description générale standardisée de votre projet
- Le package oblige à une documentation standardisée des fonctions
- Le package recommande de présenter des exemples reproductibles pour chaque fonction
- Le package permet l’intégration de guides d’utilisation (vignettes)
- La structure standardisée d’un package et son “check” sont supposés conduire à un code réutilisable.
Il y a différents endroits dans un package où écrire du code, du texte, des informations, …. Se souvenir de la procédure complète de développement de package peut être douloureux pour les débutants, même pour les développeurs avancés. De fait, certains pensent qu’il faut construire une centaine de paquets avant d’être en mesure de tout savoir. Ce n’est pas vrai.
La méthode ‘Rmd first’ peut réduire la peur de créer des package en développant comme si vous n’étiez pas dans un package
De plus, certains packages utiles sont là pour vous aider dans toutes les étapes de développement de package, parmi lesquels {devtools}, {usethis}, {attachment}, {golem}, {desc}, ….
Développez comme si vous n’étiez pas dans un package
Dans ma présentation à use’R 2019 à Toulouse, j’ai commencé à développer mon code dans un projet R “classique”. Si vous avez regardé cette présentation, vous savez que nous finirons par créer un package. Ici, nous commencerons directement par construire un squelette de package Il y a juste quelques fichiers à penser avant de développer. Vous verrez que tout est question de documentation.
Dans cet article, nous allons commencer une petite analyse de données dans un projet nommé my.analysis.package
.
Ces étapes ressemblent au développement de package…
Votre projet doit être présenté. Quel est l’objectif de ce travail ? Qui êtes-vous ? D’autres personnes sont-elles autorisées à utiliser votre code ? Nous conserverons également une trace de l’historique des étapes de la construction du package.
- Créer un nouveau “package R en utilisant devtools” si vous utilisez Rstudio
- Ou utilisez
usethis::create_package("my.analysis.package")
- Une contrainte est que le nom de votre projet ne peut être composé que de lettres, de chiffres et de points. Elle doit commencer par une lettre et ne peut se terminer par un point.
- Ou utilisez
- Créez un fichier nommé
dev_history.R
à la racine du projet- Attention, si vous travaillez avec Rstudio, il propose d’enregistrer ce fichier dans le dossier R. Il ne faut surtout pas faire cela. Vérifiez bien que le fichier
dev_history.R
est stocké à la racine du projet - Ce fichier stocke la documentation de votre workflow. Cela rend la création et le développement de votre package lui-même reproductible.
- N’oubliez pas de le cacher de la compilation du package en utilisant
usethis::use_build_ignore("dev_history.R")
. - Utilisez-le pour stocker tous vos appels vers
usethis::
,devtools::
,attachment::
et autres… - Jetez un coup d’oeil à ce que nous avons utilisé dans ce fichier pour le développement de {attachment} ou quel code {golem} on vous propose pour votre workflow de développement d’une Application Shiny
- N’oubliez pas de le cacher de la compilation du package en utilisant
- Attention, si vous travaillez avec Rstudio, il propose d’enregistrer ce fichier dans le dossier R. Il ne faut surtout pas faire cela. Vérifiez bien que le fichier
- Remplissez le fichier de
DESCRIPTION
de projet- Donnez un titre à votre analyse
- Rédigez une phrase de description. Cette phrase devra se terminer par un point
.
. Si vous devez écrire la description sur plusieurs lignes, indentez le texte avec deux espaces supplémentaires au début de chaque nouvelle ligne. - Inscrivez votre nom en tant qu’auteur et créateur:
Authors@R: person("Sébastien", "Rochette", email = "[email protected]", role = c("aut", "cre"))
- Définir la licence
- Vous pouvez envisager une licence pour tous vos travaux. Vous ne savez jamais qui va lire et réutiliser votre code dans le futur.
- Si vous ne savez pas quoi choisir, vous pouvez choisir une licence propriétaire. Utilisez
License : file LICENSE
dans le fichierDESCRIPTION
puis créez un fichier appeléLICENSE
(sans extension) à la racine du projet, contenant par exemple :
Proprietary Do not distribute outside of ThinkR.
- Ajoutez les lignes suivantes dans votre script
dev_history.R
et exécutez-les à chaque fois que vous voyez : dans cet article. Y compris maintenant.
# Document functions and dependencies
attachment::att_to_description()
# Check the package
devtools::check()
- Vous devriez obtenir
0 errors, 0 warnings, 0 notes
Travaillez vos données dans un document Rmd presque comme un projet de développement classique
Le fichier Rmd est votre bac à sable avant d’être le guide d’utilisation de votre travail. Utilisez-le pour essayer, explorer, tester vos données et analyses. Une fois que vous êtes prêt, suivez le guide pour nettoyer le contenu et tout ranger au bon endroit.
- Créer un petit jeu de données
- Un petit jeu de données facilite le processus de documentation.
Si vous construisez ce package pour une analyse spécifique pour votre travail ou pour un client, vous pourriez avoir envie d’utiliser le jeu de données complet directement. La manipulation directe d’un grand jeu de données peut être pénible pendant l’écriture du code. Vous risquez de faire face à de long temps de calcul pendant le débogage. Ne négligez pas la puissance d’un petit exemple reproductible ! Il se peut qu’il ne couvre pas tous les futurs bogues possibles, mais ça accélérera le développement et aidera à documenter le code.
- Pour commencer, vous pouvez stocker le jeu de données
my-dataset.csv
, dans un dossier nomméinst/example-data/
. Cela permettra plus tard de retrouver ces données en utilisantsystem.file("example-data/my-dataset.csv", package = "my.analysis.package")
. - Il y a différentes façons de stocker des données à l’intérieur d’un package. La solution proposée ici est un moyen facile de démarrer. Lorsque vous serez assez mûr, vous pourrez consulter le chapitre External data dans le livre “R Packages” pour décider de la manière dont vous souhaitez stocker les données.
- “Rmd first” : C’est ici que commence la méthode “Rmd first”. Pas vraiment “first” me direz-vous, mais presque “first” car nous n’avons pas encore commencé le développement du code. Créons donc une vignette. Il s’agit d’un fichier RMarkdown classique stocké à un endroit spécifique et qui sera affiché dans une sortie HTML formatée spécifiquement.
usethis::use_vignette("aa-exploration")
- Si vous choisissez un nom de fichier de vignettes commençant avec deux lettres, cela sera utilisé pour les afficher dans le bon ordre dans la documentation. Utilisez la même chose dans le champ
VignetteIndexEntry
du YAML de votre vignette, mais vous pouvez les supprimer dans le titre. Je recommande d’utiliser des lettres au lieu de chiffres à cause de l’utilisation ultérieure de {bookdown} avec le paquet {chameleon}.
- Il peut être nécessaire d’installer votre package maintenant. Cela rendra votre jeu de données disponible pour la vignette.
- Vous pouvez utiliser
devtools::install()
. Ajoutez-le dans votre"dev_history.R"
à exécuter si nécessaire.
- Vous pouvez utiliser
La méthode “Rmd first” ressemble à une analyse classique
Le workflow d’une étude commence souvent par la lecture, le nettoyage, le filtrage, l’exploration des données. Ces étapes peuvent être présentées dans un fichier Rmarkdown, afin que vous puissiez créer un rapport de votre travail et le rendre reproductible pour les autres.
Le “Rmd first” suppose que n’importe quel projet peut démarrer comme une analyse de données. Quel que soit le projet que vous réalisez, vous aurez besoin de données, d’exemples reproductibles pour le construire. Ci-dessous se trouve un petit exemple d’une construction de Rmd.
- Charger les packages nécessaires en haut de votre vignette
- Si vous avez besoin de plus de packages au cours du développement, remontez toujours en haut pour les ajouter afin que les futurs utilisateurs ne soient pas surpris au milieu de l’analyse.
- Écrivez une phrase sur ce que vous avez l’intention de faire dans le prochain bloc de code
> Say what you’ll do, do what you said (Diane Beldame, ThinkR). [Dîtes ce que vous allez faire, faîtes ce que vous avez dit]
4. Do what you said
- Chargez vos données
- Si vous avez installé votre package, le chemin d’accès aux données est disponible avec
# Get path datapath <- system.file("example-data/my-dataset.csv", package = "my.analysis.package") # Read data with {readr} for instance clients <- readr::read_csv(datapath)
- Écrivez le code que vous voulez, utilisez vos données comme cas d’application
- Quand votre code fait ce que vous voulez, transformez-le en fonction
- Documenter la fonction tant que vous êtes encore dans le Rmd
- Utiliser la syntaxe roxygen
- Documenter les paramètres
- Ajouter un exemple d’utilisation : vous avez déjà un exemple parce que vous avez écrit votre Rmd comme un exemple reproductible.
#' Filter by department
#'
#' @param x dataframe with column named id_dpt
#' @param dpt department number
#'
#' @return a filtered dataframe
#' @export
#' @examples
#' dataset_path <- system.file("example-data/my-dataset.csv", package = "my.analysis.package")
#' clients <- read_csv(dataset_path)
#' filter_by_dpt(clients, dpt = 11)
filter_by_dpt <- function(x, dpt) {
filter(x, id_dpt == dpt)
}
- Créez un test alors que vous êtes encore dans le Rmd.
- Vous disposez déjà d’un exemple reproductible pour construire votre test.
- Vous pouvez essayer le package {exampletestr} pour construire un test à partir de votre exemple de fonction. Pour cela, il faut d’abord réaliser le “Rmd cleansing” (voir ci-dessous).
dataset_path <- system.file("example-data/my-dataset.csv", package = "my.analysis.package")
clients <- read_csv(dataset_path)
output <- filter_by_dpt(clients, dpt = 11)
testthat::expect_is(output, "data.frame")
testthat::expect_equal(nrow(output), 10)
“Rmd cleansing” : Faites-en un véritable package
Tout ce dont vous avez besoin pour faire un bon package est déjà prêt. Nous allons maintenant nettoyer le Rmd. Cela signifie que nous allons déplacer des parties du code du Rmd dans les répertoires appropriés du package. Dans la vignette, nous ne conserverons que les explications et le code pour utiliser les fonctions principales.
DESCRIPTION
est déjà écrit.- Le dossier
inst/
ne change pas- Encore une fois, plus tard, vous pourrez penser à d’autres façons de stocker vos données (voir External data dans le livre “R Packages”).
- Le dossier
vignette/
ne change pas- Le fichier Rmd lui-même sera nettoyé
- La fonction
filter_by_dpt()
doit être déplacée (couper-coller)- Créez un dossier
R/
et un fichier script R nomméfilter_by_dpt.R
.- Vous pouvez utiliser
usethis::use_r("filter_by_dpt")
. Ajoutez-le dansdev_history.R
.
- Vous pouvez utiliser
- Couper et coller le code de la fonction avec son squelette roxygen
- Nettoyez les appels à
library
de votre vignette qui ont été utilisés pour vos fonctions et qui ne sont plus nécessaires directement dans la vignette
- Créez un dossier
- Les tests doivent être déplacés (couper-coller)
- Créez un dossier
tests/testthat/
et un fichier script R nommétest_filters.R
.- Vous pouvez utiliser
usethis::use_test("test_filters")
. Ajoutez-le dansdev_history.R
.
- Vous pouvez utiliser
- Créez un dossier
NAMESPACE
sera automatiquement construit dans la partie suivante
Un petit coup de polish sur le package
Votre package est prêt. Il est maintenant temps d’exécuter les fonctions pour créer de la documentation au format package et vérifier si votre package suit correctement les règles de construction.
- Créer les fichiers de documentation. Ceux qui apparaîtront lorsque vous appellerez l’aide d’une fonction. Remplissez (automatiquement) le fichier
NAMESPACE
.- Exécuter
attachment::att_to_description()
(qui contientdevtools::document()
)
- Exécuter
- Lister toutes les dépendances de votre package dans le fichier
DESCRIPTION
.- Pour ne rien oublier, utilisez
attachment::att_to_description()
.
- Pour ne rien oublier, utilisez
- régulièrement
Publier votre documentation
Puisque vous avez créé un package, vous pouvez utiliser {pkgdown} pour présenter la totalité de votre documentation dans le même site HTML. Le ‘Rmd first’ vous a obligé à créer des vignettes qui peuvent également être présentées sous forme de livre HTML (ou PDF), livré sous forme d’un guide d’utilisation partageable ou d’un rapport d’analyse.
- Utilisez {pkgdown} pour présenter votre documentation.
- Le site créé contient les vignettes, le fichier Readme (s’il a été créé) comme présentation de votre étude, les fonctions et les exemples d’utilisation des fonctions avec sorties.
- Vous pouvez le présenter à vos clients, la vignette est le résultat de votre analyse.
- Vous pouvez utiliser
chameleon::build_pkgdown()
pour construire et garder le site {pkgdown} dans votre package, afin que vos clients puissent l’appeler hors ligne avecmy.analysis.package::open_pkgdown()
.
- Transformez vos vignettes en un véritable guide d’utilisateur (ou rapport d’analyse) en utilisant {bookdown} avec
chameleon::build_book()
. Vous pouvez le laisser dans le package pour que vos clients puissent l’appeler hors ligne avecmy.analysis.package::open_book()
, ou vous pouvez livrer le livre lui-même en HTML ou PDF.- C’est la raison pour laquelle j’ai nommé mes vignettes en commençant par deux lettres comme
aa-my-analysis.Rmd
.
- C’est la raison pour laquelle j’ai nommé mes vignettes en commençant par deux lettres comme
- Combinez avec un rapport {testdown} pour montrer à vos clients que vous avez travaillé correctement avec des tests unitaires pour chaque fonction.
Tout est package
Un package n’est pas seulement une archive stockée sur CRAN qui doit être partagée avec le reste du monde. Un package est un dossier qui contient des fichiers et des dossiers, organisés de telle sorte que chacun puisse retrouver la documentation, le code et les tests à une place précise. Ainsi, chaque projet peut être un package, de sorte que les outils construits autour des packages vous permettent de travailler de manière reproductible avec des projets réutilisables. Think package :
- Un package d’analyse de données pour un client utilisant la méthode ‘Rmd first’ permet de documenter les fonctions pour les analyses futures et de créer le guide utilisateur et/ou le rapport d’analyse avec {pkgdown}/{bookdown} pour le client.
- Une Shiny App avec {golem} et ‘Rmd first’ permet la documentation des fonctions internes pour le débogage et les améliorations futures, ainsi que la présentation des étapes intermédiaires de construction de l’app avec des rapports statiques.
- Un package est un package. En utilisant la méthode ‘Rmd first’, vous écrivez la documentation en même temps que votre code.
Ressources
- Ma présentation à Use’R 2019 Toulouse en vidéo: https://youtu.be/cB1BCxFbhtk
- Support de formation en mode « Rmd first » sur Github
- Article « Transformer plusieurs scripts en un beau package R »
- Si vous avez déjà écrit votre analyse dans un fichier Rmd et si vous êtes maintenant convaincu qu’elle serait plus réutilisable à l’intérieur d’un package, je vous recommande de lire le post de blog d’Emily Riederer intitulé “RMarkdown Driven Development (RmdDD)”. Elle présente le même genre d’approche ‘Rmd first’ et détaille les étapes pour nettoyer votre Rmd afin d’en faire un package
- Toutes les informations que vous devez savoir sur le développement de package se trouvent dans le bookdown “R packages” de Hadley Wickham et Jenny Bryan
- Construire une Application Shiny à l’intérieur d’un package en utilisant {golem}
- Les recommandations pour un “Workflow pour construire de grosses applications Shiny [documentées]” utilisant {golem} sont dans ce bookdown de Colin Fay
- Le package {chameleon} pour personnaliser et mettre en valeur vos sorties HTML
- Le package {testdown} pour présenter les résultats de vos tests unitaires dans un bookdown
Prenez plaisir à faire votre documentation !
Votre futur vous et les personnes qui vous entourent vous en remercierons !