|
1 | | -## Application 3 (préparation) {.smaller} |
| 1 | +## Application 3 {.smaller} |
2 | 2 |
|
3 | 3 |
|
4 | 4 | ::: {.panel-tabset} |
5 | 5 |
|
6 | 6 | ## {{< fa brands github >}} |
7 | 7 |
|
8 | 8 | :::{.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 |
14 | 10 |
|
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. |
26 | 12 |
|
27 | 13 | ::: |
28 | 14 |
|
29 | 15 | ## {{< fa brands gitlab >}} insee |
30 | 16 |
|
31 | 17 | :::{.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 |
37 | 19 |
|
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. |
49 | 21 |
|
50 | 22 | ::: |
51 | 23 |
|
52 | 24 | ::: |
53 | 25 |
|
54 | 26 |
|
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 ? |
64 | 27 |
|
65 | | -Lecture du fichier avec `open_dataset` du _package_ `arrow` : |
| 28 | +## Application 3 {.smaller} |
66 | 29 |
|
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` |
75 | 32 |
|
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`. |
77 | 34 |
|
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). |
81 | 38 |
|
82 | 39 | ```{.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)) |
87 | 44 | ``` |
88 | 45 |
|
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). |
90 | 48 |
|
91 | 49 | ::: |
92 | 50 |
|
93 | | -_❓️ Quelle méthode retenir pour lire un `Parquet` avec `Arrow` ?_ |
| 51 | +_❓️ Quelle semble être la limite de la fonction `read_parquet` ?_ |
94 | 52 |
|
95 | | -## Application 3 (partie 2) {.smaller} |
| 53 | +## Application 3 {.smaller} |
96 | 54 |
|
97 | 55 | ::: {.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` |
99 | 57 |
|
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`. |
101 | 59 |
|
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) |
107 | 65 |
|
108 | 66 | ::: |
109 | 67 |
|
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` ?_ |
113 | 69 |
|
114 | | -## Application 3 (partie 3) {.smaller} |
| 70 | +## Application 3 {.smaller} |
115 | 71 |
|
116 | 72 | ::: {.callout-tip .nonincremental collapse="true" icon=false} |
117 | | -# Partie 3 : le Parquet partitionné |
| 73 | +# Partie 3 : Le `Parquet` partitionné |
118 | 74 |
|
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. |
120 | 76 |
|
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) |
129 | 83 |
|
130 | 84 | ::: |
131 | 85 |
|
132 | 86 | ::: {.nonincremental} |
133 | 87 |
|
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 :_ |
135 | 89 |
|
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 ?_ |
138 | 92 |
|
139 | 93 | ::: |
140 | 94 |
|
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 | | -{fig-align="center"} |
146 | | - |
147 | 95 |
|
148 | | -## Application 3 (partie 4) {.smaller} |
| 96 | +## Application 3 {.smaller} |
149 | 97 |
|
150 | 98 | :::{.callout-tip .nonincremental collapse="true" icon=false} |
151 | 99 | # Partie 4 : mise à jour de la chaîne de production |
152 | 100 |
|
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. |
181 | 102 |
|
| 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 |
182 | 105 |
|
183 | 106 | ::: |
184 | 107 |
|
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 ?_ |
188 | 109 |
|
189 | 110 |
|
190 | 111 | ## Checkpoint |
191 | 112 |
|
192 | 113 | ::: {.callout-caution .noincremental} |
193 | 114 | ## Checkpoint |
194 | 115 |
|
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) |
198 | 117 |
|
199 | 118 | {width=40% fig-align="center"} |
200 | 119 |
|
|
0 commit comments