Packages R: développer un package
Développer un package R avec RStudio
Ce tutoriel présente une démarche simple pour créer un package R moderne, documenté et testé. Il s'appuie sur les packages devtools, usethis, roxygen2 et testthat, qui constituent aujourd'hui l'écosystème de référence pour le développement de packages.
Prérequis
Installer les principaux outils de développement :
install.packages(c(
"devtools",
"usethis",
"roxygen2",
"testthat",
"pkgdown"
))
Il est également recommandé d'utiliser une version récente de R et RStudio.
1. Créer le package
Dans RStudio :
File > New Project > New Directory > R Package
Choisir :
- le nom du package (sans espaces ni caractères spéciaux)
- le dossier de création
RStudio crée automatiquement toute l'architecture du package.
On obtient notamment :
monpackage/
├── DESCRIPTION
├── NAMESPACE
├── R/
├── man/
└── monpackage.Rproj
Les rôles des principaux dossiers sont :
| Élément | Rôle |
|---|---|
| R/ | Contient toutes les fonctions du package |
| man/ | Documentation générée automatiquement |
| DESCRIPTION | Métadonnées et dépendances |
| NAMESPACE | Fonctions exportées et imports (généré automatiquement) |
Les fichiers NAMESPACE et man/ ne doivent généralement pas être modifiés à la main lorsqu'on utilise roxygen2.
2. Initialiser le package
Plusieurs outils peuvent être ajoutés immédiatement grâce à usethis.
Initialiser Git
usethis::use_git()
Éventuellement :
usethis::use_github()
pour créer directement un dépôt GitHub.
Ajouter une licence
Par exemple :
usethis::use_mit_license()
ou
usethis::use_gpl3_license()
Ajouter un README
usethis::use_readme_rmd()
3. Écrire des fonctions
Toutes les fonctions doivent être placées dans le dossier
R/
Par exemple :
R/calcul.R
contenant
addition <- function(x, y) {
x + y
}
Le nom du fichier est libre.
En revanche, les fonctions doivent toujours être dans le dossier R/.
4. Documenter avec Roxygen
Chaque fonction est précédée d'un bloc de commentaires.
Exemple :
#' Addition de deux nombres
#'
#' Additionne deux valeurs numériques.
#'
#' @param x premier nombre
#' @param y second nombre
#'
#' @return Une valeur numérique.
#'
#' @export
addition <- function(x, y) {
x + y
}
La documentation est ensuite générée automatiquement.
Dans RStudio :
Build > More > Document
ou
devtools::document()
Cette commande met à jour :
- le dossier
man/ - le fichier
NAMESPACE
Ces fichiers ne doivent normalement jamais être édités manuellement.
5. Gérer les dépendances
Éviter de modifier directement le fichier DESCRIPTION.
Préférer :
usethis::use_package("sf")
qui ajoute automatiquement
Imports:
sf
Pour une dépendance utilisée uniquement pendant le développement :
usethis::use_package("testthat", type = "Suggests")
Les types de dépendances les plus fréquents sont :
| Type | Utilisation |
|---|---|
| Imports | Package nécessaire au fonctionnement |
| Suggests | Package utilisé uniquement pour les tests ou des fonctionnalités optionnelles |
| Depends | Rarement utilisé aujourd'hui |
6. Charger le package pendant le développement
Pendant le développement, il n'est pas nécessaire de réinstaller le package après chaque modification.
Utiliser :
devtools::load_all()
Cette commande :
- charge toutes les fonctions ;
- recharge les données ;
- recharge les fichiers R modifiés ;
- permet de tester immédiatement les nouvelles fonctions.
C'est la commande utilisée en permanence pendant le développement.
7. Différence entre load_all() et le bouton Build
Ces deux actions ont des objectifs très différents.
devtools::load_all()
À utiliser pendant le développement.
Il simule un package installé mais sans construire réellement le package.
C'est très rapide.
Idéal pour :
- tester une fonction ;
- lancer une application Shiny ;
- faire du débogage.
Build
Le bouton Build réalise une véritable construction du package.
Selon l'action choisie, RStudio peut :
- documenter le package ;
- construire le fichier
.tar.gz; - installer le package ;
- vérifier (
R CMD check) qu'il respecte les standards CRAN.
Cette étape est plus lente mais permet de détecter des erreurs qui ne sont pas visibles avec load_all().
En pratique :
load_all()est utilisé des dizaines de fois par jour ;- Build est utilisé avant un commit important ou avant une diffusion.
8. Tester avec testthat
Initialiser les tests :
usethis::use_testthat()
Créer un premier fichier de test :
usethis::use_test("addition")
On obtient :
tests/testthat/test-addition.R
Exemple :
test_that("addition fonctionne", {
expect_equal(addition(2,3),5)
})
Exécuter tous les tests :
devtools::test()
Les tests permettent de vérifier que les fonctions continuent à produire les résultats attendus après des modifications.
9. Vérifier le package
Avant toute diffusion :
devtools::check()
Cette commande lance l'équivalent de :
R CMD check
Elle détecte notamment :
- erreurs ;
- avertissements ;
- notes ;
- documentation incomplète ;
- dépendances oubliées.
Un package destiné au partage devrait idéalement être exempt d'erreurs et d'avertissements.
10. Cas particulier : package contenant une application Shiny
Un package peut également contenir une application Shiny.
L'organisation est généralement la suivante :
R/
app.R
server.R
ui.R
modules/
utils.R
La fonction principale est par exemple :
run_app <- function() {
shiny::shinyApp(
ui = app_ui(),
server = app_server
)
}
Pendant le développement :
devtools::load_all()
run_app()
L'application utilise alors directement les fonctions du package en cours de développement.
Cette approche présente plusieurs avantages :
- les fonctions sont réutilisables ;
- les tests peuvent porter sur la logique métier indépendamment de Shiny ;
- le code est mieux organisé ;
- il devient facile de documenter et partager les fonctions.
Le package golem automatise une grande partie de cette architecture et constitue aujourd'hui une référence pour le développement d'applications Shiny complexes.
Bonnes pratiques
- Une fonction = une responsabilité.
- Documenter chaque fonction.
- Écrire des tests dès que possible.
- Utiliser
usethisplutôt que modifier les fichiers système à la main. - Utiliser
load_all()pendant le développement. - Lancer régulièrement
devtools::check(). - Versionner le projet avec Git.
En suivant cette démarche, on obtient un package facile à maintenir, documenté, testable et partageable.