Skip to content

Commit 9f93646

Browse files
authored
Merge pull request #29 from InseeFrLab/reprise-app3
Reprise App 3
2 parents 6e9d240 + b913acd commit 9f93646

File tree

4 files changed

+55
-171
lines changed

4 files changed

+55
-171
lines changed

R/checkpoints/application3/functions.R

Lines changed: 0 additions & 43 deletions
This file was deleted.

slides/_r_fundamentals.qmd

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,7 @@ Source : [eliocamp.github.io](https://eliocamp.github.io/reproducibility-with-r/
467467

468468
- [**Open-source**]{.orange}
469469

470-
## Utiliser des fichiers `Parquet`
470+
## Le format `Parquet`
471471

472472
- Deux *frameworks* de référence : [Arrow](https://book.utilitr.org/03_Fiches_thematiques/Fiche_arrow.html) et [DuckDB](https://book.utilitr.org/03_Fiches_thematiques/Fiche_duckdb.html)
473473
- Orientation [**fichier**]{.blue2} (`Arrow`) VS orientation [**BDD**]{.blue2} (`DuckDB`)
@@ -495,4 +495,12 @@ n_logements_depcom <- achille |>
495495
:::::
496496
:::
497497

498+
## Le format `Parquet`
499+
500+
- `Parquet` gagne sur tous les tableaux
501+
502+
. . .
503+
504+
![](img/tableau-perf-parquet.png){fig-align="center" height="500"}
505+
498506
{{< include applications_r/_application3.qmd >}}

slides/applications_r/_application3.qmd

Lines changed: 46 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -1,200 +1,119 @@
1-
## Application 3 (préparation) {.smaller}
1+
## Application 3 {.smaller}
22

33

44
::: {.panel-tabset}
55

66
## {{< fa brands github >}}
77

88
:::{.callout-tip .nonincremental collapse="true" icon=false}
9-
# Partie 0: préparation de l'exercice
10-
* Remplacer le contenu du script `R/get_data.R` en copiant-collant le contenu de [ce fichier](https://raw.githubusercontent.com/InseeFrLab/formation-bonnes-pratiques-git-R/refs/heads/main/R/checkpoints/application3/get_data.R). Exécuter ce script, il crée les fichiers nécessaires pour ces exercices.
11-
* Créer le script `R/benchmarking_functions.R` en copiant-collant le contenu de [ce fichier](https://raw.githubusercontent.com/InseeFrLab/formation-bonnes-pratiques-git-R/refs/heads/main/R/checkpoints/application3/benchmark_functions.R)
12-
* Créer un nouveau script `R` qui servira de bac à sable pour tester le format `Parquet`.
13-
* Créer les variables qui seront utiles pour les prochaines questions
9+
# Partie 0 : préparation
1410

15-
```{.r}
16-
columns_subset <- c(
17-
"REGION", "AGED", "ANAI", "CATL", "COUPLE",
18-
"SEXE", "SURF", "TP", "TRANS"
19-
)
20-
21-
filename_sample_csv <- "data/RPindividus_24.csv"
22-
filename_sample_parquet <- gsub("csv", "parquet", filename_sample_csv)
23-
filename_full_parquet <- gsub("_24", "", filename_sample_parquet)
24-
filename_full_csv <- gsub("parquet", "csv", filename_full_parquet)
25-
```
11+
* Remplacer le contenu du script `get_data.R` en copiant-collant le contenu de [ce fichier](https://raw.githubusercontent.com/InseeFrLab/formation-bonnes-pratiques-git-R/refs/heads/main/R/checkpoints/application3/get_data.R). Exécuter ce script, il importe les fichiers nécessaires pour cette application.
2612

2713
:::
2814

2915
## {{< fa brands gitlab >}} insee
3016

3117
:::{.callout-tip .nonincremental collapse="true" icon=false}
32-
# Partie 0: préparation de l'exercice
33-
* Remplacer le contenu du script `R/get_data.R` en copiant-collant le contenu de [ce fichier](https://raw.githubusercontent.com/InseeFrLab/formation-bonnes-pratiques-git-R/refs/heads/main/R/checkpoints/application3/get_data_ls3.R). Exécuter ce script, il crée les fichiers nécessaires pour ces exercices.
34-
* Créer le script `R/benchmarking_functions.R` en copiant-collant le contenu de [ce fichier](https://raw.githubusercontent.com/InseeFrLab/formation-bonnes-pratiques-git-R/refs/heads/main/R/checkpoints/application3/benchmark_functions.R)
35-
* Créer un nouveau script `R` qui servira de bac à sable pour tester le format `Parquet`.
36-
* Créer les variables qui seront utiles pour les prochaines questions
18+
# Partie 0 : préparation
3719

38-
```{.r}
39-
columns_subset <- c(
40-
"REGION", "AGED", "ANAI", "CATL", "COUPLE",
41-
"SEXE", "SURF", "TP", "TRANS"
42-
)
43-
44-
filename_sample_csv <- "data/RPindividus_24.csv"
45-
filename_sample_parquet <- gsub("csv", "parquet", filename_sample_csv)
46-
filename_full_parquet <- gsub("_24", "", filename_sample_parquet)
47-
filename_full_csv <- gsub("parquet", "csv", filename_full_parquet)
48-
```
20+
* Remplacer le contenu du script `get_data_ls3.R` en copiant-collant le contenu de [ce fichier](https://raw.githubusercontent.com/InseeFrLab/formation-bonnes-pratiques-git-R/refs/heads/main/R/checkpoints/application3/get_data_ls3.R). Exécuter ce script, il importe les fichiers nécessaires pour cette application.
4921

5022
:::
5123

5224
:::
5325

5426

55-
## Application 3 (partie 1) {.smaller}
56-
57-
::: {.callout-tip .nonincremental collapse="true" icon=false}
58-
## Partie 1 : Ouvrir un fichier `Parquet` et comprendre la logique de la lecture par bloc
59-
60-
Lecture du fichier avec `read_parquet` du _package_ `arrow` :
61-
62-
* Lire les données dont le chemin est stocké dans `filename_sample_parquet`. Pour mesurer le temps d'exécution, vous pouvez utiliser le squelette de code suggéré ci-dessous 👇️.
63-
* Faire la même chose mais cette fois, ajouter un filtre _ex post_ avec les colonnes (`select(any_of(columns_subset))`). Mesurez-vous une différence dans les temps de traitement ?
6427

65-
Lecture du fichier avec `open_dataset` du _package_ `arrow` :
28+
## Application 3 {.smaller}
6629

67-
* Cette fois, lire le fichier avec `open_dataset(filename_sample_parquet)`. Regarder la classe de cet objet.
68-
* Faire un `head(5)` après `open_dataset`. Observer l'objet obtenu (sortie en console, classe).
69-
* Maintenant regarder lorsque vous ajouter `collect()` après cette chaîne.
70-
* Mesurer le temps d'exécution de `open_dataset(filename_sample_parquet) %>% collect()`. Ajouter le filtre `select(any_of(columns_subset))`. Sa place influence-t-elle la vitesse de votre processus ?
71-
72-
Comparaison à la lecture d'un CSV :
73-
74-
* Utiliser `readr::read_csv` pour lire le fichier (chemin `filename_sample_csv`) avec et sans l'argument `col_select`. Avez-vous des gains de performance si vous ne lisez le fichier qu'avec ces colonnes ?
30+
:::{.callout-tip .nonincremental collapse="true" icon=false}
31+
# Partie 1 : Du `CSV` au `Parquet`
7532

76-
<details>
33+
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`.
7734

78-
<summary>
79-
Mesurer le temps d'exécution
80-
</summary>
35+
* Remplacer le contenu du script `get_data.R` en copiant-collant le contenu de [ce fichier](https://raw.githubusercontent.com/InseeFrLab/formation-bonnes-pratiques-git-R/refs/heads/main/R/checkpoints/application3/get_data.R). Exécuter ce script, il importe les fichiers nécessaires dans cette application
36+
* Pour effectuer les comparaisons de performance, on va utiliser la fonction [bench::mark](https://bench.r-lib.org/#benchmark). Analyser la documentation pour comprendre ce que la fonction attend en entrée.
37+
* La requête suivante permet de calculer les données pour construire une pyramide des âges sur un département donné, à partir du fichier `CSV` du recensement. Encapsuler la requête dans une fonction `req_csv` (sans argument).
8138

8239
```{.r}
83-
start_time <- Sys.time()
84-
# lecture du fichier ici
85-
end_time <- Sys.time()
86-
diff_time <- end_time - start_time
40+
res <- readr::read_csv("data/RPindividus_24.csv") %>%
41+
filter(DEPT == "36") %>%
42+
group_by(AGED, DEPT) %>%
43+
summarise(n_indiv = sum(IPONDI))
8744
```
8845

89-
</details>
46+
* Sur le même modèle, construire une fonction `req_read_parquet` basée cette fois sur le fichier `data/RPindividus_24.parquet` chargé avec la fonction [read_parquet](https://arrow.apache.org/docs/r/reference/read_parquet.html) d'`Arrow`
47+
* Comparer les performances (temps d'exécution et allocation mémoire) de ces deux méthodes grâce à la fonction [bench::mark](https://bench.r-lib.org/#benchmark), à laquelle on passera les paramètres `iterations = 1` (comparaison à partir d'une seule itération) et `check = FALSE` (autorise les outputs des deux fonctions à être différents).
9048

9149
:::
9250

93-
_❓️ Quelle méthode retenir pour lire un `Parquet` avec `Arrow` ?_
51+
_❓️ Quelle semble être la limite de la fonction `read_parquet` ?_
9452

95-
## Application 3 (partie 2) {.smaller}
53+
## Application 3 {.smaller}
9654

9755
::: {.callout-tip .nonincremental collapse="true" icon=false}
98-
## Partie 2 : Un format léger et efficace
56+
## Partie 2 : Exploiter la *lazy evaluation* et les optimisations d'`Arrow`
9957

100-
Dans cet exercice, vous devrez utiliser `open_dataset` pour lire les `Parquet`.
58+
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`.
10159

102-
* Observer l'espace disque de chaque fichier par le biais de l'explorateur de fichiers
103-
* Mesurer le temps d'exécution de la lecture du fichier dont le chemin est stocké dans la variable `filename_full_parquet`.
104-
+ Faire ceci avec et sans le filtre des colonnes[^csv].
105-
+ La croissance du temps de traitement vous apparaît-elle énorme ?
106-
* Ajouter après cette étape de lecture `filter(REGION == 24)`. Comprenez-vous pourquoi vous ne bénéficiez pas de gain de performance ?
60+
* Utiliser la fonction [arrow::open_dataset](https://arrow.apache.org/docs/r/reference/open_dataset.html) pour ouvrir le fichier `data/RPindividus_24.parquet`. Regarder la classe de l'objet obtenu.
61+
* Afficher les 5 premières lignes de la table avec la fonction `head()`. Observer l'objet obtenu (sortie en console, classe).
62+
* Ajouter une étape `collect()` à la fin de cette chaîne. Comprenez-vous la différence ?
63+
* Construire une fonction `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_dataset](https://arrow.apache.org/docs/r/reference/open_dataset.html)
64+
* Comparer les performances (temps d'exécution et allocation mémoire) des trois méthodes (`CSV`, `read_parquet` et `open_dataset`) grâce à la fonction [bench::mark](https://bench.r-lib.org/#benchmark)
10765

10866
:::
10967

110-
_❓️ Dans quel ordre sont faits les filtres par `Arrow` ?_
111-
112-
[^csv]: Ne pas faire ceci maintenant avec le CSV, le _benchmark_ arrive prochainement.
68+
_❓️ Quelle méthode retenir pour lire un `Parquet` avec `Arrow` ?_
11369

114-
## Application 3 (partie 3) {.smaller}
70+
## Application 3 {.smaller}
11571

11672
::: {.callout-tip .nonincremental collapse="true" icon=false}
117-
# Partie 3 : le Parquet partitionné
73+
# Partie 3 : Le `Parquet` partitionné
11874

119-
* Utiliser le code ci-dessous pour partitionner le fichier `Parquet` par _"REGION"_ et _"DEPT"_
75+
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.
12076

121-
```{.r}
122-
open_dataset(filename_full_parquet) %>%
123-
group_by(REGION, DEPT) %>%
124-
write_dataset("./data/RPindividus")
125-
```
126-
127-
* Observer l'arborescence de fichiers
128-
* Utiliser `Arrow` pour lire les données de la Corse du Sud (code région 94, code département 2A) à partir de ce fichier partitionné
77+
* Parcourir la documentation de la fonction [arrow::write_dataset](https://arrow.apache.org/docs/r/reference/write_dataset.html) pour comprendre comment spécifier la clé de partitionnement d'un fichier `Parquet`. Plusieurs méthodes sont possibles !
78+
* Dans une même chaîne, importer la table individus complète du recensement `data/RPindividus.parquet` avec la fonction [arrow::open_dataset](https://arrow.apache.org/docs/r/reference/open_dataset.html) et l'exporter en une table `data/RPindividus_dept.parquet` partitionnée par le département (`DEPT`)
79+
* Observer l'arborescence de fichiers de la table exportée
80+
* Modifier la fonction `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'échantillon
81+
* Construire une fonction `req_open_dataset_part` sur le modèle de `req_open_dataset`, qui importe cette fois les données partitionnées `data/RPindividus_dept.parquet`. Ne pas oublier de spécifier le paramètre `hive_style = TRUE`.
82+
* Comparer les performances (temps d'exécution et allocation mémoire) des deux méthodes grâce à la fonction [bench::mark](https://bench.r-lib.org/#benchmark)
12983

13084
:::
13185

13286
::: {.nonincremental}
13387

134-
_❓️ Imaginons que les utilisateurs voudraient aussi se restreindre à certains types de ménages en fonction de caractéristiques :_
88+
_❓️ Dans le cadre d'une mise à disposition de données en `Parquet` auprès d'utilisateurs :_
13589

136-
* _Que faudrait-il faire ?_
137-
* _Quelle est la limite ?_
90+
* _Comment bien choisir la/les clé(s) de partitionnement ?_
91+
* _Quelle est la limite à garder en tête ?_
13892

13993
:::
14094

141-
## Application 3 (partie 3) {.smaller}
142-
143-
Quand on généralise cette démarche de _benchmark_, on obtient le tableau de performance suivant
144-
145-
![](img/tableau-perf-parquet.png){fig-align="center"}
146-
14795

148-
## Application 3 (partie 4) {.smaller}
96+
## Application 3 {.smaller}
14997

15098
:::{.callout-tip .nonincremental collapse="true" icon=false}
15199
# Partie 4 : mise à jour de la chaîne de production
152100

153-
Nous allons mettre à jour les données utilisées pour notre chaîne de production :
154-
155-
* Lire les données à partir du morceau de code proposé
156-
* Vérifier que le code tourne de A à Z et changer celui-ci marginalement si ce n'est pas le cas
157-
158-
<details>
159-
160-
<summary>
161-
Modification du code pour l'import de données
162-
</summary>
163-
164-
```{.r}
165-
columns_subset <- c(
166-
"REGION", "AGED", "ANAI", "CATL", "COUPLE",
167-
"SEXE", "SURF", "TP", "TRANS", "IPONDI"
168-
)
169-
170-
df <- open_dataset(
171-
"./data/RPindividus",
172-
hive_style = TRUE
173-
) %>%
174-
filter(REGION == 24) %>%
175-
select(any_of(columns_subset)) %>%
176-
collect()
177-
178-
```
179-
180-
</details>
101+
Convaincus par ce comparatif, nous allons maintenant mettre à jour le format des données utilisées pour notre chaîne de production.
181102

103+
* Modifier le script `script.R` pour importer les données d'entrée de votre chaîne à partir de la table `Parquet` partitionnée par département
104+
* Vérifier que le code tourne de A à Z et l'adapter si ce n'est pas le cas
182105

183106
:::
184107

185-
186-
187-
_❓️ Cette mise à jour des données utilisées vous est-elle apparue plus simple que les changements de l'application 1 ?_
108+
_❓️ Cette mise à jour des données utilisées en source de la chaîne de production vous semble-t-elle complexe ? Pourquoi ?_
188109

189110

190111
## Checkpoint
191112

192113
::: {.callout-caution .noincremental}
193114
## Checkpoint
194115

195-
* Le script [`main.R`](https://raw.githubusercontent.com/InseeFrLab/formation-bonnes-pratiques-git-R/refs/heads/main/R/checkpoints/application3/main.R)
196-
* Le script [`R/functions.R`](https://raw.githubusercontent.com/InseeFrLab/formation-bonnes-pratiques-git-R/refs/heads/main/R/checkpoints/application3/functions.R)
197-
116+
* Le script [`script.R`](https://raw.githubusercontent.com/InseeFrLab/formation-bonnes-pratiques-git-R/refs/heads/main/R/checkpoints/application3/script.R)
198117

199118
![](checkpoint.jpg){width=40% fig-align="center"}
200119

0 commit comments

Comments
 (0)