Une formation aux bonnes pratiques avec Git et R
R
et Git
(page d’accueil)Origine : communauté des développeurs logiciels
Constats :
Conséquence : un ensemble de règles informelles, conventionnellement acceptées comme produisant des logiciels fiables, évolutifs et maintenables
L’activité du statisticien / datascientist tend à se rapprocher de celle du développeur :
projets intenses en code
projets collaboratifs et de grande envergure
complexification des données et donc des infrastructures
déploiement d’applications pour valoriser les analyses
Source : Peng R., Reproducible Research in Computational Science, Science (2011)
Une reproductibilité parfaite est coûteuse
Git
est un standard atteignable et efficient
Note
Quel socle de bonnes pratiques pour les projets statistiques en ?
Un point de départ commun
Un point de départ commun
Une structuration de projet plus viable
Git
1️⃣ Le contrôle de version : pourquoi faire ?
2️⃣ Le contrôle de version avec Git
3️⃣ Le travail collaboratif avec Git
pour en finir avec ça :
ou ça :
ou encore ça :
prior <- read_csv(prior_path)
prior <- prior |>
select(id, proba_inter, proba_build, proba_rfl) |>
separate(id, into = c('nidt', 'grid_id'), sep = ":") |>
group_by(nidt) |>
mutate(
proba_build = proba_build/sum(proba_build),
proba_rfl = proba_rfl/sum(proba_rfl),
) |>
unite(col = "id", nidt, grid_id, sep = ":")
# Test
# prior_test <- prior |>
# mutate(
# proba_inter = round(proba_inter, 4)
# proba_build = round(proba_build, 4)
# proba_rfl = round(proba_rfl, 4)
# )
write_csv(prior_round, "~/prior.csv")
Pour arriver à ça :
Source : ThinkR
Un modèle distribué
Source : specbee.com
Qui permet l’expérimentation en toute sécurité
Source : lutece.paris.fr
Quel que soit l’environnement de travail
Avec des outils pour faciliter la collaboration
Une vitrine pour les projets et l’organisation
Construire et naviguer à travers l’historique de son projet
La collaboration rendue simple et efficace
Améliorer la reproductibilité de ses projets
Améliorer la visibilité de ses projets
Git
L’utilisation de Git
nécessite certaines notions préalables:
filesystem
Linux
Mais
Git
sont très informatifsRStudio
, Sublime Merge
, VS Code
) qui facilitent l’apprentissageGit
, GitHub
, GitLab
… quelles différences ?Git
est un logiciel ;RStudio
, VS Code
…)Git
, GitHub
, GitLab
… quelles différences ?GitHub
et GitLab
sont des forges logiciellesAstuce
GitHub
: utilisation pour les projets open-sourceGitLab
: utilisation pour les projets internesremote
)origin
Source : Git Documentation
Action | Commande |
---|---|
Cloner un projet | git clone [url-to-git-repo] |
Afficher les changements | git status |
Retrouver l’URL du dépôt distant | git remote -v |
Action | Commande |
---|---|
Ajouter des changements à l’index de Git (stage fixes) |
Un seul fichier : git add <file-name> Tous les fichiers déjà indexés : git add -u Tous les fichiers ⚠️ : git add -A |
Warning
La méthode git add -A
peut amener à suivre les modifications de fichiers qui ne devraient pas l’être (par exemple, des données).
Il est recommandé de bien réfléchir avant de l’utiliser (ou d’avoir un bon .gitignore
)
Action | Commande |
---|---|
Faire un commit |
git commit -m "message" |
Pousser les changements locaux vers le dépôt distant (branche master ) |
git push origin master |
Récupérer les changements sur le dépôt distant (branche master ) |
git pull origin master |
git clone https://github.com/username/projet.git
git clone git@github.com:username/projet.git
git clone https://gitlab.insee.fr/username_or_groupname/projet.git
git clone git@gitlab.insee.fr:username_or_groupname/projet.git
Préparation de l’environnement de travail
GitHub
GitHub
en incluant un fichier README
RStudio
. Dans l’onglet de configuration Git
du service, fixer la durée du Cache
pour le stockage des identifiants GitHub
à une valeur suffisamment élevéeRStudio
du Datalab
):
File
→ New project
→ Version Control
→ Git
GitHub
SSP Cloud
(ou un gestionnaire de mot de passe) :
Mon Compte
-> Git
-> Token d'accès personnel pour Forge Git
GitHub
et le tokenPréparation de l’environnement de travail
gitlab.insee.fr
RStudio
. Dans l’onglet de configuration Git
du service, fixer la durée du Cache
pour le stockage des identifiants Gitlab
à une valeur suffisamment élevéeRStudio
de la plateforme LS3
):
File
→ New project
→ Version Control
→ Git
gitlab.insee.fr
LS3
(ou un gestionnaire de mot de passe) :
Mon Compte
-> Git
-> Token d'accès personnel pour Forge Git
❓ Question : qu’est ce qui différencie le projet cloné d’un projet quelconque ?
Premiers commits
scripts
script1.R
et script2.R
, chacun contenant quelques commandes R
de votre choixRStudio
commit
, auquel on donnera un message descriptif pertinentscript1.R
et modifier le contenu du fichier script2.R
RStudio
RStudio
❓ Question : à ce stade, le dépôt du projet sur GitHub
/ Gitlab
(remote
) a-t-il été modifié ?
Interactions avec le dépôt distant
push
pour intégrer les changements locaux au projet distantGitHub
/ Gitlab
Que versionne-t-on ?
.html
, .pdf
, modèles…)Note
Pour définir des règles qui évitent de committer tel ou tel fichier, on utilise un fichier nommé .gitignore
.
Si on mélange du code et des éléments annexes (output, données…) dans un même dossier, il faut consacrer du temps à ce fichier.
Des modèles de .gitignore
existent sur internet, par exemple celui-ci pour les projets .
N’hésitez pas à y ajouter des règles conservatrices (par exemple *.csv
), comme cela est expliqué dans la documentation utilitR
.
Format des commits
Le fichier .gitignore
L’objectif de cette application est de créer le fichier .gitignore
, qui permet de spécifier l’ensemble des fichiers et/ou dossiers que l’on souhaite exclure de l’indexation faite par Git
. Il doit se situer à la racine du projet.
.gitignore
à la racine du projet (attention à ne pas ajouter d’extension au fichier, type .txt
)data
à la racine du projet et créer à l’intérieur de celui-ci un fichier data/raw.csv
avec une ligne de données quelconque.gitignore
qui exclue le dossier data/
, et vérifier que la règle fonctionne*.pdf
et *.html
, et vérifier que la règle fonctionne❓ Question : que se passe-t-il lorsque l’on ajoute au .gitignore
des fichiers qui ont déjà été commit sur le projet Git ?
Des fichiers .gitignore
standards
Dans cette application, nous avons généré le fichier .gitignore
manuellement. En pratique, il existe des .gitignore standards adaptés pour chaque langage de programmation, qui implémentent déjà de nombreuses règles pertinentes. Le mieux est donc de partir du .gitignore R pour tout nouveau projet R
, et de rajouter les règles spécifiques que l’on souhaite appliquer au projet.
Git
facilite le travail collaboratif
Git
: modèle des branchesGitHub
/ GitLab
: Issues, Pull Requests, ForksSynchronisation des dépôts
Github
/ Gitlab
. Il/Elle donne des droits au(x) développeur(s) du projetRStudio
<votre_nom>-<votre_prenom>.md
. Écrire dedans trois phrases de son choix sans ponctuation ni majuscules, puis commit
et push
les modificationspush
. C’est normal ! Le premier ayant fait un push
a modifié le dépôt commun ; les autres doivent intégrer ces modifications dans leur version locale (pull
) avant d’avoir le droit de proposer un changement.pull
renvoie également une erreur : Git
ne parvient pas à résoudre la divergence d’historique. Essayons de comprendre le problème et les solutions possibles.Git
résout le problème via un fast-forward merge
Git
ne peut pas résoudre de lui même la divergence
Deux stratégies possibles
Git
crée un commit de merge
Git
effectue 3 étapes
Avantage : l’historique reste linéaire
Attention : ne jamais rebase des commits déjà poussés sur un dépôt public (plus d’explications)
Synchronisation des dépôts
RStudio
ne permet pas (encore) de sélectionner la stratégie de pull
, on va préciser la configuration voulue via une commande passée dans le terminal
git config pull.rebase false
push
a été refusé, effectuer un pull
des modifications distantes via l’interface RStudio
push
de vos modifications localespush
leurs modifications locales❓ Question : que se serait-il passé si les différents membres du groupe avaient effectué leurs modifications sur un seul et même fichier ?
Résoudre les conflits
commit
et push
les modificationspull
préalable, les développeurs modifient également le contenu du fichier du mainteneur, puis commit
et push
les modificationspush
est rejeté pour la même raison que dans l’application précédente : les dépôts ne sont plus synchronisés, il faut pull
les changements distants au préalable. Mais cette fois, le pull
est également rejeté : il y a un conflit entre l’historique du projet distant et celui du projet local. Git
nous indique qu’il faut résoudre le conflit avant de pouvoir modifier l’historique du projet.RStudio
pour résoudre le conflit, en choisissant la version du fichier que vous souhaitez conserver, puis commit
/push
les modificationspush
. Les autres doivent répéter l’opération.❓ Question : comment limiter au maximum la survenue des conflits d’historique ?
Description plus détaillée : ici
Branches, issues et pull requests
Github
/ Gitlab
, chaque personne ouvre une Issue
sur le même dépôt que les applications précédentes, dans laquelle vous suggérez une modification à apporter à votre projetajout-authentification
)commit
avec les modifications de votre choix, puis pousser les changements sur une nouvelle branche du dépôt distantPull Request
(PR
) pour proposer d’intégrer vos changements sur la branche principale du dépôt distant. Spécifier que l’acceptation de la Pull Request
entraînera la fermeture automatique de l’Issue
associée en écrivant dans le corps de la PR
: close #N
où N
est le numéro de l’Issue
en questionPR
d’un autre membre de l’équipe, suite à quoi les différentes PR
peuvent être fusionnées❓ Question : quelle organisation pour merge dans la branche principale ?
Contribuer à un projet collaboratif
L’objectif de cette application est d’initier au mode de contribution à un dépôt collaboratif. Concrètement, vous allez ouvrir une Issue
sur un dépôt d’un autre développeur, en proposant des modifications. Puis, comme vous êtes très motivés, vous allez soumettre une Pull Request
qui propose une solution à l’Issue
.
Issue
sur le dépôt git-exo
en suggérant une modification à apporter au fichier README.md
GitHub
, faire un Fork
du dépôt git-exo
commit
avec les modifications de votre choix, puis pousser les changements sur votre Fork
Pull Request
(PR
) pour proposer d’intégrer vos changements dans la branche principale (main
) du dépôt git-exo
PR
, indiquez close #N
où N
est le numéro de votre Issue
. Comme ça, lorsque la PR
sera acceptée, l’Issue
initiale sera automatiquement fermée !❓ Question : pourquoi, avec un fork
, est-il très important de toujours effectuer une Pull Request
à partir d’une branche différente de la branche principale ?
La documentation utilitR
propose plusieurs chapitres sur Git
Cours Reproductibilité et bonnes pratiques pour les projets de data science de l’ENSAE
La Bible de l’usage de Git
R
1️⃣ Qualité du code
2️⃣ Structure des projets
3️⃣ Formats de données
4️⃣ Environnements reproductibles
5️⃣ R
en production
Préparation de l’environnement de travail
Github
en incluant un fichier README
et un .gitignore
(chercher le modèle R
dans les suggestions)RStudio
. Dans l’onglet de configuration Git
du service, fixer la durée du Cache
pour le stockage des identifiants GitHub
à une valeur suffisamment élevée (conseil: 36000)RStudio
du Datalab
):
File
→ New project
→ Version Control
→ Git
get_data.R
en copiant le contenu de ce fichier, puis l’exécuterscript.R
dans votre dépôt en copiant le contenu de ce fichier. Ne l’exécutez pas, c’est l’objet de l’exercice suivant..gitignore
. Que signifie-t-elle ?Git
a ajouté)Préparation de l’environnement de travail
GitLab
RStudio
. Dans l’onglet de configuration Git
du service, fixer la durée du Cache
pour le stockage des identifiants GitLab
à une valeur suffisamment élevéeRStudio
de LS3
):
File
→ New project
→ Version Control
→ Git
get_data.R
en copiant le contenu de ce fichier, puis l’exécuterscript.R
dans votre dépôt en copiant le contenu de ce fichier.gitignore
. Que signifie-t-elle ?D’une vision utilitariste du code à une vision du code comme outil de communication
Favoriser la lisibilité et la maintenabilité
Faciliter la réutilisation
Assurer la transparence méthodologique
1️⃣ Adopter les standards communautaires
2️⃣ Eviter la duplication de code
3️⃣ (Auto)-documenter son code
4️⃣ Isoler la configuration du code
“Good coding style is like correct punctuation: you can manage without it, butitsuremakesthingseasiertoread”
Respecter les conventions du langage dans lequel il est rédigé
Il existe un guide de référence pour bien coder en R
: le Tidyverse style guide.
Deux outils pratiques aident à respecter les standards :
Astuce
Dans le cas de :
Règle d’or
Il faut utiliser une fonction dès qu’on utilise une même portion de code plus de deux fois (don’t repeat yourself (DRY))
Règles pour écrire des fonctions pertinentes
Comment bien documenter un script ?
roxygen2
.L’auto-documentation en pratique
👎 La documentation pallie des mauvais nommages
# Utilise string si x est non manquant et non vide
if (!is.na(x) && nzchar(x)) {
use_string(x)
}
👍 Les nommages suffisent à comprendre le code
x_is_not_empty_string <- (!is.na(x) && nzchar(x))
if (x_is_not_empty_string) {
use_string(x)
}
README
ou des fichiers spécialisés (DESCRIPTION
ou renv.lock
)package::fonction()
Exemple
package1
et package2
contiennent chacun une fonction appelée super_fonction
.package2
est chargé après package1
, alors la fonction de package1
est automatiquement masquée et super_fonction
désigne par défaut la fonction de package2
.package1::superFonction
et package2::superFonction
Les secrets (mots de passe, tokens, etc.) sont des données sensibles
Quelques principes de sécurité essentiels
En pratique, deux recommendations selon l’usage
.gitignore
)R
Partie 1 : vérification du bon fonctionnement du code
Un code reproductible est avant toute chose un code fonctionnel !
script.R
de s’exécuter correctement, et les corriger.Session
> New Session
(ou Ctrl+Maj+F10) et refaire tourner le code de A à Z. Avez-vous une erreur ?Les pièges que cet exercice vous montre
Partie 2 : premiers standards de qualité
R
lintr
et styler
1.tidyverse
avec lintr::use_lintr(type = "tidyverse")
script.R
avec lintr::lint("script.R")
.
script.R
avec styler::style_file("script.R")
.styler
est un formatter peu intrusif.Partie 3 : une meilleure gestion des packages utilisés
package::fonction
pour les packages rarement utilisés dans le script.tidyverse
au complet est rarement utile. N’importer à la place que les packages effectivement utilisés dans le script.A propos du rm(list = ls())
(le supprimer !)
Cette commande est une mauvaise pratique.
On la retrouve encore dans trop de scripts car elle est utilisée pour de mauvaises raisons. Elle ne remets pas à 0 votre environnement: elle supprime juste les données de celui-ci, sans toucher au reste (packages importés, etc.).
Il vaut mieux gérer cela en changeant les options de puis redémarrer la session (CTRL+SHIFT+F10)
Partie 4 : (auto-)documentation du code
L’objectif de cet exercice est de remettre de l’ordre dans le script, cela le rendra bien plus lisible.
library
pour les mettre tous ensemble au début du script.# TITRE NIVEAU 1 ------------
et ## TITRE NIVEAU 2 ==========
Au passage, vous pouvez changer les noms de certains objets pour les rendre moins cryptiques (df3
n’est pas très clair).
Partie 5 : une meilleure gestion des secrets
Dans cette application, on va explorer deux manières possibles de gérer les secrets proprement.
Première possibilité : de manière interactive.
rstudioapi
, qui permet de demander à l’utilisateur d’entrer le secret à l’aide d’un pop-up interactif.⚠️ Cette solution nécessite l’exécution du code dans un environnement RStudio
, ce qui implique un usage en self.
Deuxième possibilité : via les variables d’environnement.
.Renviron
(voir cette fiche UtilitR pour plus d’info sur ce fichier) à la racine du projet et y ajouter une ligne JETON_API=xxx
en remplaçant xxx
par la valeur du jeton.api_token
à l’aide de la fonction Sys.getenv..Renviron
est bien renseigné dans le .gitignore
. Si ce n’est pas le cas, ajouter la règle et vérifier son bon fonctionnement, puis commit/push.Checkpoint
script.R
.gitignore
Favoriser la lisibilité et la maintenabilité
Construire des projets reproductibles
├── report.Rmd
├── correlation.png
├── data.csv
├── data2.csv
├── fig1.png
├── figure 2 (copy).png
├── report.pdf
├── partial data.csv
├── script.R
└── script_final.R
Source : eliocamp.github.io
Utiliser les projets RStudio
Organiser son projet en sous-dossiers
Donner des noms pertinents aux fichiers
Documenter son projet
(Faire de son projet un package)
Git
, on s’assure de toujours travailler dans un projet RStudio !├── data
│ ├── raw
│ │ ├── data.csv
│ │ └── data2.csv
│ └── derived
│ └── partial data.csv
├── R
| ├── script.R
│ ├── script_final.R
│ └── report.Rmd
└── output
├── fig1.png
├── figure 2 (copy).png
├── figure10.png
├── correlation.png
└── report.pdf
├── data
│ ├── raw
│ │ ├── dpe_logement_202103.csv
│ │ └── dpe_logement_202003.csv
│ └── derived
│ └── dpe_logement_merged_preprocessed.csv
├── R
| ├── preprocessing.R
│ ├── generate_plots.R
│ └── report.Rmd
└── output
├── histogram_energy_diagnostic.png
├── barplot_consumption_pcs.png
├── correlation_matrix.png
└── report.pdf
Le fichier README.md
, situé à la racine du projet, est à la fois la carte d’identité et la vitrine du projet
Idéalement, il contient :
Quelques modèles de README.md
complets :
devtools
et usethis
Partie 1 : modularisation du projet
R/functions.R
.fonction_de_stat_agregee
un nom plus pertinent et des noms d’arguments plus transparents.
fonction_de_stat_agregee
selon le standard roxygen
. Vous pouvez vous aider d’une IA assistante comme ChatGPT
, Claude
ou Copilot
, rien n’est sensible dans ce code (d’ailleurs rien de sensible ne doit être dans du code !).fonction_de_stat_agregee
dans cette documentation.script.R
, appeler en début de chaîne ces fonctions avec source("R/functions.R", encoding = "UTF-8")
.script.R
.get_data.R
et script.R
pour rendre plus intelligible la chaîne de production..gitignore
puis commit/push.Partie 2 : création d’un package (FACULTATIF)
usethis::create_package()
(par défaut dans le working directory)R
du package un module stat.R
et y copier la fonction de statistique agrégéeDESCRIPTION
. En particulier, spécifier les dépendances nécessaires (Imports
) et facultatives (Suggests
)devtools::document()
. Où est-elle stockée et sous quel format ?devtools::load_all()
et vérifier que la fonction marche correctement?ma_fonction
GitHub
est y mettre le code du package. Vérifier que le package peut être installé en local avec la fonction devtools::install_github()
.Checkpoint
script.R
R/functions.R
CSV
Parquet
Exemple: Recensement de la Population
Parquet
Parquet
L’art de bien partitionner
Partitionner par une/des variable(s) d’intérêt
Eviter de créer de nombreux petits (< 128Mo) fichiers
Parquet
Gestion native des méta-données
Interopérable
Open-source
Parquet
Arrow
) VS orientation BDD (DuckDB
)tidyverse
Exemple d’une requête lazy
n_logements_depcom <- achille |>
filter(dep %in% c("01", "02", "03")) |> # Récupère seulement les données nécessaires
select(idlogement, depcom) |> # Récupère seulement les colonnes nécessaires
group_by(depcom) |>
summarise(n_logements = n()) |>
collect() # Les calculs ne sont effectués qu'à cette étape !
Parquet
Parquet
gagne sur tous les tableauxPartie 0 : Préparation
get_data.R
en copiant-collant le contenu de ce fichier. Exécuter ce script, il importe les fichiers nécessaires pour cette application.Partie 0 : Préparation
get_data.R
en copiant-collant le contenu de ce fichier. Exécuter ce script, il importe les fichiers nécessaires pour cette application.Partie 1 : Du CSV
au Parquet
Tout au long de cette application, nous allons voir comment utiliser le format Parquet
de manière la plus efficiente. Afin de comparer les différents formats et méthodes d’utilisation, nous allons comparer le temps d’exécution et l’usage mémoire d’une requête standard. Commençons par comparer les formats CSV
et Parquet
.
benchmark_parquet.R
afin de réaliser les différentes comparaisons de performance de l’applicationCSV
du recensement. Encapsuler la requête dans une fonction req_csv
(sans argument).res <- readr::read_csv("data/RPindividus_24.csv") |>
filter(DEPT == "36") |>
group_by(AGED, DEPT) |>
summarise(n_indiv = sum(IPONDI))
req_read_parquet
basée cette fois sur le fichier data/RPindividus_24.parquet
chargé avec la fonction read_parquet d’Arrow
iterations = 1
(comparaison à partir d’une seule itération) et check = FALSE
(autorise les outputs des deux fonctions à être différents).❓️ Quelle semble être la limite de la fonction read_parquet
?
Partie 2 : Exploiter la lazy evaluation et les optimisations d’Arrow
La partie précédente a montré un gain de temps considérable du passage de CSV
à Parquet
. Néanmoins, l’utilisation mémoire était encore très élevée alors qu’on utilise de fait qu’une infime partie du fichier. Dans cette partie, on va voir comment utiliser la lazy evaluation et les optimisations du plan d’exécution effectuées par Arrow
pour exploiter pleinement la puissance du format Parquet
.
data/RPindividus_24.parquet
. Regarder la classe de l’objet obtenu.head()
. Observer l’objet obtenu (sortie en console, classe).collect()
à la fin de cette chaîne. Comprenez-vous la différence ?req_open_dataset
sur le modèle de celles de la partie précédente, qui importe cette fois les données avec la fonction arrow::open_datasetCSV
, read_parquet
et open_dataset
) grâce à la fonction bench::mark❓️ Quelle méthode retenir pour lire un Parquet
avec Arrow
?
Partie 3 : Le Parquet
partitionné
La lazy evaluation et les optimisations d’Arrow
apportent des gain de performance considérables. Mais on peut encore faire mieux ! Lorsqu’on sait qu’on va être amené à filter régulièrement les données selon une variable d’intérêt, on a tout intérêt à partitionner le fichier Parquet
selon cette variable.
Parquet
. Plusieurs méthodes sont possibles !data/RPindividus.parquet
avec la fonction arrow::open_dataset et l’exporter en une table data/RPindividus_partitionne.parquet
partitionnée par la région (REGION
) et le département (DEPT
)req_open_dataset
de la partie précédente pour partir de la table complète (non-partitionnée) data/RPindividus.parquet
au lieu de l’échantillonreq_open_dataset_partitionne
sur le modèle de req_open_dataset
, qui importe cette fois les données partitionnées data/RPindividus_partitionne.parquet
. Ne pas oublier de spécifier le paramètre hive_style = TRUE
.❓️ Dans le cadre d’une mise à disposition de données en Parquet
, comment bien choisir la/les clé(s) de partitionnement ? Quelle est la limite à garder en tête ?
Partie 4 : mise à jour de la chaîne de production
Convaincus par ce comparatif, nous allons maintenant mettre à jour le format des données utilisées pour notre chaîne de production.
script.R
pour importer les données d’entrée de votre chaîne à partir de la table Parquet
partitionnée data/RPindividus_partitionne.parquet
❓️ Cette mise à jour des données utilisées en source de la chaîne de production vous a-t-elle paru compliquée ? Pourquoi ?
Checkpoint
benchmark_parquet.R
script.R
Imaginons la situation suivante :
R
sur mon posteQuels problèmes puis-je rencontrer au fil des projets ?
Est-il facile de partager un de mes projets ?
Version de R fixe, celle de l’installation système
Conflits de version : différents projets peuvent requérir différentes versions d’un même package.
Reproductibilité limitée : difficile de dire quel projet nécessite quel package.
Portabilité limitée : difficile de préciser dans un fichier les dépendances spécifiques à un projet.
renv
renv
permet de créer des environnements reproductibles
Isolation : chaque projet dispose de sa propre librairie de packages
Reproductibilité : renv
enregistre les versions exactes des packages nécessaires au projet
Portabilité: un tiers peut exécuter le projet avec les mêmes spécifications
renv
Initialisation (init
) de l’environnement local du projet
Développement du projet
Enregistrement (snapshot
) des versions des packages installés
Restauration (restore
) d’un environnement
renv::init()
dans un projet RStudio crée :
renv
et le fichier .Rprofile
: activation automatique de l’environnementrenv.lock
: versions des packages installésrenv::status()
: indique les packages installés/supprimés par rapport au fichier renv.lock
renv::snapshot()
: enregistre les versions des packages installés dans le fichier renv.lock
Ne pas oublier de committer le fichier renv.lock
!
renv::restore()
: installe/désinstalle les packages nécessaires pour arriver à l’état spécifié dans le fichier renv.lock
Portabilité : un tiers peut recréer un environnement avec les mêmes spécifications
Partie 0 : Préparation
get_data.R
en copiant-collant le contenu de ce fichier. Exécuter ce script, il importe les fichiers nécessaires pour cette application.Partie 0 : Préparation
get_data.R
en copiant-collant le contenu de ce fichier. Exécuter ce script, il importe les fichiers nécessaires pour cette application.Partie 1 : Initialisation de l’environnement virtuel
sf
avec la fonction library()
.renv::init()
, lire le message et accepter.renv
a-t-il choisi les packages à ajouter au fichier renv.lock
?sf
. Comprenez-vous pourquoi celui-ci n’est plus disponible ?Partie 2 : Mise à jour du fichier renv.lock
script.R
, ajouter ce morceau de code, qui produit une carte à l’aide du package sf
renv::status()
et observer le messagesf
renv::status()
et observer le messagerenv.lock
avec la commande renv::snapshot()
renv::status()
et observer le messagePartie 3 : Test de portabilité
RStudio
et cloner à nouveau votre dépôt (sous un nom différent)renv::restore()
pour installer les packages nécessaires à l’exécution du projetscript.R
s’exécute correctement dans ce nouveau projetRStudio
et refaire les étapes précédentesCheckpoint
script.R
Limites des environnements virtuels :
La conteneurisation (ex : Docker
) apporte la solution
Intuition : au lieu de distribuer la recette pour recréer l’environnement, distribuer directement une “machine” qui contient tout l’environnement nécessaire au projet
R
en productionProduction statistique : chiffres, données, analyses produits par l’Insee / les SSM
Production informatique : processus maintenus par la DSI sur des infrastructures avec “garantie de service”
Self de production : processus maintenus par le métier sur une infrastructure “self”
Code de production : code respectant des standards de qualité qui le rendent efficace et maintenable
R
: un langage de production ?R
a émergé dans la communauté statistique
Penser son projet en termes de maintenabilité
Adopter les standards communautaires
“Le simple fait qu’un package fasse (ou semble faire) ce que vous voulez n’est pas une raison suffisante de l’utiliser, surtout si votre programme doit rester fonctionnel pendant une longue période. Déterminer si on peut utiliser un package revient à faire un arbitrage entre avantages et inconvénients, et à évaluer le risque d’instabilité d’un package.”
Git
“Les développeurs, quel que soit leur rôle, y voient de multiples avantages et un contexte dans lequel ils peuvent se former mutuellement, maintenir l’intégrité des bases de code de leurs équipes et créer, établir et faire évoluer des normes qui garantissent la lisibilité et la cohérence du code.”
Sadowski et al., Modern code review: a case study at google (2018)
Penser à l’expérience des utilisateurs finaux
Pour la mise à disposition de données : Parquet
Pour la documentation / publication : quarto
pdf
, odt
, html
)Exemple “2 en 1” : utiliser les données RP en Parquet
Penser son projet en termes de reproductibilité
Une tâche de production
Problème : le monde “autour du code” évolue
Le code / les données changent… et cassent
Spécifier les attendus
Outils : testthat et pointblank
Les dépendances changent (fonctionnalités, bugs, failles de sécurité…)
Arbitrage entre évolutivité et stabilité
On limite les risques en spécifiant l’environnement
R
…)
Concevoir sa chaîne comme un pipeline de données
Avantage supplémentaire : automatisation
Outil : intégration continue via GitHub
/ GitLab
Git
!OK, mais pourquoi 🤔 ?
Pourquoi migrer vers des infrastructures centralisées ?
Pourquoi migrer vers des infrastructures Kubernetes ?
Un point de départ commun
Un point de départ commun
Une structuration de projet plus viable
Un point de départ commun
Une structuration de projet plus viable
Facilitée par l’éco-système cloud
Qu’est-ce qui change 🤔 ?
Techniquement, tout… ou presque !
En pratique, Onyxia facilite la transition
Qu’est-ce qui change 🤔 ?
Monde conteneurisé : les traitements sont éphémères
S3
/ MinIO
)
Qu’est-ce qui change 🤔 ?
Evolution de la nature des traitements
Le débogage devient moins immédiat
Partie 1 : transition vers le stockage S3
Arrow
entre votre session R
et l’espace de stockage S3
bucket <- s3_bucket(bucket_formation, endpoint_override = Sys.getenv("AWS_S3_ENDPOINT"))
bucket_path <- bucket$path(paste0(path_within_bucket, "/RPindividus"))
main.R
, modifier les codes utilisant open_dataset
pour remplacer les chemins par la variable bucket_path
.df <- open_dataset(
bucket_path,
hive_style = TRUE
) %>%
filter(REGION == 24) %>%
select(any_of(columns_subset)) %>%
collect()
departements <- aws.s3::s3read_using(
FUN = sf::st_read,
object = "france.geojson",
bucket = paste0(bucket_formation, "/", path_within_bucket),
opts = list("region" = "")
)
Partie 2 : orchestrer sa chaîne de production
Au fil des chapitres précédents, nous avons appliqué un ensemble de bonnes pratiques à notre chaîne de production pour accroître sa qualité et sa maintenabilité. Néanmoins, celle-ci est encore sous la forme d’un unique script.
De manière générale, on a plutôt envie de modéliser les étapes d’une chaîne comme une série de fonctions, avec une fonction “cheffe d’orchestre” qui appelle les autres dans le bon ordre.
Modifier main.R
pour tenir compte de la modularisation (version + sspcloud ou version + LS3)
Passer la souris sur une des nouvelles fonctions et faire F1
targets
: un orchestrateur formel
On aurait pu également utiliser un orchestrateur dédié pour effectuer cette tâche, comme le package targets. Les plus curieux d’entre vous pourront aller voir le chapitre et les exercices qui lui étaient auparavant dédiés dans cette formation.
Partie 3 : ajout de contrôles de qualité des données
Un critère de qualité majeur d’une chaîne de production est sa robustesse. Naturellement, les données en entrée de la chaîne peuvent évoluer dans le temps. Afin de gérer au mieux les risques posés par de telles évolutions, on va ajouter des contrôles sur la qualité des données, en entrée et en sortie de la chaîne.
Partie 4 : tests unitaires et versionnage de la chaîne
Notre chaîne tourne à présent de manière robuste. Pour autant, ce n’est pas un objet fixe : on peut vouloir lui apporter des corrections ou des améliorations fonctionnelles. Et ces modifications peuvent, à leur tour, provoquer des nouvelles erreurs. Pour gérer ces risques, on va : - versionner la chaîne, afin de certifier le code qui la fait tourner sans erreur à un moment T - implémenter des tests unitaires, qui permettent de continuer à modifier la chaîne sans risquer de régressions
Partie 5 : un rapport reproductible pour documenter sa chaîne de production
Une bonne manière de favoriser à la fois la maintenabilité de sa chaîne et la réutilisationde ses produits est de documenter son fonctionnement. Le format quarto — successeur de R Markdown
— permet de reproduire facilement des rapports reproductibles, qui intègrent code et texte. En plus, ces rapports peuvent être facilement publiés en différents formats, du plus interactif (html
) aux plus classiques (pdf
, odt
, etc.).
Partie 6 : automatiser la mise à disposition
On dispose finalement d’une chaîne orchestrée, robuste et bien documentée. Afin d’en faire une chaîne vraiment intégrée de bout en bout, on va automatiser les étapes, de sorte à ce que les modifications apportées au projet se répércutent sur ses sorties. Pour cela, on va utiliser les outils de l’intégration continue proposés par GitHub
/ GitLab
.
Cours Reproductibilité et bonnes pratiques pour les projets de data science de l’ENSAE
Un cours complet (en anglais) sur la reproductibilité avec R
Conférence “R in production” d’Hadley Wickham (en anglais)
Le cours (en construction) “R in production” d’Hadley Wickham (en anglais)
Des standards communautaires qui favorisent la reproductibilité et la maintenabilité
De multiples outils pour simplifier leur mise en oeuvre
Un coût d’autant plus faible que l’on se place en amont
Le mouvement de modernisation du self est l’occasion d’une montée en compétence collective
Tchap
: Langage R (Tchap)Slack
: r-grrrBonnes pratiques pour les projets statistiques (retour au site principal ; )