-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathdata_manipulation.qmd
431 lines (327 loc) · 38.5 KB
/
data_manipulation.qmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
```{r}
#| echo: false
#| warning: false
#| message: false
library(flextable)
library(officer)
library(readr)
```
# Importation et manipulation d'une base de données
## Comprendre ce qu'est une base de données
Lorsqu'on souhaite répondre à une question, la démarche scientifique classique consiste à effectuer une série de mesures ou d'observations selon un protocole qui a été conçu en cohérence avec la question posée. En principe, ces mesures ou observations donnent lieu à l'obtention de valeurs. Ces valeurs peuvent être de forme numérique (e.g., les valeurs de taille de différents individus) ou de forme littérale (e.g., les valeurs de sexe de différents individus). Quelle que soit leur forme, les valeurs que l'on obtient dans un contexte qui est connu, comme dans le cas d'un protocole de mesures, ont un sens bien défini car elles sont associées à des choses que l'on a cherché à caractériser. Lorsqu'une valeur est porteuse d'un sens bien défini, on peut alors considérer qu'il s'agit d'une **donnée**.
Très souvent, pour répondre à une question, il est nécessaire d'acquérir plusieurs données qui seraient relatives à différentes choses que l'on a cherché à caractériser (e.g., la taille, la couleur, le poids, etc.), et qui seraient relatives également à différents individus chez qui l'on aurait souhaité caractériser ces choses. Afin de conduire les analyses qui permettraient de répondre à la question posée, il convient alors de répertorier toutes les données acquises dans un même document, et plus exactement dans un même fichier, qui serait la base de données, telle que présentée dans le @tbl-database.
```{r database}
#| echo: false
#| label: tbl-database
#| tbl-cap: "Exemple de base de données"
flextable::set_flextable_defaults(fonts_ignore = TRUE)
flextable(
data.frame(id = seq(1, 7, 1),
genre = c("H", "H", "H", "F", "F", "H", "H"),
taille = c(1.80, 1.93, 1.50, 1.95, 1.52, 1.87, 1.83),
nb_victoires = c(45, 90, 100, 43, 34, 67, 79),
niveau = c(1, 3, 4, 1, 2, 2, 3)
)) |>
theme_zebra() |>
hline_top(part = "all", border = fp_border(width = 1.5)) |>
hline_bottom(part = "all", border = fp_border(width = 1.5)) |>
align(align = "center", part = "all" ) |>
autofit()
```
La base de données prend donc la forme d’un tableau. Plusieurs principes sont à respecter en général lors de la création d’une base de données. Tout d’abord, les lignes de la base de données (qu'on appelle des **observations**) doivent correspondre le cas échéant à des individus bien identifiés. Ensuite, chaque colonne doit correspondre à une variable. L’ensemble des données contenues dans une même ligne correspond donc aux données relatives aux différentes variables qui auraient été obtenues chez un même individu (e.g., la taille, le poids, le sexe, etc.). Dans le cas d’études où l'on évaluerait une ou plusieurs variables plusieurs fois chez un même individu (i.e., à différentes moments, dans différentes conditions), il peut convenir de créer autant de lignes que de fois où les variables auraient été évaluées. Par exemple, le @tbl-databasePlusObs représente une base de données, certes très sommaire, qui contient des données d'individus dont on aurait évalué le poids deux fois, avant et après un programme de prise en charge. On remarque alors qu'il y a deux lignes par individu qui correspondent aux deux temps d'évaluation. La taille, elle, n'a été évaluée qu'une seule fois, en début de programme, mais pour éviter de laisser des cellules vides, la valeur initiale de la taille a été reproduite dans la seconde ligne.
```{r databasePlusObs}
#| echo: false
#| label: tbl-databasePlusObs
#| tbl-cap: "Organisation d'une base de données avec des mesures répétées"
flextable(
tibble::tribble(
~id, ~taille, ~temps_eval, ~poids,
#---/--------/------------/--------
"1", 1.75, "pre", 75,
"1", 1.75, "post", 73,
"2", 1.89, "pre", 90,
"2", 1.89, "post", 88
), cwidth = 2) |>
theme_zebra() |>
hline_top(part = "all", border = fp_border(width = 1.5)) |>
hline_bottom(part = "all", border = fp_border(width = 1.5)) |>
align(align = "center", part = "all" ) |>
autofit()
```
En principe, les données de la base qui ont été obtenues selon la même procédure d’acquisition représentent le même type de choses. Ces choses sont appelées des **variables** car elles varient selon les individus qui ont été étudiés et les conditions de mesure qui ont été mises en oeuvre (dans le cas où il y en aurait plusieurs). Lorsque ces choses ne sont pas censées varier, on parle de **constantes**. Une base de données peut comporter des variables de types différents (@tbl-typesVariables).
```{r typesVariables}
#| echo: false
#| label: tbl-typesVariables
#| tbl-cap: "Les différents types de variables"
flextable(
tibble::tribble(
~Type, ~Continue, ~"Discrète",
#---/--------/------------
"Quantitative", "", "",
"Intervalle", "X", "X",
"Ratio", "X", "X",
"Qualitative", "", "",
"Nominale", "", "X",
"Ordinale", "", "X"
), cwidth = 2) |>
theme_zebra() |>
hline_top(part = "all", border = fp_border(width = 1.5)) |>
hline_bottom(part = "all", border = fp_border(width = 1.5)) |>
bold(i = c(1, 4), j = 1) |>
align(j = "Type", align = "left", part = "all" ) |>
align(j = "Continue", align = "center", part = "all" ) |>
align(j = "Discrète", align = "center", part = "all" ) |>
autofit()
```
Les **variables quantitatives** (aussi dites numériques) comportent des nombres dont les différences entre eux ont un sens physique. Parmi les variables quantitatives, il est possible de plus précisément distinguer celles qui peuvent être associées seulement à une **échelle d'intervalles**, et celles qui peuvent être aussi associées à une **échelle de ratios**. Les variables étant associables seulement à une **échelle d'intervalles** ont la particularité de ne pas avoir de zéro naturel, de sorte que multiplier ou diviser les valeurs de cette variable n'aurait pas de sens. Un exemple de ce type de variables pourrait être la température exprimée en degrés Celsius. Avec cette variable, il est seulement possible de décrire le fait qu'il fait x degrés plus chaud ou plus froid à un endroit par rapport à un autre, mais cela n'aurait pas de sens de dire qu'il ferait deux fois plus chaud à un endroit où il y aurait 20°C par rapport à un endroit où il y aurait seulement 10°C. Il serait possible de le dire si 0°C correspondrait à "pas de température du tout", mais cela n'est évidemment pas le cas (cet exemple est repris de Danielle Navarro [-@navarroLearningStatistics2018]). En revanche, les variables pouvant être associées à une échelle de ratios présentent des valeurs qui, en plus de permettre de calculer des différences numériques qui ont un sens, peuvent être multipliées ou divisées, comme par exemple le temps de réaction à un stimulus donné.
En plus de la distinction échelle d'intervalles / échelle de ratios, les variables quantitatives peuvent être considérées comme étant soit **continues**, soit **discrètes**. Les variables quantitatives continues contiennent des nombres pouvant comporter théoriquement un nombre infini de décimales (e.g., la taille, le poids, etc.). Au contraire, les variables quantitatives discrètes ne peuvent contenir théoriquement que des nombres finis (e.g., le nombre de victoires sportives au cours d’une année). Certaines variables en théorie discrètes sont cependant souvent considérées comme continues tant le nombre de valeurs théoriquement possibles pour la variable est grand, tel que pour le nombre de globules blancs mesurés dans le sang [@labreucheDifferentsTypesVariables2010].
Les **variables qualitatives** (aussi dites catégorielles), elles, contiennent des valeurs désignant non pas des quantités mais des modalités. Ces variables sont donc forcément discrètes. Les modalités peuvent être exprimées sous forme littérale ou numérique. Parmi les variables qualitatives, on distingue celles qui sont **nominales** et celles qui sont **ordinales**. Les variables qualitatives nominales contiennent des modalités qui ne peuvent pas être ordonnées (e.g., les couleurs, les genres, etc.). Au contraire, les variables qualitatives ordinales contiennent des modalités qui peuvent être ordonnées (e.g., les niveaux de compétition sportive : départemental ; régional ; interrégional ; national ; international). Les variables qualitatives ordinales qui prendraient des valeurs numériques pour indiquer par exemple un niveau d'expertise (e.g., 1, 2, 3, et 4) se différencient des variables quantitatives discrètes par l’absence d’information sur la distance qui sépare les nombres de cette variable [@labreucheDifferentsTypesVariables2010].
## Fixer le répertoire de travail
Lorsque l'on souhaite réaliser l'analyse d'une base de données avec RStudio, il peut être utile et plus fonctionnel pour la suite de créer un dossier spécifique, sur l'ordinateur, relatif à son projet de travail. Ce dossier pourrait alors contenir au moins trois sous-dossiers appelés : "data" ; "out" ; et "R". L'idée ici est d'organiser tous les fichiers qui vont être utilisés et produits au cours du travail d'analyse. Le dossier "data" devrait ne contenir que les fichiers à analyser. Le dossier "out" ne devrait contenir que les fichiers qui sont exportés au cours des analyses réalisées. Et le dossier "R" ne devrait contenir que les fichiers .R qui servent à écrire et activer le code nécessaire aux analyses.
Une fois la structure de travail créée, il est préférable de faire en sorte que l'emplacement sur le PC du dossier du projet en cours soit le point de départ des chemins d'accès qui serviront à importer des données dans RStudio ou à exporter des fichiers à partir de RStudio. Pour faire cela, il faut créer un fichier .Rproj dans le dossier général du projet. Pour créer ce fichier, il suffit de suivre, dans RStudio, le chemin suivant : **Fichier > New Project... > Existing Directory > Sélectionner le chemin d'accès au dossier souhaité > Cliquer sur Create Project**. Un grand avantage de cette procédure est que cela permet de ne pas écrire, dans le script, le chemin d'accès complet d'un fichier sur son PC (de la racine au fichier lui-même) et ainsi de ne pas révéler dans le script des informations qui sont relativement privées. Cette solution est donc celle à préférer, surtout lorsqu'on envisage de partager son script avec d'autres collaborateurs qui risqueraient qui plus est d'être gênés par cette ligne de code d'accès au fichier qui ne leur servirait à rien, car le chemin d'accès au dossier de travail d'un collègue sera très probablement différent de celui des autres collaborateurs.
Une fois le projet bien configuré, il faut ensuite, dans RStudio, ouvrir un fichier **Script** où toutes les commandes seront écrites et enregistrables (chemin d'accès : **File > New File > R Script**). Une fois ouvert, il est possible d'enregistrer le script en appuyant sur **Ctrl+S**.
## Importer la base de données
Il existe plusieurs fonctions pour importer une base de données dans RStudio. La fonction `read_csv2()` du package `{readr}` permet d'importer par exemple des fichiers .csv qui, structurellement, séparent les données avec des points-virgules. C'est généralement le type de structure de fichier .csv que l'on obtient après avoir réalisé un export .csv à partir du logiciel Excel. Pour illustrer ici l'importation d'une base de données, il est d'abord possible d'en créer une dans le répertoire de travail actif, cela en exportant un tableau de données qui existe déjà avec le logiciel R. Le logiciel R dispose en effet d'un grand nombre de jeux de données différents que tout utilisateur peut consulter et manipuler. L'ensemble des jeux de données disponibles suite à l'installation par défaut de R est visible en lançant dans la Console la commande `data()`. Au fur et à mesure de la découverte des analyses montrées dans ce document, différents jeux de données seront utilisés en fonction des besoins. Pour le moment, il est possible d'utiliser le jeu de données qui s'appelle `iris`. Même si on ne le voit pas dans la fenêtre Environnement de RStudio, il est bel et bien là, disponible, prêt à être utilisé. Afin d'exporter ce jeu de données dans le répertoire de travail fixé au préalable, il est possible d'utiliser la fonction `write_csv2()` du package `{readr}`. Pour cela, il suffit d'utiliser le nom du jeu de données, puis d'indiquer entre guillemets le nom que l'on veut que le fichier exporté ait, tout en n'oubliant pas de mettre l'extension .csv à la fin du nom pour indiquer le format d'export, comme ci-dessous.
```{r write_csv2}
#| eval: false
library(readr)
write_csv2(x = iris, file = "out/iris.csv")
```
Si la commande ci-dessus est activée dans RStudio et que l'on jette ensuite un oeil dans le répertoire de travail (dossier "out"), il est alors possible d'y voir un nouveau fichier .csv du nom de `iris`. Maintenant qu'il existe une base de données dans le répertoire de travail actif, il est possible de concrétiser la procédure de son importation dans RStudio. Comme évoqué plus tôt dans ce document, il est intéressant, et en réalité nécessaire, d'assigner cette base de données à un nom pour pouvoir plus facilement manipuler le jeu de données par la suite. Ici, nous allons tout simplement associer ce nouvel objet au nom `iris`, tel que montré ci-dessous.
```{r read_csv2}
#| warning: false
iris <- read_csv2(file = "out/iris.csv")
```
Suite à l'activation de la commande, RStudio nous montre un message d'information sur la manière dont la fonction `read_csv2()` a configuré le jeu de données importé. Ce message apparaît car la fonction importe le jeu de données non pas sous la forme d'un *data frame* comme nous avons pu en créer auparavant, mais sous la forme d'un *tibble*, qui désigne un format de tableau que l'on ne peut obtenir qu'en passant par le biais de fonctions associées à l'ensemble de packages `{tidyverse}`. Pour comprendre l'intérêt d'un *tibble*, revenons au format classique d'un *data frame* à l'aide de la fonction `as.data.frame()`.
```{r as_data_frame}
iris <- as.data.frame(x = iris)
```
À présent, regardons ce qu'il se passe si on lance le nom `iris` dans la Console...
```{r iris}
iris
```
RStudio nous montre tout le jeu de données dans la Console, ce qui n'est pas très utile, d'autant plus que l'on peut perdre de vue la première ligne de titre lorsque le jeu de données contient beaucoup de lignes. Retournons donc au format *tibble* grâce à la fonction `as_tibble()` du package `{tibble}`, et voyons ce qu'il se passe lorsqu'on lance à nouveau le nom `iris` dans la Console.
```{r as_tibble}
library(tibble)
iris <- as_tibble(x = iris)
iris
```
Cette fois, RStudio n'affiche que les premières lignes du jeu de données, et il fournit en plus de cela des informations quant aux types de variables présentes dans le jeu de données, en-dessous de la ligne de titres. Maintenant que la base de données a été importée, il ne reste plus qu'à voir différentes fonctions pour pouvoir configurer la base de données telle qu'on la voudrait pour réaliser confortablement les analyses.
## Manipuler la base de données
### Vérifier le succès de l'importation de la base
Avant de débuter les analyses de la base de données, une bonne pratique est de vérifier si la base de données a été correctement importée avec RStudio. Une manière rapide de faire cela est de regarder les nombres d'observations (i.e., de lignes) et de variables (i.e., de colonnes) associés à l'objet crée lors de l'importation et visibles dans la fenêtre Environnement de RStudio, puis de cliquer sur le nom associé à l'objet. Lors de l'étape précédente, nous avons importé le jeu de données `iris` en l'appelant ainsi. Lorsque l'on cherche le nom `iris` dans la fenêtre Environnement, on peut voir que l'objet associé contient 150 observations et 5 variables, signes que la structure du jeu de données a été bien interprétée par R si l'on sait que ce sont effectivement les dimensions du jeu de données en question. Puis, lorsque l'on clique sur le nom `iris` dans la liste des noms montrés dans la fenêtre Environnement, RStudio ouvre un onglet qui contient les données. Il est alors possible de voir d'un simple coup d'oeil si les données sont bien présentes et organisées en lignes et en colonnes comme attendu.
### Vérifier et reconfigurer les types des variables de la base
Il convient de vérifier que les types des variables que RStudio a associés aux variables du jeu de données importé soient bien en accord avec ce qui était attendu. Pour vérifier les types des variables, il est possible d'utiliser la fonction `str()` avec le nom auquel on a associé la base de données.
```{r str}
str(iris)
```
Après avoir activé la commande contenant la fonction `str()`, la Console affiche plusieurs lignes d'information (cf. texte des résultats ci-dessus), avec à chaque fois le nom de la variable, son type, et les premières valeurs de la variable. Plusieurs termes peuvent être rencontrés selon la manière dont R a interprété les variables du jeu de données, notamment:
* num : désigne une variable quantitative continue ;
* int : dégine une variable quantitative discrète (avec des nombres entiers) ;
* Factor : désigne une variable qualitative ;
* chr : désigne une variable texte ;
* Date : désigne une variable date.
Le logiciel R s'appuie donc sur une classification des types de variables plus complexe que celle que nous avons présentée précédemment. On peut noter que les abbréviations montrées pour indiquer le type de variable en utilisant la fonction `str()` sont différentes de celles montrées lorsque l'on observe un jeu de données au format *tibble* dans la Console, mais ces différences reflètent en réalité principalement une divergence dans les stratégies d'écriture de l'information par les concepteurs des packages et des fonctions. En outre, si l'on veut déterminer le type d'une seule variable, ou plus globalement le type de l'objet qui nous intéresse, on peut utiliser la fonction `class()`. Utiliser un nom de variable avec cette fonction renverra le type de la variable, comme montré ci-dessous.
```{r class}
class(x = iris$Sepal.Length)
```
Lorsque le type d'une variable ne correspond pas à celui attendu après avoir importé la base de données dans RStudio, il peut être utile de se questionner sur les erreurs qui ont pu causer cela. Lorsque l'on obtient une variable de type *Factor* ou de type *chr* alors qu'une variable de type *num* étaient attendue, une cause possible est que l'importation du jeu de données a été réalisée avec une fonction d'importation mal configurée par rapport au contenu du jeu de données. Par exemple, il est possible que la fonction d'importation du jeu de données reconnaissait les nombres décimaux seulement lorsqu'ils avaient des points (e.g., 24.3) alors qu'en réalité les nombres décimaux étaient écrits avec des virgules (e.g., 24,3) dans la base de données. Une autre possibilité est que l'on n'ait pas indiqué, dans la fonction d'importation, sous quelle forme se présentaient les valeurs manquantes de la base de données. Par exemple, avec des valeurs manquantes qui seraient notées "NA" dans des variables numériques de la base de données, l'usage de certaines fonctions d'importation sans indiquer à l'intérieur que "NA" désigne "valeur manquante" conduira R à interpréter les variables concernées comme des variables *chr*. En utilisant la fonction `read_csv2()` du package `readr`, ces écueils sont plus facilement évités car les paramètres par défaut de la fonction nous facilitent le travail. En revanche, d'autres fonctions, plus anciennes, comme `read.csv2()` qui est une fonction de base de R, nécessitent plus de vigilance.
Lorsque la modification du type de la variable est nécessaire, une stratégie possible est de créer une variable portant exactement le même nom à partir de la variable initiale et à laquelle on applique une fonction capable d'imposer un certain type de variable. Il existe une fonction pour chaque type de variable à définir, notamment :
* La fonction `as.numeric()` pour obtenir un type de variable quantitative ;
* La fonction `as.factor()` pour un obtenir un type de variable qualitative ;
* La fonction `as.character()` pour un obtenir un type de variable texte ;
* La fonction `as.Date()` pour obtenir un type de variable date.
Par exemple, nous aurions pu vouloir faire en sorte que toutes les variables du jeu de données `iris` soient de type texte :
```{r as_character}
iris$Sepal.Length <- as.character(x = iris$Sepal.Length)
iris$Sepal.Width <- as.character(x = iris$Sepal.Width)
iris$Petal.Length <- as.character(x = iris$Petal.Length)
iris$Petal.Width <- as.character(x = iris$Petal.Width)
iris$Species <- as.character(x = iris$Species)
```
Remarquons qu'à chaque fois, le nom de variable écrit à gauche de la flèche d'assignation est exactement le même que celui qui est écrit à droite de la flèche d'assignation dans la fonction `as.character()`, ce qui implique que la création de la nouvelle variable entraîne la suppression et le remplacement de la précédente qui portait le même nom. Il est possible de vérifier la conséquence de ces commandes avec la fonction `str()`.
```{r verif_iris}
str(iris)
```
Cette stratégie de modification du type de la variable peut convenir lorsqu'il y a peu de variables à modifier. Cependant, lorsque la liste s'allonge, il peut être plus lisible, en matière de code, de fonctionner avec le symbole `|>` (qu'on appelle *pipe*) lorsque sa version de R est >= 4.1.0 (ou à défaut avec le symbole `%>%` du package `{magrittr}`), et la fonction `mutate()` du package `{dplyr}`.
```{r trans_type_var_iris}
#| warning: false
library(dplyr)
iris <-
iris |>
mutate(
Sepal.Length = as.numeric(x = Sepal.Length),
Sepal.Width = as.numeric(x = Sepal.Width),
Petal.Length = as.numeric(x = Petal.Length),
Petal.Width = as.numeric(x = Petal.Width),
Species = as.factor(x = Species)
)
```
Ici, le symbole `|>` permet d'indiquer à R que toutes les fonctions qui sont écrites après ce symbole s'appliquent à ce qui a été défini avant ce symbole. La fonction `mutate()`, dont nous reparlerons peu après, permet de créer de nouvelles variables dans le cadre de cette stratégie, soit en écrasant les anciennes variables si les anciens noms sont conservés, soit en créant de nouvelles variables si de nouveaux noms sont utilisés. Remarquons également qu'avec ce code, nous venons de créer un nouvel objet (en l'assignant à nouveau au nom `iris`) à partir de l'ancien objet, mais dont on a transformé les types des variables, perdant dans le même temps l'ancien objet.
On pourrait penser que le bloc de code montré juste ci-dessus n'est pas très satisfaisant car on répète finalement plusieurs fois une même action. Ici il y a seulement 5 variables dont 4 numériques donc cela peut encore aller. Mais s'il y en avait des dizaines et plus, devrait-on tout écrire comme ci-dessus ? Heureusement non. Pour éviter de réécrire la même action pour plusieurs variables, on peut utiliser la fonction `across()` du package `dplyr`, comme ceci :
```{r}
iris <-
iris |>
mutate(across(c(Sepal.Length:Petal.Width), as.numeric),
Species = as.factor(Species))
```
Enfin, si la fonction `str()` a l'intérêt de faire partie des fonctions de base de R, on peut noter l'existence de fonctions associées à d'autres packages qui sont particulièrement intéressantes pour découvrir un jeu de données. C'est le cas notamment de la fonction `skim()` du package `{skimr}` :
```{r}
library(skimr)
skim(iris)
```
### Sélectionner des variables avec `select()`
Certains jeux de données peuvent être très larges, c'est-à-dire qu'ils peuvent contenir beaucoup de colonnes, parfois inutiles, et qui peuvent être gênantes lorsque l'on veut avoir une vue claire du contenu du jeu de données. La fonction `select()` du package `{dplyr}` permet de sélectionner des colonnes facilement.
```{r select}
iris |>
dplyr::select(Petal.Length, Petal.Width, Species)
```
### Renommer des variables avec `rename()`
Il est possible que certains noms de variables ne soient pas clairs ou trop longs, voire mal écrits, ce qui peut être gênant pour écrire un code le plus lisible possible. La fonction `rename()` du package `{dplyr}` permet de gérer cela. Dans l'exemple ci-dessous, on observe que le nouveau nom doit être écrit à gauche du signe `=`, alors que l'ancien nom doit être écrit à droite du signe `=`. Si le nom d'origine contient au moins une espace entre deux mots, ou encore s'il contient des caractères spéciaux, il convient d'encadrer le nom à remplacer par des guillemets (" ").
```{r rename}
iris |>
rename(Sepal_long = Sepal.Length,
Sepal_lar = Sepal.Width,
Petal_long = Petal.Length,
Petal_lar = Petal.Width,
Especes = Species)
```
Cette méthode visant à renommer les variables pour modifier un caractère ou plusieurs caractères peut être à nouveau très fastidieuse en présence de nombreuses variables à renommer car mal écrite au départ. La fonction `clean_names()` du package `{janitor}` pourrait alors faire gagner beaucoup de temps. Cette fonction réécrit les noms des variables pour qu'elles soient titrées (nommées) conformément à certains standards, à savoir : pas de points en général dans les noms des variables, plutôt des tirets du bas ; éviter les majuscules dans les noms d'objet ; éviter les caractères spéciaux ; etc. Voilà ce que cela donnerait :
```{r}
#| warning: false
library(janitor)
iris |>
clean_names()
```
### Créer des variables avec `mutate()`
Certaines analyses peuvent nécessiter d'ajouter des variables à partir de calculs réalisés sur des variables qui existent déjà dans le jeu de données. La fonction `mutate()`, du package `{dplyr}`, et que nous avons déjà rencontrée précédemment, permet cela. Dans l'exemple ci-dessous, on observe que le nom de la nouvelle variable à créer est à gauche du signe `=` et que le calcul créant les nouvelles valeurs est décrit à droite du signe `=`.
```{r mutate}
iris |>
mutate(ratio_sepal = Sepal.Length / Sepal.Width,
ratio_petal = Petal.Length / Petal.Width)
```
### Sélectionner des lignes avec `filter()`
En fonction des besoins de l'analyse, on peut vouloir ne retenir que certaines lignes du fichier de données. La fonction `filter()` du package `{dplyr}` est faite pour réaliser ce filtrage. Plusieurs opérateurs sont disponibles pour ne retenir que les lignes que l'on veut (cf. @tbl-SymbolsFilter).
```{r SymbolsFilter}
#| echo: false
#| label: tbl-SymbolsFilter
#| tbl-cap: "Les opérateurs utilisables avec la fonction filter()"
flextable(
data.frame(
"Opération" = c("Égal", "Inférieur ou égal", "Supérieur ou égal", "Différent de"),
"Opérateur" = c("==", "<=", ">=", "!=")), cwidth = 2.5) |>
theme_zebra() |>
hline_top(part = "all", border = fp_border(width = 1.5)) |>
hline_bottom(part = "all", border = fp_border(width = 1.5)) |>
align(j = 1, align="left", part="all") |>
align(j = 2, align="center", part="all") |>
autofit()
```
De plus, dans la configuration du code, ces opérateurs peuvent être couplés à l'opérateur `|` (OU) et à l'opérateur `&` (ET). Dans l'exemple ci-dessous, le code permet, à partir du jeu de données `iris`, de ne garder que les lignes du jeu de données qui contiennent les noms d'espèce *setosa* OU *virginica*, ET en même temps qui affichent une longueur de sépale inférieure ou égale à 5.
```{r filter}
iris |>
filter((Species == "setosa" | Species == "virginica") &
Sepal.Length <= 5)
```
### Réordonner les lignes avec `arrange()`
On peut vouloir trier les lignes du jeu de données selon un certain ordre, en fonction des valeurs d'une variable donnée. La fonction `arrange()` du package `{dplyr}` est très utile pour gérer ce genre de réalisation. L'exemple ci-dessous conduit à trier les données selon un ordre croissant en fonction des valeurs de la variable `Sepal.Length`. Le fait de mettre le symbole `-` devant le nom de la variable aurait conduit à un tri décroissant.
```{r arrange}
iris |>
arrange(Sepal.Length)
```
### Résumer des variables avec `group_by()` et `summarise()`
Bien qu'une base de données puisse contenir énormément de lignes, on peut n'en vouloir que la version résumée. Les fonctions `group_by()` et `summarise()` du package `{dplyr}` permettent de faire cela aisément. Dans l'exemple ci-dessous, la fonction `group_by()` permet d'indiquer que les calculs réalisés par la suite avec la fonction `summarise()` doivent être exécutés pour les modalités de la variable `Species` prises séparément. La fonction `summarise()`, quant à elle, permet d'exécuter différents calculs. Dans l'exemple ci-dessous, il s'agit de moyennes, obtenues à l'aide de la fonction `mean()`. De plus, la fonction `summarise()` permet, comme montré ci-dessous, d'indiquer à gauche du `=` le nom du titre du calcul alors effectué .
```{r summarise}
iris |>
group_by(Species) |>
summarise(
mean_sep_len = mean(Sepal.Length),
mean_sep_wid = mean(Sepal.Width)
)
```
Au cours des illustrations montrant l'usage des fonctions `select()` jusqu'à `summarise()`, il aura été possible de noter que les commandes n'écrasaient pas le jeu de données initial, ni ne créaient de nouveaux jeux de données, car aucune assignation à un nom n'était faite. Lorsqu'une assignation est réalisée, il est conseillé d'utiliser un nouveau nom, différent de celui utilisé pour le jeu de données initial, pour pouvoir revenir au jeu de données originel lorsque cela est souhaité. Ci-dessous un exemple de création d'un nouvel objet de type tableau (assigné au nom `iris2`) à partir de l'utilisation de la plupart des fonctions que nous venons de voir et qui peuvent être utilisées dans un même bloc de code grâce au *pipe* (`|>`) :
```{r full_example_dplyr}
iris2 <-
iris |>
dplyr::select(Petal.Length, Petal.Width, Species) |>
clean_names() |>
mutate(petal_ratio = petal_length / petal_width) |>
filter(
(species == "setosa" |
species == "virginica") & petal_ratio > 3
) |>
arrange(-petal_ratio)
iris2
```
### Passer d'une disposition en lignes à une disposition en colonnes et inversement avec `pivot_wider()` et `pivot_longer()`
Il convient de respecter certaines règles de base lors de la conception d'une base de données (e.g., mettre les observations en lignes et les variables en colonnes). Toutefois, dans certains cas, même après avoir bien respecté les règles, la manière selon laquelle la base de données a été organisée peut finalement ne pas être adéquate pour pouvoir utiliser certaines fonctions. Prenons par exemple le cas où toutes les valeurs numériques d'une variable quantitative auraient été mises dans une même colonne en regard d'une variable qualitative pour que chaque valeur numérique corresponde à une modalité de cette variable qualitative (c'est le cas, par exemple, avec le jeu de données `iris`), et que la fonction à utiliser nécessiterait que l'on ait une colonne pour chacune des modalités de la variable qualitative, avec des colonnes mises côte à côte. Une fonction qui permet alors de passer d'un format "long" (i.e., toutes les valeurs numériques sont dans la même colonne) à un format "large" (i.e., les valeurs numériques sont réparties dans différentes colonnes selon la modalité à laquelle elles sont associées), est la fonction `pivot_wider()` du package `{tidyr}`. Pour pouvoir utiliser cette fonction, il faut qu'il y ait une variable permettant d'identifier à quels individus ou groupes appartiennent les données dont on va changer l'organisation. Dans une base de données classique, il y a généralement une variable présente pour cela. Toutefois, dans le jeu de données `iris`, il n'y a pas une telle variable. Pour pouvoir illustrer l'utilisation de la fonction `pivot_wider()`, nous avons donc, dans un jeu de données s'appelant à présent `iris2`, ajouté arbitrairement une variable `id` grâce à la fonction `mutate()` pour simuler le fait que les données de `iris` auraient été acquises en référence à des individus bien identifiés. Nous avons aussi ajouté une variable `date` pour pouvoir réaliser l'exemple :
```{r add_id_iris}
iris2 <-
iris |>
mutate(
id = rep(1:50, each = 3),
date = rep(c(2001:2003), times = 50)
) |>
dplyr::select(id, date, Species, everything()) |>
arrange(id, date) |>
as_tibble()
iris2
```
La fonction `pivot_wider()` permet alors de mettre en colonnes les valeurs des variables sélectionnées pour chacune des trois modalités de la variable visée, ici la variable `date` dans l'exemple ci-dessous.
```{r pivot_wider}
library(tidyr)
iris3 <-
iris2 |>
pivot_wider(
names_from = date,
values_from = Sepal.Length : Petal.Width
)
iris3
```
L'argument `names_from` a permis d'indiquer la variable à partir de laquelle on a dispatché les valeurs en colonnes, et l'argument `values_from` a permis de préciser les variables pour lesquelles on voulait que les valeurs numériques soient dispatchées. L'utilisation des deux-points (`:`) nous a permis de sélectionner toutes les variables allant de `Sepal.Length` à `Petal.Width` dans le jeu de données.
Dans une situation inverse à celle que nous venons de voir, nous pourrions vouloir une disposition des données davantage en lignes. Par exemple, s'agissant du jeu de données `iris`, nous pourrions vouloir une seule colonne comprenant tous les paramètres mesurés pour chaque fleur, et une seule colonne contenant les valeurs associées. La fonction `pivot_longer()` permet de faire ce genre de conversion, tel que montré ci-dessous :
```{r pivot_longer}
# Avant
head(iris, 5)
# Après
iris |>
pivot_longer(
cols = c(-Species),
names_to = "Parameter",
values_to = "Value"
)
```
Dans la fonction `pivot_longer()` ci-dessus, nous avons indiqué à l'aide de l'argument `cols` et de la fonction `c()` avec le signe `-` la colonne que nous ne voulions pas utiliser avec la fonction (c'était plus rapide que d'indiquer dans la fonction `c()` les trois colonnes à utiliser). L'argument `names_to` nous a permis de donner un nom à la variable qualitative qui comporte à présent les modalités associées aux valeurs numériques, et l'argument `values_to` nous a permis de donner un nom à la colonne où se trouvent maintenant les valeurs numériques.
Lorsque l'on veut utiliser la fonction `pivot_longer()` sur plusieurs colonnes qui sont écrites avec la même logique (par exemple, avec un nom composé de deux morceaux séparés par un `_`, avec un morceau pour indiquer la chose mesurée, et un autre morceau pour indiquer la modalité), il convient de procéder comme ci-dessous :
```{r pivot_longer_2}
# Avant
head(iris3, 5)
# Après
iris3 |>
pivot_longer(
cols = c(Sepal.Length_2001:Petal.Width_2003),
names_to = c(".value", "date"),
names_pattern = '(.*)_(.*)'
)
```
Dans le code montré ci-dessus, l'argument `cols` a permis de sélectionner toutes les colonnes dont on voulait faire pivoter les données, l'argument `names_to` a permis d'indiquer qu'il faut garder des colonnes spécifiques nommées avec le premier morceau du nom des variables antérieures (le mot `".value"` permet cela), et le mot `"date"` a permis de nommer la colonne avec les dates qui étaient les seconds morceaux des noms des variables antérieures.
Nous venons de voir plusieurs fonctions qui peuvent être très utiles pour pouvoir facilement préparer sa base de données en vue des futures analyses. Il ne s'agit que d'une vue très superficielle de tout le potentiel de manipulation des données qu'ont ces fonctions. Pour une vue plus approndie des possibilités qu'offrent ces fonctions, la lecture de l'ouvrage *R for Data Science* d'Hadley Wickham et de Garrett Grolemund [-@wickhamDataScience2017] sera particulièrement enrichissante. Cet ouvrage est en libre accès ici : https://r4ds.had.co.nz.
## Résumé
* La base de données est un tableau comportant l'ensemble des données avec les observations organisées en lignes et les variables organisées en colonnes.
* Les grands types de variables que l'on peut retrouver dans une base de données sont les variables quantitatives (avec une échelle d'intervalles ou une échelle de ratios) et les variables qualitatives (nominales ou ordinales).
* Avant de débuter un travail d'analyse, il est conseillé d'initialiser un projet (en créant un fichier .Rproj) dans un dossier où se trouvent le ou les fichiers à analyser.
* Pour importer un jeu de données au format .csv, il est possible d'utiliser la fonction `readr::read_csv2()`.
* Pour exporter un jeu de données au format .csv, il est possible d'utiliser la fonction `readr::write_csv2()`.
* Pour mettre un tableau de données au format *data frame*, utiliser la fonction `as.data.frame()`.
* Pour mettre un tableau de données au format *tibble*, utiliser la fonction `tibble::as_tibble()`.
* Pour lister les variables présentes dans un tableau de données, utiliser la fonction `str()`, ou encore la fonction `skimr::skim()`.
* Pour modifier les types des variables, utiliser des fonctions comme `as.numeric()`, `as.factor()`, `as.character()`, `as.Date()`, etc., éventuellement en combinaison avec la fonction `dplyr::across()` si cela s'y prête.
* Pour sélectionner les variables d'un tableau de données, utiliser la fonction `dplyr::select()`.
* Pour renommer les variables d'un tableau de données, utiliser la fonction `dplyr::rename()`, ou encore la fonction `janitor::clean_names()` pour une réécriture automatique des noms des variables.
* Pour créer de nouvelles variables dans un tableau de données, utiliser la fonction `dplyr::mutate()`.
* Pour sélectionner des lignes dans un tableau de données, utiliser la fonction `dplyr::filter()`.
* Pour trier les lignes d'un tableau de données, utiliser la fonction `dplyr::arrange()`.
* Pour résumer les variables d'un tableau de données, utiliser les fonctions `dplyr::group_by()` et `dplyr::summarise()`.
* Pour passer d'un tableau de données au format *long* à un tableau de données au format *wide*, utiliser la fonction `tidyr::pivot_wider()`.
* Pour passer d'un tableau de données au format *wide* à un tableau de données au format *long*, utiliser la fonction `tidyr::pivot_longer()`.
* Pour enchaîner l'application de fonctions, utiliser le symbole `|>` (*pipe*) avec une version de R >= 4.1.0, ou à défaut le symbole `%>%` du package `{magrittr}`.