## Installation de `usethis` et `devtools` ----
install.packages("usethis")
install.packages("devtools")
## Chargement des packages -----
library("usethis")
library("devtools")
L’objectif de cette session de travaux pratiques est de créer un research compendium, c.-à-d. un dossier de travail dont la structure est dérivée de celle d’un package . Vous allez découvrir les fichiers importants que nous vous recommandons d’ajouter à un projet de recherche. Vous allez aussi apprendre à écrire et documenter des fonctions .
Ce research compendium pourra, par exemple, servir de base pour votre travail de stage de Master 2.
NB. Ce tutoriel est une version simplifiée de l’exercice proposé par la formation “Recherche reproductible en écologie numérique” du CESAB.
Préambule
Afin de nous assister dans le développement de notre compendium, nous allons utiliser les packages usethis
et devtools
. Ils permettent de faciliter la création de certains fichiers/répertoires spécifiques à un compendium/package et d’utiliser à notre avantage les outils de développement de packages .
Installez les packages usethis
et devtools
depuis le CRAN :
Comme ces packages ne sont PAS nécessaires à l’execution de nos analyses (ils sont necessaires pour facilement créer un compendium et l’utiliser), nous les installons directement via la console et les chargeons entièrement via des appels à la fonction library()
.
Projet RStudio
Créez un nouveau projet RStudio : File > New Project > New Directory > New Project
- Choisissez un nom pour votre projet (sans signe de ponctuation)
- Sélectionnez l’emplacement où le nouveau projet sera créé
- Décochez toutes les autres cases
- Cochez la case “open in new session”
- Validez
Toujours travailler dans un Projet RStudio. Cela présente l’avantage de simplifier les chemins d’accès aux fichiers, notamment avec le package here
et sa fonction here()
dans les documents quarto. Les chemins d’accès seront toujours construits par rapport au dossier contenant le fichier .Rproj (racine du projet). On parle de chemin relatif. N’utilisez plus jamais la fonction setwd()
.
Fichier DESCRIPTION
Le fichier DESCRIPTION décrit les métadonnées du projet (titre, auteur, description, dépendances requises, etc.). C’est un des éléments essentiels d’un package . Ici, nous allons le détourner pour l’utiliser dans le cadre d’un compendium afin de bénéficier des outils de développement de packages . Ajoutons ce fichier avec la fonction use_description()
de usethis
.
## Ajout d'un fichier DESCRIPTION ----
::use_description(check_name = FALSE) usethis
Notez l’utilisation de check_name = FALSE
pour pallier les problèmes liés aux restrictions imposées par le CRAN sur les caractères possibles dans les noms de package.
Remplissez les champs utiles du fichier DESCRIPTION (Title, Authors@R). Vous pourrez remplir le champ “Description” lorsque vous saurez ce que vous faites.
Package: basic_compendium
Title: What the Package Does (One Line, Title Case)
Version: 0.0.0.9000
Authors@R:
person("First", "Last", , "first.last@example.com", role = c("aut", "cre"),
comment = c(ORCID = "YOUR-ORCID-ID"))
Description: What the package does (one paragraph).
License: `use_mit_license()`, `use_gpl3_license()` or friends to pick a
license
Encoding: UTF-8
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.3.1
Toujours ajouter un fichier DESCRIPTION à la racine du projet. Il permet de décrire le projet et ses auteurs. On le vera ensuite, il est indispensable pour lister les dépendances et les installer facilement.
Choix d’une Licence
Tout matériel partagé doit disposer d’une licence qui décrit ce qu’il est possible de faire avec. Ainsi, nous vous recommandons d’ajouter dès le début du projet une licence. Pour trouver une licence appropriée à votre projet, rendez-vous, par exemple, sur cette page : https://chooser-beta.creativecommons.org/.
Ajoutons la licence CC-by à notre projet avec la fonction usethis::use_ccby_license()
.
## Ajout d'une licence ----
::use_ccby_license() usethis
Notez qu’un nouveau fichier a été créé : LICENSE.md. Celui-ci détaille le contenu de la license choisie et sera lu par GitHub. Regardez aussi le contenu du fichier DESCRIPTION : la section License a été mise à jour.
Toujours ajouter une LICENSE à un projet qui sera rendu public. Visitez le site creativecommons.org pour choisir la plus appropriée à votre projet.
Si aucune licence n’est renseignée, votre projet est soumis aux règles de la No License : aucune permission n’est accordée. En d’autres termes, personne ne peut rien faire avec votre projet (pas d’utilisation, pas de modification, pas de partage, etc.).
Vous pouvez utiliser le site creativecommons.org pour obtenir le code source d’un badge reflétant la licence associée à votre compendium :
This work is licensed under CC BY 4.0
Ajout des répertoires
La prochaine étape consiste en la création de sous-répertoires, chacun ayant un rôle précis. Pour cela, utilisez la fonction dir.create()
.
## Ajout de sous-répertoires ----
<- c("data", "analyses", "R", "outputs")
reps lapply(reps, dir.create)
## Plus optimal que ----
# dir.create("data")
# dir.create("analyses")
# dir.create("R")
# dir.create("outputs")
Un bon Research compendium sera composé de différents sous-répertoires, chacun destiné à accueillir un certain type de fichiers. Par ex. :
le dossier data/ contiendra toutes les données brutes nécessaires au projet
le dossier outputs/ contiendra tous les résultats générés par les analyses
le dossier R/ ne contiendra que des fonctions (et leurs documentations)
le dossier analyses/ contiendra des scripts (ou des fichiers
.qmd
) qui appeleront les fonctions . Cette structure peut bien sûr être adaptée selon les besoins et la complexité du projet.
Données du compendium
Nous voilà fin prêt à travailler !
Nous utiliserons les données associées à l’étude de Williams, Zipkin, and Brodie (2024) dont voici le résumé :
Biogeographic history can lead to variation in biodiversity across regions, but it remains unclear how the degree of biogeographic isolation among communities may lead to differences in biodiversity. Biogeographic analyses generally treat regions as discrete units, but species assemblages differ in how much biogeographic history they share, just as species differ in how much evolutionary history they share. Here, we use a continuous measure of biogeographic distance, phylobetadiversity, to analyze the influence of biogeographic isolation on the taxonomic and functional diversity of global mammal and bird assemblages. On average, biodiversity is better predicted by environment than by isolation, especially for birds. However, mammals in deeply isolated regions are strongly influenced by isolation; mammal assemblages in Australia and Madagascar, for example, are much less diverse than predicted by environment alone and contain unique combinations of functional traits compared to other regions. Neotropical bat assemblages are far more functionally diverse than Paleotropical assemblages, reflecting the different trajectories of bat communities that have developed in isolation over tens of millions of years. Our results elucidate how long-lasting biogeographic barriers can lead to divergent diversity patterns, against the backdrop of environmental determinism that predominantly structures diversity across most of the world.
Les auteurs explorent les patrons globaux de dissimilarité phylogénétique des mammifères et des oiseaux au regard de l’environnement et des barrières biogéographiques. Ici nos analyses seront simples, elles visent à apprendre à construire et utiliser un compendium : nous allons rapidement explorer et cartographier les patrons de richesse spécifique pour les oiseaux et les mammifères à l’échelle globale en nous appuyant sur des données générées par Williams, Zipkin, and Brodie (2024).
Explorez rapidement les différentes sections de l’article et notez la présence des sections “Data availability” et “Code availability”. Les auteurs expliquent comment obtenir les données de base qui soutiennent leurs analyses et fournissent un lien vers le compendium associé à leur article.
Vous pouvez télécharger l’article (en open access) dans un sous répertoire references
pour y avoir accès facilement.
## Ajout de sous-répertoires ----
<- c("data", "analyses", "R", "outputs", "references")
reps lapply(reps, dir.create, showWarnings = FALSE)
Rendez-vous sur le dépôt github associé à l’article, explorez le README et le contenu. Pensez-vous pouvoir reproduire (facilement) les analyses de Williams, Zipkin, and Brodie (2024) à l’aide du compendium ?
Localisez les fichiers contenant les informations de richesse spécifique pour les oiseaux et les mammifères, notez leurs urls (attention à ne pas récupérer l’url de la page github qui affiche le fichier de données formaté, mais bien l’url du fichier -> bouton “Raw” en haut à droite).
Localisez aussi la couche “shapefile” qui correspond à la grille globale utilisée par les auteurs (attention le format “shp” est pénible, il y a plusieurs fichiers pour une seule couche vectorielle).
Implémentation des fonctions
Dans cette partie nous allons écrire 2 fonctions qui vont permettre de télécharger les fichiers de données hébergés sur le dépôt GitHub mentionné ci-dessus. Les fichiers seront enregistrés dans le dossier data/.
Création d’un fichier dans R/
Nous allons d’abord créer un fichier dans lequel nous écrirons le code source de nos fonctions.
Utilisez la fonction usethis::use_r()
pour créer un fichier functions.R
dans le dossier R/.
## Creation d'un fichier functions.R dans R/ --
::use_r("functions") usethis
Écriture des fonctions
Nous allons tout d’abord utiliser la fonction utils::download.file()
pour développer un code qui télécharge les données de richesse spécifique pour les oiseaux :
## File name ----
<- "bird_SR_cells.csv"
filename
## GitHub directory URL ----
<- "https://raw.githubusercontent.com/pwilliams0/Biogeography_and_global_diversity/refs/heads/main/Data/"
url
## Complete data source url ----
<- paste0(url, filename)
source_url
## Destination ----
<- "data"
dest_dir <- file.path(dest_dir, filename)
dest_path
## Download file ----
::download.file(url = source_url,
utilsdestfile = dest_path,
mode = "wb")
Ici nous déclarons le nom du fichier une seule fois et nous nous en servons pour créer l’url de la source des données
source_url
puis le chemin de destination du téléchargementdest_path
.Notez l’utilisation de
paste0()
pour l’url et defile.path
pour le chemin.Notez les commentaires et les indentations pour faciliter la lecture du code.
Pour “transformer” ce code en fonction nous allons l’encapsuler dans une déclaration de fonction , ici nous choisissons d’appeler la fonction dl_bird_sr
(pour “download bird species richness”) :
<- function() {
dl_bird_sr
## File name ----
<- "bird_SR_cells.csv"
filename
## GitHub directory URL ----
<- "https://raw.githubusercontent.com/pwilliams0/Biogeography_and_global_diversity/refs/heads/main/Data/"
url
## Complete file url ----
<- paste0(url, filename)
file_url
## Destination ----
<- "data"
dest_dir <- file.path(dest_dir, filename)
dest_path
## Download file ----
::download.file(url = file_url,
utilsdestfile = dest_path,
mode = "wb")
return(dest_path)
}
Une fonction est un objet comme les autres : on doit choisir son nom et on utilise le symbole d’attribution
<-
pour la déclarer.On utilise
return()
en fin de fonction pour retourner à l’utilisateur le résultat de la fonction. Ici on renvoit simplement le chemin de destination du téléchargement.Cette fonction n’a pas de paramètres (les parenthèses après
function
sont vides :<- function()
) : aucune information n’est nécessaire à son fonctionnement, la fonction télécharge les données pour les oiseaux, c’est tout.
Nous allons éxecuter notre fonction. Il faut d’abord la rendre disponible à l’utilisation.
pour cela il suffit de sélectioner sa définition et de la charger dans la console ctrl + entrée
.
Observez votre environnement, la fonction apparaît.
pour éxecuter la fonction il suffit de l’appeler dans la console :
dl_bird_sr()
Plutôt que de créer une deuxième fonction pour les mammifères nous allons modifier notre fonction pour avoir le choix entre plusieurs fichiers à télécharger (par exemple la richesse spécifique des oiseaux OU celle des mammifères).
Nous pouvons ajouter un paramètre à notre fonction. Le paramètre devra être renseigné à l’utilisation et sa valeur sera observée par le code de la fonction pour choisir quel fichier télécharger :
<- function(taxon) {
download_sr
## Inform about the data being downloaded
message("# ---- Downloading data for taxon: ", taxon)
## File name ----
if (taxon == "bird") filename <- "bird_SR_cells.csv"
if (taxon == "mamm") filename <- "mamm_SR_cells.csv"
## GitHub directory URL ----
<- "https://raw.githubusercontent.com/pwilliams0/Biogeography_and_global_diversity/refs/heads/main/Data/"
url
## Complete file url ----
<- paste0(url, filename)
file_url
## Destination ----
<- "data"
dest_dir <- file.path(dest_dir, filename)
dest_path
## Download file ----
::download.file(url = file_url,
utilsdestfile = dest_path,
mode = "wb")
return(dest_path)
}
Le nom de la fonction à changé …
On utilise
if ()
pour observer la valeur detaxon
renseignée par l’utilisateur.On utilise
==
pour tester l’égalité de 2 valeurs (!=
,<
,<=
,>
,>=
pour les autres comapraisons)On devra obligatoirement renseigner une valeur pour
taxon
lors de l’utilisation de la fonction. Sinon elle ne saura pas quel fichier télécharger :download_sr(taxon = "bird")
oudownload_sr(taxon = "mamm")
.Si on ne renseigne pas de valeur pour
taxon
ou si on reseigne une valeur différente de"bird"
ou"mamm"
, alors la fonction échoue et renvoit un message d’erreur. C’est une bonne chose !
Nous allons utiliser cette fonction plus tard, patience. En attendant nettoyons notre environnement car la bonne pratique est de charger les fonctions via un autre mécanisme que nous verrons plus tard.
rm(dl_bird_sr)
rm(download_sr)
Ecrivez des fonctions : on parle de Factorisation de code. Cela rendra votre code plus clair et plus facilement réutilisable. Placez toujours vos fonctions dans le dossier R/. Si vous utilisez des fonctions de dépendances externes, priviligiez cette écriture : package::fonction()
.
Écrivez une fonction dl_grid_cells
qui télécharge les fichiers “shapefile” de la grille globale.
<- function() {
dl_grid_cells
## Inform about the data being downloaded
message("# ---- Downloading the global grid")
## File names ----
<- "global_cells"
base_name <- c(".shp", ".shx", ".prj", ".dbf")
extensions <- paste0(base_name, extensions)
filenames
## GitHub directory URL ----
<- "https://raw.githubusercontent.com/pwilliams0/Biogeography_and_global_diversity/refs/heads/main/Data/"
url
## Complete file url ----
<- paste0(url, filenames)
file_urls
## Destination ----
<- "data"
dest_dir <- file.path(dest_dir, filenames)
dest_paths
## Download files ----
lapply(1:length(file_urls), function(i) {
message("-> ", base_name, extensions[i])
::download.file(url = file_urls[i],
utilsdestfile = dest_paths[i],
mode = "wb")
})
return(dest_paths)
}
Documentation
Maintenant, documentez votre/vos fonctions. C’est essentiel ! Pour cela, ajoutez un entête roxygen2 à vos fonctions. Cette syntaxe permet de documenter efficacement toute fonction .
Cette entête devra contenir (a minima) un titre, une description de chaque argument et le retour de la fonction.
Pour ajouter une entête roxygen2 à une fonction existente utlisez le menu Code > Insert Roxygen Skeleton.
Proposition de documentation (explorez)
#' Download Species Richness Data from Williams et al. 2024
#'
#' @param taxon (bird, mamm) the taxon for which data are downloaded
#'
#' @return the path to the downloaded file
#' @export
#'
<- function(taxon) { ... } download_sr
Optionnel : transpilez vos entêtes roxygen2 en fichiers .Rd
, seuls fichiers acceptés par pour documenter des fonctions. Ces fichiers .Rd
seront stockés dans le dossier man/.
## Génération de la doc ----
::document() devtools
L’aide de votre fonction est maintenant accessible via ?nom_fonction
.
Pensez aux autres (et au vous du futur) : documentez toujours votre code. Un code sans documentation est inutile. Utilisez les entêtes roxygen2 pour documenter vos fonctions , de simples commentaires pour documenter du code et des README
pour tout le reste.
Ajout des dépendances
Nos fonctions contiennent une dépendance à un package externe : utils
. Nous devons ajouter cette dépendance au fichier DESCRIPTION. Pour cela, nous allons utiliser la fonction usethis::use_package()
.
## Ajout de dépendances ----
::use_package("utils") usethis
Regardez le contenu du fichier DESCRIPTION. Par défaut, les packages requis sont listés sous le tag Imports. Ainsi, pour utiliser une fonction externe, il faudra l’appeler par package::fonction()
.
Si vous remplacer le tag Imports par Depends (?usethis::use_package()
), l’utilisation de la fonction devtools::load_all()
(voir plus bas) aura le même effet qu’un library()
et vous pourrez utiliser une fonction externe par fonction()
. C’est pratique pour les packages comme ggplot2
qui demande de nombreux appels à leurs fonctions pour réaliser une tâche.
## Ajout de dépendances ----
::use_package("ggplot2", type = "Depends") usethis
Listez toujours les packages requis dans le fichier DESCRIPTION. Ainsi, vous centralisez la liste des packages requis en un seul endroit.
En plus de la description du projet, il permet de lister les packages dont le projet dépend (tags Imports, Depends et Remotes). Avec ce fichier, plus besoin d’utiliser les fonctions install.packages()
et library()
.
Elles seront remplacées respectivement par devtools::install_deps()
et devtools::load_all()
.
J’ai besoin d’un nouveau package pour mes analyses, j’utilise toujours la procédure suivante :
je renseigne le package dans DESCRIPTION
j’execute
devtools::install_deps()
pour l’installerj’execute
devtools::load_all()
si j’utilise le champ “Depends” pour pouvoir utliser les fonctions du package (c’est mal), sinon j’utilise (toujours)package::fonction
.
Chargement du projet
Maintenant que notre compendium contient les éléments clés d’un package , c.-à-d. un fichier DESCRIPTION et un répertoire R/, nous pouvons utiliser les outils de développement des packages .
Les fonctions stockées dans le dossier R/ doivent être chargées avec la fonction devtools::load_all()
.
Cette fonction vient remplacer la fonction source()
(et la fonction library()
si les packages requis sont listés sous le tag Depends dans le fichier DESCRIPTION).
Après chaque modification d’une fonction , n’oubliez pas d’exécuter la fonction devtools::load_all()
.
Essayez ces deux fonctions.
## Installation des packages manquants ----
::install_deps()
devtools
## Chargement des packages et fonctions R ----
::load_all() devtools
Avec un dossier R/, vous pouvez utiliser la fonction devtools::load_all()
(chargement du projet) au lieu de library()
et source()
.
Appel aux fonctions
Jusqu’à présent, nous n’avons fait que définir des fonctions , mais nous ne les avons pas exécutées. Nous allons créer notre premier script dans le dossier analyses/. Celui-ci aura pour objectif d’appeler les fonctions définies précédemment pour télécharger les données.
Créez un nouveau script comme suit et éditez-le:
## Ajout d'un script R ----
::file.edit("analyses/01_download-data.R") utils
Proposition de contenu
# Download project raw data
#
# This script will download bird and mammal species richness data and the global grid
# from Williams et al. 2024. The files will be stored in `data/`.
#
# All functions used in the script have been developed for this project
# and can be found in the folder R/.
#
# Jane Doe <jane.doe@mail.me>
## Download SR data ----
### birds
download_sr("bird")
### mammals
download_sr("mamm")
## Download the global grid ----
dl_grid_cells()
Ajout d’un make.R
Afin d’automatiser notre projet, nous allons créer un script à la racine du projet. Nous l’appelerons, par convention, make.R. Celui-ci aura deux objectifs : 1) mettre en place le projet et 2) exécuter le projet. L’idée est de n’exécuter que ce script.
Créez un fichier make.R
à la racine du projet, placez-y une entête informative (le nom du compendium, le nom du fichier, votre email). Placez-y aussi les appels à devtools::install_deps()
pour installer les dépendences et devtools::load_all()
pour charger les fonctions que vous avez écrites.
## Ajout du make ----
::file.edit("make.R") utils
Contenu du make.R :
############################################################
# Basic Compendium
# francois.guilhaumon@ird.fr
# make.R
############################################################
# create directories ----
<- c("data", "analyses", "R", "outputs", "references")
reps lapply(reps, dir.create, showWarnings = FALSE)
# install the project dependencies listed in DESCRIPTION ----
::install_deps()
devtools
# load the project functions ----
::load_all() devtools
Un fichier make.R placé à la racine du projet permet de facilement mettre en place le projet (installation et chargement des packages requis et des fonctions ) et d’exécuter les différentes analyses de manière séquentielle (en sourçant les scripts qui appellent eux-même les fonctions ). C’est la porte d’entrée des analyses.
Finalement, ajoutez une ligne dans le fichier make.R qui permettra d’exécuter le script analyses/01_download-data.R
. Utilisez la fonction source()
pour cela.
############################################################
# Basic Compendium
# francois.guilhaumon@ird.fr
# make.R
############################################################
# create directories ----
<- c("data", "analyses", "R", "outputs", "references")
reps lapply(reps, dir.create, showWarnings = FALSE)
# install the project dependencies listed in DESCRIPTION ----
::install_deps()
devtools
# load the project functions ----
::load_all()
devtools
# download the project data ----
source("analyses/01_download-data.R")
Pour charger le projet et lancer les analyses, il suffit d’exécuter ce fichier make.R.
Le dossier analyses/ contient les scripts qui appellent les fonctions stockées dans le dossier R/. Il peut être ignoré dans le cas de simples analyses. Le code de l’analyse devra alors se trouver dans le make.R. A contrario, dans le cas d’analyses complexes, n’hésitez pas à multiplier les scripts (plutôt que d’avoir un seul gros script).
Ajout d’un README
Un jour, sûrement, vous verrez comment envoyer ce projet sur GitHub. Pour l’instant, nous allons légèrement anticiper et ajouter un README
à notre compendium. Ce sera la vitrine du projet. Les rôles d’un README
sont : 1) de présenter le projet, 2) d’expliquer son contenu, et 3) d’expliquer comment l’installer et l’utiliser.
Pour cela, vous allez ajouter un README.md
(fichier Markdown) à la racine de votre projet.
## Ajout d'un README ----
::file.edit("README.md") utils
# Basic Compendium
This commpendium takes as example the data associated with the study of Williams et al. (2024) to exemplify the structure of a basic research compendium:
> Williams, P.J., Zipkin, E.F. & Brodie, J.F. Deep biogeographic barriers explain divergent global vertebrate communities. Nat Commun 15, 2457 (2024). https://doi.org/10.1038/s41467-024-46757-z
## Usage
The analyses can be run by executing the commands in [make.R](make.R).
[ ... ]
N’oubliez pas aussi d’éditer les sections Title et Description du fichier DESCRIPTION.
Ajoutez un README à votre projet afin d’aider l’utilisateur à comprendre votre projet.
Encore des fonctions !
Maintenant que nous avons implémenté l’étape de téléchargement des données brutes. Nous pouvons les préparer en vue de futures analyses (étape de préparation des données). Nous disposons de deux fichiers csv qui nous renseignent sur la richesse spécifique dans chaque cellule de la grille globale, pour laquelle nous avons un shapefile. Pour importer et manipuler des données spatiales sous , le package sf
est très pertinent.
Créons une fonction qui
importe le shapefile de la grille globale (fonction
sf::st_read
),les données contenues dans les deux fichiers csv,
augmente la table d’attributs de la grille avec les données de richesse (fonction
dplyr::left_join
)
dans le fichier R/functions.R
.
<- function() {
gather_raw_data
## import the global grid ----
<- sf::st_read("data/global_cells.shp") #head(grid)
grid
## import richness data ----
<- read.csv("data/mamm_SR_cells.csv", row.names = 1)
mamm_sr names(mamm_sr)[names(mamm_sr) == "SR"] <- "mamm_sr"
<- read.csv("data/bird_SR_cells.csv", row.names = 1)
bird_sr names(bird_sr)[names(bird_sr) == "SR"] <- "bird_sr"
## add richness data to the grid
<- dplyr::left_join(grid, mamm_sr, by = "cell_id")
grid <- dplyr::left_join(grid, bird_sr, by = "cell_id")
grid
## il y a une majuscule a Realm !!
names(grid) <- tolower(names(grid))
## show the data ?
# plot(grid["mamm_sr"], border = NA)
# plot(grid["bird_sr"], border = NA)
grid
}
Documentez la fonction svp. Et mettez à jour le flux d’analyses, cette fois-ci en écrivant directement l’appel à la fonction basic_compendium::gather_raw_data()
dans le make.R … Comme cette étape d’assemblage des données ne contient qu’un seul appel à une fonction, on ne va pas créer un scripts dans analyses
rien que pour cela …
Crédits
L’exercice de la formation “Recherche reproductible en écologie numérique” du CESAB à été créé par Nicolas Casajus. Les illustrations ont été créées par Allison_horst.