Une formation aux bonnes pratiques avec Git et R
R
et Git
Retour à la page d’accueil pour explorer les autres versions
L’activité du statisticien / datascientist tend à se rapprocher de celle du développeur :
Source : Peng R., Reproducible Research in Computational Science, Science (2011)
Git
est un standard atteignable et efficientNote
Quel socle de bonnes pratiques pour les projets statistiques en R
?
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
Git
L’utilisation de Git
nécessite certaines notions préalables:
filesystem
Linux
Mais
RStudio
, 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
Préparation de l’environnement de travail
GitHub
GitHub
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
-> Services externes
-> Jeton d'accès personnel GitHub
GitHub
et le token❓ 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
(remote
) a-t-il été modifié ?
Interactions avec le dépôt distant
push
pour intégrer les changements locaux au projet distantGitHub
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
Lors de la création du projet sur GitHub
, nous avons demandé la création d’un fichier .gitignore
, qui se situe à la racine du projet. Il spécifie l’ensemble des fichiers qui seront toujours exclus de l’indexation faite par Git
.
*.pdf
et *.html
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
le dossier data/
❓ Question : que se passe-t-il lorsque l’on ajoute au .gitignore
des fichiers qui ont déjà été commit sur le projet Git ?
Git
facilite le travail collaboratif
Git
: modèle des branchesGitHub
/ GitLab
: Issues, Pull Requests, ForksSynchronisation des dépôts
Github
. Il/Elle donne des droits au(x) développeur(s) du projetRStudio
du Datalab<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 divergenceGit
crée un commit de merge
Git
effectue 3 étapes
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 true
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
, 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 ?
Utilisation de GitLab
Cette application vise à prendre en matin l’interface de GitLab
, éventuellement dans le cadre de votre environnement interne de travail.
GitLab
interne, il est plus confortable d’utiliser l’authentification via clé ssh
plutôt que l’authentification par token. Si vous n’en avez pas encore, générer une clé ssh
et ajouter celle-ci à votre compte GitLab
. Se référer pour cela à la documentation GitLab ou, idéalement, à la documentation de cette procédure sur votre environnement interne de travail.GitLab
pour retrouver les différents éléments que nous avons exploités sur l’interface de GitHub
.GitLab
et répéter les opérations des applications 1 et 2.Contribution à un projet open-source
GitHub
, faire un fork
du dépôt git-exo
<votre_nom>-<votre_prénom>
commit
avec les modifications de votre choix, puis pousser les changements sur votre fork
Pull Request
(PR
) pour proposer d’intégrer vos changements sur la branche principale du dépôt git-exo
Issue
dans laquelle vous mentionnez la Pull Request
précédemment ouverte❓ 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 ?
R
utilitR
propose plusieurs chapitres sur Git
Git
Git
sur AUS
Git
!R
Améliorations graduelles dans l’échelle de la reproductibilité :
1️⃣ Qualité du code
2️⃣ Structure des projets
3️⃣ Formats de données
4️⃣ Environnements reproductibles
5️⃣ Pipelines de données
6️⃣ Publication reproductible
Préparation de l’environnement de travail
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
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 ?“Good coding style is like correct punctuation: you can manage without it, butitsuremakesthingseasiertoread”
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
.R
utilise par défaut la librairie chargée le plus récemmentpackage::fonction()
dplyr::filter()
Exemple
package1
et package2
contiennent chacun une fonction appelée superFonction
.package2
est chargé après package1
, alors superFonction
désigne par défaut la fonction de package2
.package1::superFonction
et package2::superFonction
R
R
Python
en 3A d’ENSAEPartie 1 : vérification du bon fonctionnement du code
Un code reproductible est avant toute chose un code fonctionnel ! Repérez les erreurs qui empêchent le script script.R
de s’exécuter correctement, et les corriger.
Partie 2 : premiers standards de qualité
R
lintr
et styler
.tidyverse
: lintr::use_lintr(type = "tidyverse")
script.R
: lintr::lint("script.R")
.
script.R
: 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.Partie 4 : (auto-)documentation du code
library
pour les mettre tous ensemble au début du script.# TITRE NIVEAU 1 ------------
et ## TITRE NIVEAU 2 ==========
Partie 5 : une meilleure gestion des secrets
secrets.yaml
où vous écrivez ce secret sous la forme key: value
. ⚠️ Attention : le package yaml
impose la création d’une ligne vierge à la fin du fichier pour être valide.script.R
, importer ce YAML (avec yaml::read_yaml("secrets.yaml")
) pour créer une variable api_token
ayant cette valeur..gitignore
le fichier secrets.yaml
et indiquer dans le README.md
de votre projet que les secrets sont stockés dans ce fichier. ⚠️ Attention : il ne faut pas committer secrets.yaml
car le jeton d’API est personnel et secret!2 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
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
README.md
, situé à la racine du projet, est à la fois la carte d’identité et la vitrine du projetREADME.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.script.R
, appeler en début de chaîne ces fonctions avec source("R/functions.R", encoding = "UTF-8")
.roxygen2
.script.R
.script.R
.Partie 2 : création d’un package
usethis::create_package()
R
du package un module stat.R
et y copier la fonction de statistique agrégéedevtools::load_all()
et vérifier que la fonction marche correctementDESCRIPTION
. En particulier, spécifier les dépendances nécessaires (Imports
) et facultatives (Suggests
)devtools::document()
. Où est-elle stockée et sous quel format ??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()
.RDS
, RData
, fst
, sas7bdat
, etc.).Manipulation des formats CSV et Parquet
individu_reg.csv
get_data.R
pour écrire les données au format Parquet
à l’aide de la fonction arrow::write_parquet
script.R
pour importer le fichier Parquet
à l’aide de la fonction arrow::read_parquet
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 ?
renv
renv
permet de créer des environnements reproductiblesrenv
enregistre les versions exactes des packages nécessaires au projetrenv
init
) de l’environnement local du projetsnapshot
) des versions des packages installésrestore
) d’un environnementrenv::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
renv.lock
!renv::restore()
: installe/désinstalle les packages nécessaires pour arriver à l’état spécifié dans le fichier renv.lock
Partie 1 : prise en main de la librairie renv
Installer le package renv
Taper dans la console renv::init()
, lire le message et accepter.
Observer les nouveaux élements qui sont apparus dans le projet.
Installer le package gt
et observer le message dans la console.
Dans la partie sur les statistiques descriptives d’âge, ajouter ce code :
stats_age <- df %>%
group_by(decennie = decennie_a_partir_annee(age)) %>%
summarise(n())
table_age <- gt::gt(stats_age) %>%
gt::tab_header(
title = "Distribution des âges dans notre population"
) %>%
gt::fmt_number(
columns = `n()`,
sep_mark = " ",
decimals = 0
) %>%
gt::cols_label(
decennie = "Tranche d'âge",
`n()` = "Population"
)
Partie 2 : faire un snapshot de l’environnement
renv::status()
et observer le message.renv::snapshot()
pour intégrer les nouveaux packages au lockfilescript.R
, .Rprofile
, renv.lock
et du dossier renv/
.Partie 3 : test de la portabilité (facultatif)
RStudio
sur le SSPCloud
renv::restore()
pour installer les packages nécessaires à l’exécution du projetscript.R
s’exécute correctement dans ce nouvel environnement.Docker
) apporte la solutionUne analyse de données ou une chaîne de production font intervenir des étapes standardisées
Ces étapes peuvent être formalisées sous forme d’un pipeline (direct acyclic graph)
targets
targets
est un framework de modélisation de pipelines spécifiquement dédié aux projets R
.targets
_targets.R
tar_visnetwork()
pour inspecter la structure du pipelinetar_make
pour exécuter la chaine de productionNote
Quand on part du chaine de traitement dans un fichier déja existant (script.R
par exemple), il faut faire la transition vers un fichier _targets.R
dont la structure est particulière.
script.R
_targets.R
R/functions.R
├── _targets.R
├── data
│ ├── raw
│ │ └── data.csv
├── R
│ └── functions.R
_targets.R
_targets.R
_targets.R
doit satisfaire plusieurs conditions :
targets
# _targets.R file
library(targets)
source("R/functions.R")
tar_option_set(packages = c("readr", "dplyr", "ggplot2"))
list(
tar_target(file, "data.csv", format = "file"),
tar_target(data, get_data(file)),
tar_target(model, fit_model(data)),
tar_target(plot, plot_model(model, data))
)
Note
Les fonctions get_data
, fit_model
et plot_model
sont définies dans 📁 R/functions.R
tar_visnetwork
permet de visualiser le pipelinetar_make
exécute le pipeline défini dans _targets.R
targets
saute automatiquement les étapes qui n’ont pas changéModélisation d’un projet sous forme de pipeline de données
targets
._targets.R
à la racine du dépôt, à partir de l’exemple dans la documentation._targets/
dans le .gitignore
.library
dans le fichier R/functions.R
et récupérer les noms des packages pour les mettre dans la section tar_option_set()
.R/functions.R
les fonctions read_yaml_secret
et read_from_parquet
:read_yaml_secret <- function(path, key) {
return(yaml::read_yaml(path)[[key]])
}
read_from_parquet <- function(path) {
df <- arrow::read_parquet(
path,
col_select = c(
"region", "aemm", "aged", "anai", "catl", "cs1", "cs2", "cs3",
"couple", "na38", "naf08", "pnai12", "sexe", "surf", "tp",
"trans", "ur"
)
)
return(df)
}
Modélisation d’un projet sous forme de pipeline de données (suite)
script.R
, créer les deux premières cibles (targets) dans _targets.R
:
file_token
et file_data
stockant les chemins de nos fichiers en entréeformat = 'file'
dans tar_target
_targets.R
:
token
et data
à partir, respectivement, de read_yaml_secret
et read_from_parquet
.data
, utiliser l’argument format = 'parquet'
dans tar_target
.R
la fonction tar_visnetwork()
.tar_make()
dans la console et observer le changement d’état avec tar_visnetwork()
.Modélisation d’un projet sous forme de pipeline de données (suite)
clean_data
et table_age
) qui automatisent la production du tableau gt
retraitement_donnees <- function(df){
df <- df %>%
mutate(aged = as.numeric(aged))
df$sexe <- df$sexe %>%
as.character() %>%
fct_recode(Homme = "1", Femme = "2")
return(df)
}
produce_table_age <- function(df){
stats_age <- df %>%
group_by(decennie = decennie_a_partir_annee(aged)) %>%
summarise(n())
table_age <- gt::gt(stats_age) %>%
gt::tab_header(
title = "Distribution des âges dans notre population"
) %>%
gt::fmt_number(
columns = `n()`,
sep_mark = " ",
decimals = 0
) %>%
gt::cols_label(
decennie = "Tranche d'âge",
`n()` = "Population"
)
return(table_age)
}
tar_visnetwork
puis exécuter tar_make()
. Analyser la sortie dans la console.tar_read(table_age)
, visualiser la table des âges produite.retraitement_donnees
pour la modifier:tar_visnetwork
puis exécuter tar_make()
pour lancer le pipeline. Analyser la sortie.:::
pdf
, html
, odt
, etc.)R Markdown
R Markdown
est un package R
qui permet de lier
Markdown
R
qui peut être exécuté et dont les sorties peuvent être intégrées au texteknitr
transforme le texte et les sorties R
en un document Markdown
standardpandoc
transforme le document .md
en un format de sortie standard (html
, pdf
, etc.)Quarto
Quarto
est le successeur de R Markdown
Quarto
supporte différents moteurs de calcul (knitr
, Jupyter
, Observable
..) ce qui le rend nativement multi-langage (R
, Python
, JavaScript
..)Introduction à la publication reproductible
File > New file > Quarto document
:
HTML
comme format de sortieUse visual markdown editor
report.qmd
.Introduction à la publication reproductible (suite)
R
qui importe le package targets
. Le contenu produit par ce chunk doit être caché, il est donc nécessaire d’utiliser une option.qmd
, importer l’objet table_age
via tar_load
et faire en sorte d’afficher ce tableau dans le documentRender
et vérifier que l’output correspond à celui attendu.Important
En supposant que ce document est à vocation d’une audience non technique, masquer le code ayant généré les sorties
Introduction à la publication reproductible (suite)
script.R
, nous avions créé un graphique p.png
.
R/functions.R
, créer une nouvelle target graph_part_hommes
dans _targets.R
.figure_part_homme_age <- function(df){
p <- df %>%
group_by(aged, sexe) %>%
summarise(SH_sexe = n()) %>%
group_by(aged) %>%
mutate(SH_sexe = SH_sexe / sum(SH_sexe)) %>%
filter(sexe == "Homme") %>%
ggplot() +
geom_bar(aes(x = aged, y = SH_sexe), stat = "identity") +
geom_point(
aes(x = aged, y = SH_sexe),
stat = "identity", color = "red") +
coord_cartesian(c(0, 100))
return(p)
}
tar_make
)tar_make()
dans votre document à la suite du library(targets)
Render
pour mettre à jour votre noteutilitR
R
ENSAE
Slack
grrr, très dynamiqueTchap
Langage RR
à l’Insee : le salon Tchap
Insee - Outils Stats v2Bonnes pratiques pour les projets statistiques (retour au site principal ; )