Chapitre 1 Construire un package

Nous prĂ©sentons ici comment construire un package efficacement Ă  l’aide d’outils graphiques prĂ©sents dans RStudio et du package devtools.

Le support de rĂ©fĂ©rence sur ce sujet est le livre R packages1 d’Hadley Wickham & Jennifer Bryan, disponible en ligne.

1.1 Initialiser un package

Une maniĂšre simple, et intĂ©grĂ©e Ă  RStudio, pour initialiser un package est d’executer les Ă©tapes suivantes :

👉 À vous de jouer (dĂ©jĂ )!

  1. créer un nouveau projet (menu déroulant en haut à gauche dans RStudio)

  2. choisir “New Directory”

  3. choisir “R package using devtools” (s’il n’est pas disponible c’est que le package devtools n’est pas installer et dans ce cas on peut alors choisir “R package” – la diffĂ©rence Ă©tant qu’avec “R package”, il faudra supprimer des fichiers crĂ©Ă©s automatiquement mais inutiles)

  4. donner un nom au package, par exemple mypkgr.

On récupÚre alors la structure minimale pour un package , à savoir :

  • un fichier DESCRIPTION dont les parties Title, Version, Authors@R et Description sont Ă  Ă©diter (d’autres parties pourront ĂȘtre Ă©diter voire mĂȘme ajouter de maniĂšre automatique, comme nous le verrons plus loin)

  • un fichier NAMESPACE qui sera Ă©ditĂ© automatiquement ultĂ©rieurement

  • un dossier R/ dans lequel on va ajouter des fichiers de scripts .R

devtools ajoute Ă©galement trois fichiers facultatifs :

  • .gitignore, relatif Ă  git, outils de contrĂŽle de version que nous verrons en dĂ©tails dans la partie suivante sur git & GitHub

  • mypkgr.Rproj qui est un fichier spĂ©cifique de RStudio, et permet de dĂ©finirles caractĂ©ristiques et prĂ©fĂ©rences du projet que nous venons de crĂ©er

  • .Rbuildignore qui permet d’ignorer certains fichiers au moment oĂč on construira le package un peu plus loin (par exemple, le fichier mypkgr.Rproj ne doit pas ĂȘtre inclus dans le package)

1.2 Ajouter une fonction : exemple fil rouge

Nous vous proposons de coder la fonction suivante, que nous reprendrons tout au long de la formation :

Nous souhaitons calculer la valeur de la densitĂ© d’une loi normale multivariĂ©e sur \(\mathbb{R}^p\) en \(n\) points. Notre fonction doit pouvoir s’appliquer pour n’importe quelle loi normale multivariĂ©e (vecteur de moyennes \(\boldsymbol \mu\) dans \(\mathbb{R}^p\) et matrice de variance-covariance \(\boldsymbol\Sigma\) d’ordre de \(p\) quelconques), et on souhaite pouvoir calculer toutes les valeurs de la densitĂ© Ă©valuĂ©es sur les \(n\) points \(\mathbf{x}\) en un seul appel de la fonction.

Pour rappel, la fonction de densitĂ© d’une loi normale multivariĂ©e s’écrit : \[\displaystyle (2\pi )^{-p/2}\det({\boldsymbol {\Sigma }})^{-1/2}\,\exp \left(-{\frac {1}{2}}(\mathbf {x} -{\boldsymbol {\mu }})^{\mathsf {T}}{\boldsymbol {\Sigma }}^{-1}(\mathbf {x} -{\boldsymbol {\mu }})\right)\]

Vous devez donc créer une fonction mvnpdf() dans un fichier nommé mvnpdf.R dans le dossier R/ du package, qui :

  • prend en arguments :

    • x une matrice, Ă  \(n\) colonnes (les observations) et \(p\) lignes

    • mean un vecteur de moyennes

    • varcovM une matrice de variance-covariance

    • Log un paramĂštre logique valant TRUE par dĂ©faut

  • renvoie une liste contenant la matrice x ainsi qu’un vecteur des images des points de x par la fonction de densitĂ© de la variable alĂ©atoire de loi normale multivariĂ©e considĂ©rĂ©e.

👉 À vous de jouer !

Voici une proposition de fonction que vous pouvez tĂ©lĂ©charger ici. ⚠ ATTENTION ! Si vous cliquez trop vite sur le lien ci-dessous, cela invalidera votre participation Ă  la formation !

Pour des conseils lors de la rédaction de code, voir le chapitre R code dans R packages (2023) de Wickham & Bryan2.

1.3 Documenter une fonction

Il est important de bien documenter votre code. Tout projet a au moins 2 développeurs :

  • vous

  • vous dans 6 mois

Par Ă©gard Ă  votre “futur vous”, soyez sympas et prenez le temps de documenter votre code 😉 !

Nous vous conseillons vivement d’utiliser le package roxygen2 pour documenter vos packages. L’avantage principal Ă©tant d’avoir l’aide d’une fonction dans le mĂȘme fichier que le code dĂ©finissant cette fonction.

👉 À vous de jouer !

  1. Commencer par insĂ©rer le squelette de l’aide grĂące Ă  “Insert Roxygen Skeleton” situĂ© dans le menu “Code” ou le sous-menu Baguette magique dans la fenĂȘtre de script.

  2. Compléter la documentation en renseignant :

    • le titre de la fonction (premiĂšre ligne)

    • la description de ce que fait la fonction (deuxiĂšme paragraphe)

    • si vous renseignez un troisiĂšme paragraphe, cette partie ira dans la section “Details” de la page d’aide

    • la signification des paramĂštres

    • la sortie, aprĂšs la balise @return

  3. GĂ©nĂ©rer la documentation Ă  l’aide de “Document” dans le menu “More” de l’onglet “Build” (ou Ctrl+Shift+D ou devtools::document()). L’effet de cette commande est multiple :

    • un dossier man a Ă©tĂ© crĂ©Ă© et Ă  l’intĂ©rieur, un fichier mvnpdf.Rd a Ă©tĂ© crĂ©Ă© et contient les informations de l’aide de la fonction

    • le fichier NAMESPACE a Ă©tĂ© modifiĂ©

En cas de bug ou par curiosité ET une fois que vous avez terminé vous pouvez consulter cette proposition.

Pour plus de dĂ©tails sur la documentation de package et les balises roxygen2, voir la page Object documentation du site d’Hadley.

Finissons par Ă©voquer une fonction du package usethis qui initialise une page d’aide pour le package dans son ensemble :

usethis::use_package_doc()

La page d’aide gĂ©nĂ©rĂ©e sera alors accessible, une fois le package installĂ©, via :

?mypkgr

1.4 Tester le package de maniÚre intéractive

Pour tester le package, vous devez le charger dans R à l’aide de : dans l’onglet “Build”, le menu “More” puis “Load All” (ou Ctrl+Shift+L ou devtools::load_all()).

Vous pouvez alors utiliser votre package directement dans R : consulter l’aide de la fonction avec ?mvnpdf et par exemple exĂ©cuter les commandes renseignĂ©es dans la section exemple de cette page d’aide.

?mvndpf

Ainsi, lors du développement, vous pouvez :

  • Ajouter/Modifier le code

  • Re-charger le package Ctrl+Shift+L

  • L’essayer dans la console

  • Et ainsi de suite


1.5 Tester le package de maniĂšre automatique

Pour initialiser la fonctionnalité de tests automatiques dans le package, executer la commande suivante :

usethis::use_testthat()

Cette commande induit la crĂ©ation d’un dossier tests qui comprend un fichier testthat.R – Ă  ne pas modifier – et un dossier testthat/ dans lequel on va insĂ©rer nos tests. Cet outils s’appuie sur la thĂ©orie des tests unitaires.

Voici par exemple le contenu d’un fichier contenant 2 tests qui devrait s’appeller test-mvnpdf.R Ă  mettre dans le dossier testthat/ (plutĂŽt que de le crĂ©er vous-mĂȘme, vous pouvez simplement utiliser la fonction usethis::use_test() qui crĂ©era le fichier pour vous en le plaçant directement au bon endroit) :

test_that("correct result for univariate gaussian", {
  expect_equal(mvnpdf(x=matrix(1.96), Log=FALSE)$y, dnorm(1.96))
  expect_equal(mvnpdf(x=matrix(c(1.96, -0.5), ncol = 2), Log=FALSE)$y,
               dnorm(c(1.96, -0.5)))
})

test_that("correct results for bivariate gaussian", {
  expect_equal(mvnpdf(x=matrix(rep(1.96,2), nrow=2, ncol=1), Log=FALSE)$y,
               mvtnorm::dmvnorm(rep(1.96, 2)))
})

Pour exĂ©cuter ces tests, on peut utiliser “Test package” (Ctrl+Shift+T) du menu “More” dans l’onglet “Build”, ou alors executer devtools::test() dans la console.

L’avantage de ces tests automatiques est qu’ils vont s’exĂ©cuter Ă  chaque fois qu’on effectuera un check du package.

Une bonne pratique est d’ajouter un test unitaire Ă  chaque fois qu’un bug est identifiĂ© et rĂ©solu, afin de pouvoir immĂ©diatement identifier et prĂ©venir qu’une erreur identique ne se reproduise dans le futur.

1.6 Faire un check du package

Faire un check signifie vĂ©rifier que tout est correct dans le package et fonctionne comme attendu, afin que l package puisse s’installer sans problĂšme sur diffĂ©rents systĂšmes d’exploitation. Il est impĂ©ratif de “passer” le R CMD CHECK pour pouvoir dĂ©poser un package sur le CRAN.

Pour exĂ©cuter celui-ci, utiliser “Check” (Ctrl+Shift+E) dans l’onglet “Build”, ou alors executez devtools::check() dans la console.

Lors du R CMD CHECK, les tests que nous avons mis au point prĂ©cĂ©demment sont exĂ©cutĂ©es. C’est justement l’avantage d’avoir fait ces tests, nous n’avons plus besoin de s’en prĂ©occuper, mais juste de rĂ©agir en cas d’erreurs renvoyĂ©es.

1.7 Installer le package

Pour le moment, le package n’existe que dans l’environnement associĂ© au projet Rstudio qu’on a crĂ©Ă©. Pour pouvoir l’utiliser dans R de maniĂšre gĂ©nĂ©rale, il faut l’installer (comme un package du CRAN par exemple).

Pour faire ça, utiliser “Install and Restart” Ctrl+Shift+B dans l’onglet “Build” (devtools::install() ou ).

Et enfin, vous pouvez configurer le comportement de RStudio pour qu’au moment de l’installation, il documente en mĂȘme temps le package : aller dans l’onglet “Build”, le menu “More” puis “Configure Build Tools
”. Cliquer ensuite sur “Configure” juste Ă  cĂŽtĂ© de “Generate documentation with Roxygen” puis cocher la case “Install and Restart”.

Annexe 1.1 : ajouter une méthode S3

Dans la plupart des packages on est amenĂ©s Ă  implĂ©menter des mĂ©thodes S3, trĂšs souvent pour qu’à partir d’un objet rĂ©sultat res, on puisse exĂ©cuter print(res), summary(res), plot(res)


Voici un exemple de mĂ©thode plot() qu’on peut ajouter dans notre package :

#' Plot of the mvnpdf function
#'
#' @param x an object of class \code{mvnpdf} resulting from a call of
#' \code{mnvpdf()} function.
#' @param ... graphical parameters passed to \code{plot()} function.
#'
#' @return Nothing is returned, only a plot is given.
#' @export
#'
#' @examples
#' pdfvalues <- mvnpdf(x=matrix(seq(-3, 3, by = 0.1), nrow = 1), Log=FALSE)
#' plot(pdfvalues)
plot.mvnpdf <- function(x, ...) {
  plot(x$x, x$y, type = "l", ...)
}

⚠ ATTENTION ! Pour que cette mĂ©thode fasse bien ce qu’on veut quand on l’applique au rĂ©sultat de notre fonction mvnpdf(), il faut dĂ©clarer que ce rĂ©sultat est de classe mvnpdf.

Tester cette fonction, en exĂ©cutant l’exemple.
N’oubliez pas de rĂ©installer le package (“Install and Restart” ou Ctrl+Shift+B).

Consulter le contenu du dossier man et les modifications qui ont été apportées au fichier NAMESPACE.

Voici une proposition de solution : le fichier contient le code complet de la fonction mvnpdf() et de la méthode plot() associée.

Annexe 1.2 : soumettre son package au CRAN

Executer les 2 commandes suivantes : devtools::check() puis devtools::submit_cran().

Pour plus de détails, voir la procédure recommandée dans Wickham & Bryan (2023)3