Rmd first: Quand le développement commence par la documentation

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’’. J’ai fini cette présentation en disant : Think package ! 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 ?

É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.

Créez un fichier nommé dev_history.R, par exemple à 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

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: [email protected]: 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 fichier DESCRIPTION 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

  • Exécutez-les à chaque fois que vous voyez : build-now 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 utilisant system.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}.
  • build-now

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.

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]

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
  • 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 dans dev_history.R.
    • 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
  • 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 dans dev_history.R.
  • 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 contient devtools::document())
  • Lister toutes les dépendances de votre package dans le fichier DESCRIPTION.
    • Pour ne rien oublier, utilisez attachment::att_to_description().
  • build-now 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 avec my.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 avec my.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.
  • 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

Prenez plaisir à faire votre documentation ! Votre futur vous et les personnes qui vous entourent vous en remercierons !

Auteur

Sébastien Rochette
Sébastien RochetteModeller, R-trainer, Playing with maps
Modeller, R-trainer, Playing with maps