plot of chunk
headband

Ce document est un document de travail listant toutes les étapes nécessaires pour créer un package R. Je l'ai construit pour pouvoir m'y référer moi-même la prochaine fois que je souhaiterai créer un package. Il s'adresse donc à un public certes exigeant (mon moi du futur!) mais avec des besoins bien spécifiques. Il ne se veut donc pas exhaustif, et les termes que j'y emploie peuvent être, si ce n'est approximatifs, du moins imagés. Cependant je le mets à disposition de quiconque le souhaite car il a le mérite de balayer l'ensemble d'un processus qui peut être a priori assez intimidant!

Créer un package R, pour quoi faire?

  • pour soi-même, parce que si l'on en fait pas des packages on a tendance à perdre les bénéfices des fonctions que l'on s'écrit pour soi-même: on oublie qu'on les a faites/on oublie où on les a mises/ on oublie comment on s'en sert etc.

  • pour les autres, parce que partager un package et la documentation associée, c'est plus facile que de partager des scripts épars, surtout si l'on s'adresse à des utilisateurs de R qui ne sont pas eux-mêmes développeurs.

Voici deux liens où je suis allée chercher des renseignements pour apprendre à créer un package:

Création et organisation d'un dépôt local pour le package

Création du dépôt local et de son squelette

Par exemple on peut créer un dossier monpackage sous l'emplacement par défaut pour dépôt sur GitHub:

C:\Users\lvaudor\Documents\GitHub\monpackage

Pour l'instant le dossier est vide. Pour créer l'architecture d'un package R (sous sa forme "source")

On aura besoin du package devtools (création et installation du package) et roxygen2 (création de la documentation):

if(!require(devtools)){install.packages("devtools")}
if(!require(roxygen2)){devtools::install_github("klutometis/roxygen")}

La commande suivante crée

  • un fichier DESCRIPTION,

  • un fichier NAMESPACE,

  • un dossier R (qui contiendra les scripts contenant eux-même les fonctions)

  • les fichiers .gitignore, .Rbuildignore, et un projet Rstudio

devtools::create("monpackage")

Modification du fichier DESCRIPTION

Le fichier DESCRIPTION doit être modifié. Il faut notamment préciser:

  • le titre et la description du package (ce qu'il fait)

  • l'auteur et les informations de contact

  • les dépendances: ("Depends: ") c'est ici qu'il faut préciser quels autres packages doivent être chargés

Ajout du dossier inst

On peut si besoin créer ce dossier et y placer les dossiers de scripts, d'images, ou tout autre fichier nécessaire à l'execution des fonctions du package.

Lors de l'installation du package (transformation du dossier source en dossier "librairie") les dossiers placés dans "inst" se retrouveront à la racine du dossier "librairie".

Pour ma part j'ai créé une fonction findmypath qui permet de retrouver (une fois le package installé) facilement le chemin de tout fichier "file" placé initialement dans inst/dir/:

findmypath=function(dir,file){
  path=system.file(dir,file,package="monpackage")
  return(path)
}

Si j'utilise les fonctions du package sans l'avoir installé et chargé au préalable je dois alors utiliser une autre fonction findmypath.

Organisation des scripts

Il faut ensuite déposer l'ensemble des scripts contenant les fonctions du package sous le dossier R.

On peut organiser les fonctions soit en les regroupant dans un script soit en faisant une fonction = un script.

Pour l'exemple on peut imaginer une fonction "mafonction" sauvegardée dans un script mafonction.R:

mafonction <- function(nom="toi"){
  print(paste0("Salut, ", nom, "!"))
}

Documentation

Fichiers d'aide associés aux fonctions

Il y a plusieurs manières de procéder pour générer la documentation. Pour ma part je préfère documenter les fonctions sous forme d'entêtes puis générer la documentation à l'aide des packages devtools+roxygen2.

Reprenons l'exemple de la fonction "mafonction". Voici le genre d'entête qui permet de générer la documentation associée à cette fonction:

#' Une fonction pour l'exemple
#'
#' Cette fonction ne sert a rien d'autre qu'a servir d'exemple.
#' @param nom (par defaut, "toi")
#' @keywords salutation
#' @export
#' @examples
#' mafonction("mec")
mafonction <- function(nom="toi"){
  print(paste0("Salut, ", nom, "!"))
}

La commande suivante génère les fichiers d'aide à partir des entêtes:

devtools::document("monpackage")

On peut réexecuter cette fonction (puis réinstaller le package) pour mettre à jour la documentation si l'on modifie les entêtes par la suite.

Création d'une vignette

Une vignette correspond à la documentation "informelle" d'un package (contrairement aux fichiers d'aide liés aux fonctions qui sont très "contraints" d'un point de vue formel). On peut générer la vignette de la manière suivante:

devtools::use_vignette("monpackage_vignette",pkg="monpackage")

On peut alors aller modifier le fichier vignettes/monpackage_vignette.Rmd et y inclure les explications, la biblio, et les exemples d'application du package.

Installation et utilisation du package en local:

Voici comment installer le package depuis le dépôt local vers une librairie.

devtools::install("monpackage")

Cette installation devrait créer un dossier monpackage dans vos librairies R. Chez moi, elle correspond à la création d'un dossier au chemin suivant: C:\Users\lvaudor\Documents\R\win-library\3.2\monpackage

L'architecture et les fichiers contenus dans ce dossier sont différents de ceux du dépôt local (ayant pour chemin, chez moi, C:\Users\lvaudor\Documents\GitHub\monpackage)

On peut dès lors charger le package et utiliser ses fonctions:

require(monpackage)
mafonction()

On a documenté mafonction, on peut donc afficher l'aide associée:

help(mafonction)

Depôt du package sur GitHub

Pour quoi faire?

  • pour partager ses scripts en ligne

  • pour gérer les versions du package (le système Git permet de garder trace de l'historique du package: quand et pourquoi tel ou tel script a été modifié, et de quelle manière).

Préalable

Création d'un dépôt distant ("repo") sur GitHub

On crée un repo (ou repository) sur GitHub qui sera un clone du dépôt local.

Il est utile d'associer un fichier README.Rd à ce repo de manière à renseigner l'utilisateur sur son contenu. Personnellement j'y indique en quelques lignes la fonction du package et de quelle manière l'utilisateur peut l'installer sur sa machine.

Commandes Git

On peut alors envoyer les scripts depuis le dépôt local vers le repo (dépôt distant).

On peut le faire soit depuis l'application GitHub Desktop, soit par ligne de commande avec Git Shell. J'utilise pour ma part Git Shell car je trouve que c'est la méthode la mieux documentée en ligne (et je débute en Git!).

Je me place dans le dossier qui m'intéresse (dossier local):

cd monpackage

Je crée le squelette git (un nouveau sous-dossier .git apparaît dans le dossier):

git init

J'ajoute l'ensemble des fichiers dans le répertoire local à la liste des fichiers "à suivre" par Git:

git add .

Je consigne les changements en effectuant un "commit":

git commit -m "Premier commit"

Il est nécessaire d'associer un court message (ici "Premier commit") à chaque fois pour que la commande soit effective.

Je vais ensuite lier mon dépôt local à mon dépôt distant ("origin" correspond à un nom court auquel il me sera facile de faire référence par la suite):

git remote add origin https://github.com/lvaudor/monpackage

Je peux alors pousser mes fichiers depuis le dépôt local vers le dépôt distant (ici vers la branche master):

git push -u origin master

Remarque:

Par la suite je pourrai utiliser davantage de fonctionnalités de Git, comme la possibilité de créer des branches.

Modifications ultérieures

Si je veux mettre à jour le dépôt distant par la suite je n'aurai pas besoin de réeffectuer toutes les tâches listées ci-dessus. Les lignes de commandes suivantes sont suffisantes:

cd monpackage
git add .
git commit -m "nouveau commit"
git push -u origin master

Installation du package depuis GitHub (dev version)

Dès lors que les fichiers relatifs à mon package ont été "poussés" de ma machine vers mon repo GitHub, n'importe qui peut installer le package depuis ce repo (cependant cela nécessite d'utiliser une fonction du package devtools):

if(!require(devtools)){install.packages("devtools")}
devtools::install_github("lvaudor/graphiT")

Attention lorsque l'on travaille sur une version qui n'est pas la toute dernière on peut avoir des difficultés à installer le package devtools.

Dépôt du package sous CRAN

En l'état actuel des choses mes packages peuvent être installés depuis GitHub (grâce à la fonction install_github du package devtools) et non par la commande habituelle install_packages (la plus simple pour installer un package).

Le dépôt d'un package sur CRAN a pour intérêt de permettre l'installation des packages par cette commande, et de garantir que le package est testé sur divers systèmes d'exploitation.

Le dépôt sur CRAN est quelque chose que je n'ai pour le moment pas encore testé... Document à compléter donc...