-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path04-Visualisation-III.Rmd
executable file
·1016 lines (731 loc) · 66.1 KB
/
04-Visualisation-III.Rmd
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
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# Visualisation III {#visu3}
```{r setup, include=FALSE, echo=FALSE, message=FALSE, results='hide'}
SciViews::R
```
##### Objectifs {.unnumbered}
- Être capable de réaliser différents graphiques pour représenter des variables facteurs comme le graphique en barres, ou le graphique en camembert dans R avec la fonction `chart()`
- Comprendre et utiliser la boite de dispersion pour synthétiser la distribution de données numériques
- Arranger différents graphiques dans une figure unique
- **De manière optionnelle**, découvrir différents systèmes graphiques (graphiques de base, {lattice}, {ggplot2}) et les comparer entre eux
##### Prérequis {.unnumbered}
Assurez-vous de bien maîtriser les bases relatives à la représentation graphique abordées dans le module \@ref(visu1) et d'être à l'aise dans l'utilisation des outils logiciels (SciViews Box, RStudio, R Markdown, Git & GitHub).
## Graphique en barres
Le graphique en barres (on dit aussi graphique en bâtons) compare les effectifs pour différents niveaux (ou modalités) d'une variable qualitative ou facteur. La différence avec l'histogramme est donc subtile et tient au fait que, pour l'histogramme, nous partons d'une variable quantitative qui est découpée en classes.
### Effectifs par facteur
La question du nombre et/ou de l'intervalle des classes ne se pose pas dans le cas du graphique en barres. Par défaut, les barres seront séparées les unes des autres par un petit espace vide pour bien indiquer visuellement qu'il n'y a pas continuité entre les classes (dans l'histogramme, les barres sont accolées les unes aux autres pour matérialiser justement cette continuité).
La formule que vous utiliserez, ici encore, ne fait appel qu'à une seule variable et s'écrira donc :
$$\sim variable \ facteur$$
```{r barres-facteur, fig.cap= "Exemple d'un graphique en barres montrant le dénombrement des niveaux d'une variable facteur, avec les éléments importants du graphique mis en évidence en couleurs.", echo=FALSE, message=FALSE}
zooplankton <- read("zooplankton", package = "data.io", lang = "FR")
copepoda <- sfilter(zooplankton,
class %in% c("Calanoïde", "Cyclopoïde", "Harpacticoïde", "Poecilostomatoïde"))
chart(data = copepoda, ~ class) +
geom_bar() +
labs(x = "Label de l'axe x + [Unité éventuelle]",
y = "Effectifs") +
theme(axis.text.x = element_text(colour = "#a80039", size = 14),
axis.title.x = element_text(colour = "#029687", size = 14),
axis.text.y = element_text(colour = "#a80039", size = 14),
axis.title.y = element_text(colour = "#029687", size = 14),
axis.line.x = element_line(colour = "#a80039"),
axis.line.y = element_line(colour = "#a80039"))
```
Outre les barres elles-mêmes, prêtez toujours attention aux éléments suivants du graphique (ici mis en évidence en couleurs) :
- les axes avec les graduations (en rouge)
- les niveaux de la variable facteur (en rouge également)
- le label des axes (en bleu)
Les instructions dans R pour produire un graphique en barres à l'aide de la fonction `chart()` sont les suivantes. Nous partons d'un jeu de données `zooplankton` que nous importons et dont nous extrayons un sous-ensemble à l'aide de la fonction `sfilter()` (vous étudierez en détail les fonctions de remaniement de tableaux dans les deux prochains modules) avant de réaliser notre graphique à l'aide de `chart()` :
```{r barres-facteur2, fig.cap="Abondances de quatres types de copépodes dans un échantillon de zooplancton."}
# Importation du jeu de données
(zooplankton <- read("zooplankton", package = "data.io", lang = "FR"))
# Réduction du jeu de données à quatre classes seulement
(copepoda <- sfilter(zooplankton, class %in% c("Calanoïde", "Cyclopoïde",
"Harpacticoïde", "Poecilostomatoïde")))
# Réalisation du graphique
chart(data = copepoda, ~ class) +
geom_bar() +
xlab("Classe") +
ylab("Effectifs")
```
La fonction `geom_bar()` se charge d'ajouter les barres verticales dans le graphique. La hauteur de ces barres correspond au nombre d'observations rencontrées dans le jeu de données pour chaque niveau (ou classe, ou groupe) de la variable facteur représentée.
### Effectifs par deux facteurs
Reprenons maintenant le jeu de données `biometry`.
```{r}
# Importation des données `biometry`
(biometry <- read("biometry", package = "BioDataScience", lang = "FR"))
```
Nous voulons représenter des barres pour les effectifs d'hommes et de femmes dans ce jeu de données (variable `gender`), mais en les séparant par année (variable `year_measure`). C'est faisable, mais notez que, si `gender` est déjà une variable facteur `<fct>`, `year_measure` est encodé comme variable quantitative numérique (c'est un "double" indiqué `<dbl>`, c'est-à-dire un nombre décimal par opposition à entier `<int>`). Or nous devons **absolument utiliser des variables facteur ici**. Nous allons donc effectuer la conversion avec la fonction `as.factor()` avant de réaliser notre graphique en barres. Nous en profitons pour indiquer un `label()` en français pour cette variable.
```{r}
# Conversion de la variable year_measure de numérique à facteur
biometry$year_measure <- as.factor(biometry$year_measure)
label(biometry$year_measure) <- "Année de la mesure"
```
Notez bien comment on se réfère à la variable `year_measure` à l'intérieur du jeu de données `biometry` avec `biometry$year_measure`. Et cette notation peut aussi bien être utilisée pour récupérer la colonne `year_measure` dans un argument d'une fonction (à droite), que comme résultat de l'assignation (à gauche de l'opérateur d'assignation `<-`). Ainsi l'instruction qui transforme en facteur **remplace** la version dans le jeu de données `biometry`. Maintenant, considérons que nous nous intéressons aux mesures antérieures à 2017 (cela nous permettra d'illustrer des points importants relatifs à l'utilisation de variables facteurs).
```{block, type='note'}
Les variables facteurs sont encodées dans R comme des entiers 1, 2, 3, ... pour les différents niveaux avec en plus, un attribut `levels` qui y associe une description textuelle à chacun des niveaux. Cela peut être perturbant quand la description textuelle est constituée d'un nombre comme ici pour `year_measure`. Mais les calculs sont prohibés sur les variables facteurs.
```
```{r}
tail(biometry)$year_measure
```
Nous utilisons `head()` et `tail()` pour extraire les quelques lignes de début ou de fin d'un tableau. C'est utile pour en réduire la taille à l'impression. Faites très attention : lorsque vous imprimez le contenu d'une variable facteur, R *substitue* automatiquement les niveaux 1, 2, 3 ... par le contenu textuel comme indiqué dans la dernière ligne `Levels: ...`, et **l'imprime sans mettre le texte entre guillemets**. Cela peut renforcer la fausse impression que c'est bien des valeurs numériques.
Vous commencez à comprendre que si vous effectuez maintenant la comparaison `year_measure < 2017` lorsque cette variable est encodée comme facteur, cela ne fonctionnera pas comme vous le souhaitez ! Si on est chanceux, un message d'erreur ou d'avis ("warning") est imprimé.
```{r, error=TRUE}
biometry2 <- sfilter(biometry, year_measure < 2017)
```
Notez au passage que `sfilter()` n'utilise pas la notation `biometry$year_measure`, mais prend un premier argument qui est le jeu de données `biometry`, et la suite se réfère aux variables de ce jeu de données telles que `year_measure` en priorité aux autres variables disponibles dans l'environnement utilisateur de R. Aussi `sfilter()` renvoie tout le tableau remanié. Donc, nous devons l'affecter simplement à une variable qui contient ce tableau (`biometry2` ici). Vous pouvez aussi utiliser la fonction `filter()` qui s'emploie de manière similaire.
Mais revenons à notre variable facteur. Dans d'autres cas, le résultat peut être dramatique, car le calcul est appliqué à l'encodage des niveaux de la variable facteur. Or, avec quatre niveaux, les encodages sont 1, 2, 3 et 4, ... et ils sont bien évidemment tous inférieurs à 2017 (pour rappel, "2013", "2014", "2016" et "2017" sont les libellés textuels des niveaux de la variable facteur) !
```{block, type='warning'}
De manière générale, n'effectuez **jamais** de calcul sur des variables facteurs. Tranformez-les toujours avant. Si les libellés contiennent des valeurs numériques sur lesquelles vous voulez faire des calculs, utilisez `as.numeric(as.character(VARFACT))`, et une fois le calcul réalisé, retransformez en facteur avec `as.factor()`.
```
Le calcul explicite et sûr est donc le suivant :
```{r}
# Transforme de manière sûre factor -> numeric (double)
biometry$year_measure <- as.numeric(as.character(biometry$year_measure))
# Filtre les données sur une copie du tableau
biometry2 <- sfilter(biometry, year_measure < 2017)
# Retransforme en variable factor
biometry2$year_measure <- as.factor(biometry2$year_measure)
# Vérification
tail(biometry2)$year_measure
```
Naturellement ici, la bonne stratégie est d'effectuer le calcul sur le tableau de départ **avant** de transformer en facteur, mais nous sommes partis de la variable facteur à titre d'illustration d'un cas qui peut se rencontrer en pratique. À présent que nous avons notre jeu de données sans les individus de 2017, et les variables correctement encodées, nous pouvons aborder différentes représentations pour observer des dénombrements tenant compte de plusieurs variables facteurs. Par défaut, l'argument `position =` a pour valeur `"stack"` (donc, lorsque cet argument n'est pas précisé dans `geom_bar()`, les barres sont empilées par rapport à la seconde variable facteur).
```{r barres-2facteurs, echo=TRUE, fig.cap="Dénombrement des hommes (H) et des femmes (F) dans l'étude sur l'obésité en Hainaut, (A) graphique utilisant un seul facteur. (B) graphique prenant en compte les années de mesure."}
a <- chart(data = biometry2, ~ gender) +
geom_bar() +
ylab("Effectifs")
b <- chart(data = biometry2, ~ gender %fill=% year_measure) +
geom_bar() +
ylab("Effectifs") +
scale_fill_viridis_d()
combine_charts(list(a, b), common.legend = TRUE)
```
Il existe d'autres options en utilisant les valeurs `"dodge"` ou `"fill"` pour l'argument `position =`.
```{r barres-2facteurs2, fig.cap="Dénombrement des hommes (H) et des femmes (F) dans l'étude sur l'obésité en Hainaut en tenant compte des années de mesure (différentes présentations).", echo=TRUE}
a <- chart(data = biometry2, ~ gender %fill=% year_measure) +
geom_bar(position = "stack") +
ylab("Effectifs") +
scale_fill_viridis_d()
b <- chart(data = biometry2, ~ gender %fill=% year_measure) +
geom_bar(position = "dodge") +
ylab("Effectifs") +
scale_fill_viridis_d()
c <- chart(data = biometry2, ~ gender %fill=% year_measure) +
geom_bar(position = "fill") +
ylab("Fractions") +
scale_fill_viridis_d()
combine_charts(list(a, b, c), common.legend = TRUE)
```
Soyez vigilant à la différence entre l'argument `position = "stack"` (figure A) et `position = "fill"` (Figure C) qui malgré un rendu semblable ont l'axe des ordonnées qui diffèrent (dans le cas de `"fill"`, il s'agit de la **fraction** par rapport au total qui est représentée, et non pas des **effectifs** absolus dénombrés).
`r h5p(211, height = 270, toc = "Graphiques en barres")`
##### Pièges et astuces {.unnumbered}
###### Réordonner la variable facteur par fréquence {.unnumbered}
Vous pouvez avoir le souhait d'ordonner votre variable facteur afin d'améliorer le rendu visuel de votre graphique. Pour cela, vous pouvez employer la fonction `fct_infreq()`.
```{r barres-facteur-ordonne, fig.cap="Dénombrement des classes de copépodes du jeu de données zooplankton."}
chart(data = copepoda, ~ fct_infreq(class)) +
geom_bar() +
labs(x = "Classe", y = "Effectifs")
```
###### Rotation des axes du graphique en barres {.unnumbered}
Lorsque les niveaux dans la variable étudiée sont trop nombreux, les légendes en abscisse risquent de se chevaucher, comme dans la Fig. \@ref(fig:barchart1)
```{r barchart1, fig.cap="Dénombrement des classes du jeu de données zooplankton."}
chart(data = zooplankton, ~ class) +
geom_bar() +
ylab("Effectifs")
```
Dans ce cas, il est possible de réaliser un graphique en **barres horizontales** qui laisse plus de place pour le libellé sur l'axe. Il existe deux manières de le faire : préférentiellement en utilisant l'argument `orientation = "y"` de `geom_bar()` (mais alors il faut utiliser `aes(y = ...)` à la place de la formule). Une seconde option consiste à réaliser le graphique en barres verticales (tout le code reste identique), mais de rajouter `coord_flip()` tout à la fin. Utilisons successivement ces deux approches.
```{r barres-rotation, fig.cap="Dénombrement des classes du jeu de données zooplankton (version avec barres horizontales en utilisant `orientation = \"y\"`)."}
chart(data = zooplankton, aes(y = class)) +
geom_bar(orientation = "y") +
xlab("Effectifs")
```
Pourquoi ne peut-on pas utiliser de formule dans ce cas ? En fait, il faudrait écrire `class ~`, ... seulement voilà, une formule ne *peut pas* avoir un membre de droite vide. Donc, on est dans une impasse et on doit utiliser la forme explicite `aes()` pour "aesthetics" en anglais qui indique quelle variable est utilisée pour quoi dans le graphique en indiquant `y =`.
Avec la fonction `coord_flip()` ajoutée à votre graphique, vous pouvez effectuer une rotation des axes (l'axe X devient Y et inversement) pour obtenir un **graphique en barres horizontales**. De plus, l'œil humain perçoit plus distinctement les différences de longueurs horizontales que verticales. Donc, de ce point de vue, le graphe en barres horizontales est considéré comme meilleur que le graphe en barres verticales.
```{r barres-rotation2, fig.cap="Dénombrement des classes du jeu de données zooplankton (version avec barres horizontales en utilisant `coord_flip()`)."}
chart(data = zooplankton, ~ class) +
geom_bar() +
ylab("Effectifs") +
coord_flip()
```
```{block, type='warning'}
Avec `coord_flip()`, gardez toujours à l'esprit que vos axes *finaux* X et Y sont inversés avant l'utilisation de cette instruction. Ainsi, si vous voulez changer le libellé de l'axes X sur le graphique final, mais *avant* d'avoir indiqué `coord_flip()`, c'est en fait le libellé de l'axe Y que vous devez indiquer avec, par exemple `ylab()` comme c'est le cas dans la figure précédente pour le libellé "Effectifs".
```
##### Pour en savoir plus {.unnumbered}
- [Graphes en barres à l'aide de ggplot2](http://www.cookbook-r.com/Graphs/Bar_and_line_graphs_(ggplot2)/). Un tutoriel en anglais utilisant la fonction `ggplot()`.
- [Page d'aide de la fonction `geom_bar()`](http://ggplot2.tidyverse.org/reference/geom_bar.html) en anglais.
- Une page qui présente l'[utilisation de geom_col()](https://www.datanovia.com/en/fr/lessons/ggplot-barplot/), une autre fonction qui réalise des graphiques en bâtonnets, en français.
### Valeurs moyennes {#barres-mean}
Le graphique en barres peut être aussi employé pour résumer des données numériques via la moyenne. Il ne s'agit plus de dénombrer les effectifs d'une variable facteur, mais de résumer des données numériques en fonction d'une variable facteur. On peut exprimer cette relation dans R sous la forme de :
$$y \sim x$$
que l'on peut lire :
$$y \ en \ fonction \ de \ x$$
Avec *y* une variable numérique et *x* une variable facteur. Considérez l'échantillon suivant :
```
1, 71, 55, 68, 78, 60, 83, 120, 82 ,53, 26
```
Calculez la moyenne sur base de la formule de la moyenne
$$\overline{y} = \sum_{i = 1}^n \frac{y_i}{n}$$
```{r}
# Création du vecteur
x <- c(1, 71, 55, 68, 78, 60, 83, 120, 82, 53, 26)
# Calcul de la moyenne
mean(x)
```
Les instructions pour produire ce graphe en barres à l'aide de `chart()` sont :
```{r barres-mean, fig.cap="Exemple de graphique en barres représentant les moyennes de tailles par groupe zooplanctonique."}
chart(data = copepoda, size ~ class) +
stat_summary(geom = "col", fun = "mean") +
xlab("Classe")
```
Ici, nous faisons appel à une autre famille de fonctions : celles qui effectuent des calculs sur les données avant de les représenter graphiquement. Leurs noms commencent toujours par `stat_`.
```{block, type='warning'}
Le graphe en barres pour représenter les moyennes est très répandu dans le domaine scientifique malgré le grand nombre d'arguments en sa défaveur et que vous pouvez lire dans la section **pour en savoir plus** ci-dessous. L'un des arguments le plus important est la faible information qu'il véhicule puisque l'ensemble des données n'est plus représenté que par une valeur (la moyenne) pour chaque niveau de la variable facteur. Pour un petit nombre d'observations, il vaut mieux toutes les représenter à l'aide d'un nuage de points. Si le nombre d'observations devient très grand (dizaines ou plus), le graphique en boites de dispersion est plus indiqué (voir plus loin dans ce module). Le graphique en violon (cf. module précédent) est également utilisable s'il y a encore plus de données.
```
`r h5p(210, height = 270, toc = "Quel graphique est-ce ?")`
##### Pour en savoir plus {.unnumbered}
- [Beware of dynamite](http://users.stat.umn.edu/~rend0020/Teaching/STAT8801-2015Spring/handouts/24-dynamite.pdf). Démonstration de l'impact d'un graphe en barres pour représenter la moyenne (et l'écart type) = graphique en "dynamite".
- [Dynamite plots: unmitigated evil?](http://emdbolker.wikidot.com/blog%3Adynamite) Une autre comparaison du graphe en dynamite avec des représentations alternatives qui montre que le premier peut avoir quand même quelques avantages dans des situations particulières.
## Graphique en camembert
Le graphique en camembert (ou en parts de tarte, ou encore appelé diagramme circulaire, *pie chart* en anglais) vous permettra de visualiser un dénombrement d'observations par facteur, tout comme le graphique en barres.
```{r piechart, fig.cap="Exemple de graphique en camembert montrant les effectifs des niveaux d'une variable facteur."}
chart(data = copepoda, ~ factor(0) %fill=% class) +
geom_bar(width = 1) +
coord_polar("y", start = 0) +
theme_void() +
scale_fill_viridis_d()
```
Ce graphique est plus difficile à réaliser à l'aide de `chart()` ou `ggplot()`. En fait, il faut ruser ici, et l'auteur du package {ggplot2} n'avait en fait pas l'intention d'ajouter ce type de graphique dans la panoplie proposée. En effet, il faut savoir que l'œil humain est nettement moins bon pour repérer des angles que pour comparer des longueurs. **Donc, le diagramme en barres est souvent meilleur pour comparer des effectifs par classes.** Mais d'une part, le graphique en camembert est (malheureusement) un graphique très répandu et il faut savoir l'interpréter, et d'autre part, il peut s'avérer quand même utile dans certaines situations. Notez l'utilisation des fonctions `coord_polar()` qui crée des coordonnées polaires et la fonction `theme_void()` qui crée un graphique sans axes. En fait, un graphique en camembert peut aussi se concevoir comme un graphique en barres représenté en coordonnées polaires.
##### Pièges et astuces {.unnumbered}
Partons d'un exemple fictif pour vous convaincre qu'un graphique en barres est souvent plus lisible qu'un graphique en camembert. Combien d'observations comptez-vous pour la lettre **H** ?
```{r, echo=FALSE}
df <- dtx(
Index = 1:347,
Facteur = as.factor(rep(LETTERS[1:10],
times = c(10, 1, 1, 50, 2, 78, 101, 24, 31, 49)
))
)
```
```{r piechart2, echo=FALSE, fig.cap="Arrivez-vous à lire facilement des valeurs sur un graphique en camenbert (une échelle y est ajoutée de manière exceptionnelle pour vous y aider)."}
chart(data = df, ~ factor(0) %fill=% Facteur) +
geom_bar(width = 1) +
coord_polar("y", start = 0) +
labs( x = "", y = "") +
scale_fill_viridis_d()
```
Maintenant, effectuez le même exercice sur base d'un graphique en barres, combien d'observations pour la lettre **H** ?
```{r barchart2, echo=FALSE, fig.cap="Dénombrement des niveaux d'une variable facteur sur un graphique en barres."}
chart(data = df, ~ Facteur %fill=% Facteur) +
geom_bar(width = 1) +
ylab("Effectifs") +
scale_fill_viridis_d()
```
Dans ce dernier cas, c'est bien plus facile : il y a 24 observations relatives à la lettre **H** (vous ne voyez peut-être pas que l'effectif de *H* est exactement 24, mais vous pouvez voir sans problème qu'il est d'*environ* 25, alors que sur le graphique en camembert, nous le voyons beaucoup moins bien).
##### À vous de jouer ! {.unnumbered}
`r learnr("A04La_barplot", title = "Graphiques en barres et camembert", toc = "Graphiques en barres et camembert")`
##### Pour en savoir plus {.unnumbered}
- [Graphique en camembert à l'aide de la fonction `ggplot()`](https://www.datanovia.com/en/fr/blog/comment-creer-un-camembert-dans-r-en-utilisant-ggplot2/). Explications en français des différentes étapes pour passer d'un graphique en barres à un graphique en camembert avec **ggplot2**.
- [Autre explication](https://dataparkblog.wordpress.com/2017/09/24/diagramme-en-camembert-avec-r-et-ggplot/) en français, également accompagnée d'informations sur les bonnes pratiques en matière de graphique en camembert.
- [Save the pies for dessert](http://www.perceptualedge.com/articles/08-21-07.pdf) est une démonstration détaillée des méfaits du graphique en camembert (le graphique en camembert, un graphique puant ? Pourrait-on peut-être titrer en français).
- [Les côtés positifs du graphe en camembert](https://www.displayr.com/why-pie-charts-are-better-than-bar-charts/) sont mis en évidence dans ce document (en anglais).
## Boite de dispersion {#boxplot}
Vous souhaitez représenter graphiquement cette fois *un résumé* d'une variable numérique mesurée sur un nombre (relativement) important d'individus, soit depuis une dizaine jusqu'à plusieurs millions. Vous souhaitez également conserver de l'information sur la distribution des données, et voulez éventuellement comparer plusieurs distributions entre elles : soit différentes variables, soit différents niveaux d'une variable facteur. Nous avons déjà vu au module \@ref(visu2) les diagrammes en violon et en lignes de crêtes pour cet usage. Nous allons étudier ici les **boites de dispersion** (encore appelées boites à moustaches) comme option alternative intéressante. La boite de dispersion représentera graphiquement cinq descripteurs communément appelés les **cinq nombres**.
Considérez l'échantillon suivant :
```
1, 71, 55, 68, 78, 60, 83, 120, 82 ,53, 26
```
Ordonnons-le de la plus petite à la plus grande valeur :
```{r}
# Créer du vecteur
x <- c(1, 71, 55, 68, 78, 60, 83, 120, 82, 53, 26)
# Ordonner le vecteur par ordre croissant
sort(x)
```
Le premier descripteur des cinq nombres est la **médiane** qui est la valeur se situant à la moitié des observations, donc, avec autant d'observations plus petites et d'observations plus grandes qu'elle. La médiane sépare l'échantillon en deux.
```{r}
median(x)
```
En effet, nous voyons sur le vecteur ordonné que cinq valeurs sont plus petites que 68 et cinq valeurs sont plus grandes. Les quartiles séparent l'échantillon en quatre. Le **premier quartile** (Q1) sera la valeur pour laquelle 25% des observations seront plus petites. Elle se situe donc entre la valeur minimale et la médiane. Cette médiane est égale au second quartile (50% des observations plus petites). Le **troisième quartile** (Q3) est la valeur pour laquelle 75% des observations de l'échantillon sont plus petites[^04-visualisation-iii-1]. Enfin, la valeur **minimale** et la valeur **maximale** observées dans l'échantillon complètent ces cinq nombres qui décrivent de manière synthétique la *position* et l'*étendue* des observations.
[^04-visualisation-iii-1]: Notez que, lorsque la coupure tombe entre deux observations, une valeur intermédiaire est utilisée. Ici par exemple, le premier quartile est entre 53 et 55, donc, il vaut 54. Le troisième quartile se situe entre 78 et 82. Il vaut donc 80.
`r h5p(209, height = 270, toc = "Médiane")`
```{block, type='note'}
Les **cinq nombres** sont : la **valeur minimale**, le **premier quartile**, la **médiane** (ou deuxième quartile), le **troisième quartile** et la **valeur maximale**.
```
Voici comment on les calcule facilement dans R :
```{r}
fivenum(x)
```
La boite de dispersion est une représentation graphique codifiée de ces cinq nombres. La représentation de `x` sous forme de nuage de points n'est ni très esthétique, ni très lisible, surtout si nous avons affaire à des milliers ou des millions d'observations qui se chevauchent sur le graphique[^04-visualisation-iii-2].
[^04-visualisation-iii-2]: Il est possible de modifier la transparence des points en utilisant l'argument `alpha =` et/ou de les déplacer légèrement vers la gauche ou vers la droite de manière aléatoire pour résoudre le problème de leur chevauchement sur un graphique en nuage de points univarié en replaçant `geom_point()` par `geom_jitter()`.
```{r nuage-de-points, fig.cap="Nuage de points univarié.", echo=FALSE}
df <- dtx(x = x)
chart(data = df, x ~ 0) +
geom_point() +
theme(axis.text.x = element_blank(),
axis.ticks.x = element_blank(),
axis.text.x.top = element_blank()) +
labs(x = "", y = "")
```
La boite de dispersion va remplacer cette représentation peu lisible par un objet géométrique qui représente les cinq nombres.
```{r boxplot-construct, fig.cap="A) Nuage de points annoté avec les cinq nombres représentés par des traits horizontaux. B) Boite de dispersion obtenue pour les même données que A.", echo=FALSE}
a <- chart(data = df, x ~ 0) +
geom_point() +
theme(axis.text.x = element_blank(),
axis.ticks.x = element_blank(),
axis.text.x.top = element_blank()) +
labs(x = "", y = "") +
geom_hline(yintercept = fivenum(x)) +
geom_hline(yintercept = median(x), linewidth = 1.3)
b <- chart(df, x ~ 0) +
geom_boxplot(outlier.colour = "#a80039", fill = "#029687") +
theme(axis.text.x = element_blank(),
axis.ticks.x = element_blank(),
axis.text.x.top = element_blank()) +
labs(x = "", y = "")
combine_charts(list(a, b))
```
Vous observez à la Fig. \@ref(fig:boxplot-construct) que certaines valeurs minimales et maximales ne sont pas reliées à la boite de dispersion, il s'agit de **valeurs extrêmes**.
```{block, type='note'}
**Règle pour déterminer s'il y a des valeurs extrêmes avec une boite de dispersion :** une valeur est considérée comme extrême si son écart par rapport à la boite est supérieur à une fois et demi la hauteur de la boite (encore appelée **espace inter-quartile** IQR correspondant à Q3 - Q1). Les tiges (ou "moustaches") qui prolongent la boite de dispersion s'arrêtent donc aux dernières valeurs les plus petites et plus grandes, mais qui rentrent encore dans une fois et demi l'IQR. Les valeurs extrêmes sont ensuite représentées individuellement par un point au dessus et en dessous.
```
```{r, echo=FALSE, message=FALSE, warning=FALSE}
# https://www.r-bloggers.com/exploring-ggplot2-boxplots-defining-limits-and-adjusting-style/
ggplot_box_legend <- function(family = "sans") {
# Create data to use in the boxplot legend
set.seed(100)
sample_df <- dtx(
parameter = "test",
values = sample(500))
# Extend the top whisker a bit
sample_df$values[1:100] <- 701:800
# Make sure there's only 1 lower outlier
sample_df$values[1] <- -350
# Function to calculate important values
ggplot2_boxplot <- function(x) {
quartiles <- as.numeric(quantile(x, probs = c(0.25, 0.5, 0.75)))
names(quartiles) <- c("1er quartile",
"2ème quartile\n(médiane)", "3ème quartile")
IQR <- diff(quartiles[c(1, 3)])
upper_whisker <- max(x[x < (quartiles[3] + 1.5 * IQR)])
lower_whisker <- min(x[x > (quartiles[1] - 1.5 * IQR)])
upper_dots <- x[x > (quartiles[3] + 1.5 * IQR)]
lower_dots <- x[x < (quartiles[1] - 1.5 * IQR)]
list("quartiles" = quartiles,
"1er quartile" = as.numeric(quartiles[1]),
"2ème quartile\n(médiane)" = as.numeric(quartiles[2]),
"3ème quartile" = as.numeric(quartiles[3]),
"IQR" = IQR,
"moustache supérieure" = upper_whisker,
"moustache inférieure" = lower_whisker,
"extrêmes hauts" = upper_dots,
"extrêmes bas" = lower_dots)
}
# Get those values
ggplot_output <- ggplot2_boxplot(sample_df$values)
# Lots of text in the legend, make it smaller and consistent font
update_geom_defaults("text", list(size = 3, hjust = 0, family = family))
# Labels don't inherit text
update_geom_defaults("label", list(size = 3, hjust = 0, family = family))
# Create the legend
# The main elements of the plot (the boxplot, error bars, and count)
# are the easy part.
# The text describing each of those takes a lot of fiddling to
# get the location and style just right:
explain_plot <- ggplot() +
stat_boxplot(data = sample_df,
aes(x = parameter, y = values),
geom = 'errorbar', width = 0.3) +
geom_boxplot(data = sample_df,
aes(x = parameter, y = values),
width = 0.3, fill = "#029687") +
theme_minimal(base_size = 5, base_family = family) +
geom_segment(aes(
x = 2.3, xend = 2.3,
y = ggplot_output[["1er quartile"]],
yend = ggplot_output[["3ème quartile"]])) +
geom_segment(aes(
x = 1.2, xend = 2.3,
y = ggplot_output[["1er quartile"]],
yend = ggplot_output[["1er quartile"]])) +
geom_segment(aes(
x = 1.2, xend = 2.3,
y = ggplot_output[["3ème quartile"]],
yend = ggplot_output[["3ème quartile"]])) +
geom_text(aes(
x = 2.4,
y = ggplot_output[["2ème quartile\n(médiane)"]]),
label = "Espace\ninter-quartile",
fontface = "bold",
vjust = 0.4) +
geom_text(aes(
x = c(1.17,1.17),
y = c(ggplot_output[["moustache supérieure"]],
ggplot_output[["moustache inférieure"]]),
label = c(
"Plus grande valeur comprise dans 1.5 fois\nl'espace inter-quartile\nau dessus du 3ème quartile",
"Plus petite valeur comprise dans 1.5 fois\nl'espace inter-quartile\nen dessous du 1er quartile")),
fontface = "bold",
vjust = 0.9) +
geom_text(aes(
x = c(1.17),
y = ggplot_output[["extrêmes bas"]],
label = "Valeur extrême"),
fontface = "bold",
vjust = 0.5) +
#geom_text(aes(
# x = c(1.95),
# y = ggplot_output[["extrêmes bas"]],
# label = " valeur < 1.5 fois\nl'espace inter-quartile"),
# vjust = 0.5) +
geom_label(aes(
x = 1.17,
y = ggplot_output[["quartiles"]],
label = names(ggplot_output[["quartiles"]])),
vjust = c(0.4, 0.85, 0.4),
fill = "white",
label.size = 0) +
ylab("") + xlab("") +
theme(axis.text = element_blank(),
axis.ticks = element_blank(),
panel.grid = element_blank(),
aspect.ratio = 4/3,
plot.title = element_text(hjust = 0.5, size = 10)) +
coord_cartesian(xlim = c(1.4,3.1), ylim = c(-600, 900)) +
labs(title = "Description")
explain_plot
}
```
La boite de dispersion finale ainsi que sa description sont représentées à la Fig. \@ref(fig:boxplot-desc) ci-dessous.
```{r boxplot-desc, fig.cap="A) Boite de dispersion pour `x` et B) description des différents éléments constitutifs.", echo=FALSE}
a <- chart(df, x ~ 0) +
stat_boxplot(geom = 'errorbar', width = 0.3) +
geom_boxplot(fill = "#029687", width = 0.3) +
theme(axis.text.x = element_blank(),
axis.ticks.x = element_blank(),
axis.text.x.top = element_blank()) +
labs(x = "Label de l'axe x", y = "Label de l'axe y [unité]")
b <- ggplot_box_legend()
combine_charts(list(a, b))
```
`r h5p(212, height = 270, toc = "Boite de dispersion et valeurs extrêmes")`
Un des gros avantages de la boite de dispersion est de mettre en évidence de manière synthétique la distribution des données sur l'axe. La **boite de dispersion parallèle** place *plusieurs* boites de dispersion côte à côte en face d'un même axe. C'est un excellent moyen de *comparer* la dispersion de données numériques en fonction des niveaux d'une variable **factor**. Les instructions dans R pour produire un graphique en boites de dispersion parallèles sont :
```{r boxplot, fig.cap="Distribution des tailles par groupes taxonomiques pour le zooplancton."}
chart(data = copepoda, size ~ class) +
geom_boxplot()
```
La formule à employer est `YNUM (size) ~ XFACTOR (class)`. Ensuite, pour réaliser une boite de dispersion, vous devez ajouter la fonction `geom_boxplot()`.
### Taille de l'échantillon
Lors de la réalisation de boites de dispersion, vous devez être vigilant au nombre d'observations qui se cachent sous chacune d'elles. En effet, réaliser une boite de dispersion à partir d'échantillons ne comportant que cinq valeurs ou moins n'a *aucun* sens !
```{r boxplot-points, fig.cap= "Piège des boites de dispersion : trop peu d'observations disponibles pour `a`.", echo= FALSE}
df <- dtx(
Facteur = rep(c("A", "B", "C"), times = c(4, 11, 8)),
Valeur = c(55, 83, 120, 26, 1, 71, 55, 68, 78, 60, 83, 120,
82, 53, 26, 1, 71, 78, 60, 83, 120, 53, 26))
chart(data = df, Valeur ~ Facteur) +
geom_boxplot() +
geom_point(color = "red")
```
La boite de dispersion pour le niveau `"A"` est calculée à partir de seulement quatre observations. C'est trop peu. Comme les points représentant les observations ne sont habituellement pas superposés à la boite, cela peut passer inaperçu et tromper le lecteur ! Une bonne pratique consiste à ajouter *n*, le nombre d'observations au-dessus de chaque boite. Cela peut se faire facilement avec les fonctions `give_n()` et `stat_summary()` ci-dessous[^04-visualisation-iii-3].
[^04-visualisation-iii-3]: La fonction `give_n()` est une **fonction personnalisée** que nous avons écrite nous-mêmes. Elle positionne du texte `y =` 10% plus haut que le `max(x)`, et ce texte est `length(x)`, la longueur du vecteur qui correspond au nombre d'observations pour `x` (`x` étant utilisé en interne par le moteur graphique). Il est possible, et même assez facile, dans R d'écrire ses *propres* fonctions. Néanmoins cela dépasse du cadre du cours pour l'instant. Pour utiliser `give_n()` dans vos documents Quarto ou R Markdown, copiez simplement sa définition dans un chunk avant de l'utiliser comme c'est fait ici. Elle est aussi réutilisable plus loin dans le même Quarto ou R Markdown, une fois qu'elle est définie.
```{r boxplot2, fig.cap="Taille de copépodes pour différents groupes taxonomiques (le nombre d'observations est indiqué au dessus de chaque boite)."}
give_n <- function(x)
c(y = max(x) * 1.1, label = length(x))
chart(data = copepoda, size ~ class) +
geom_boxplot() +
stat_summary(fun.data = give_n, geom = "text", hjust = 0.5)
```
La fonction `stat_summary()` ajoute des éléments à un graphique sur base d'un *calcul*. Ici, nous rajoutons du texte `geom = "text"`, sur base du calcul effectué avec notre fonction `give_n()` définie plus haut. L'argument `hjust = 0.5` indique que le texte doit être justifié horizontalement à 0.5 (= centré, car 0 = justification à gauche, et 1 = justification à droite).
### En fonction de deux facteurs
La Fig. \@ref(fig:boxplot-tooth) présente un graphique en boites de dispersion parallèles qui combine l'usage de *deux* variables facteurs différentes. Nous allons utiliser un autre jeu de données qui a besoin d'être retravaillé pour effectuer ce graphique.
```{r}
# Importation du jeu de données ToothGrowth
(tooth_growth <- read("ToothGrowth", package = "datasets", lang = "fr"))
# Remaniement et labelisation du jeu de données
tooth_growth$dose <- as.ordered(tooth_growth$dose)
tooth_growth <- labelise(tooth_growth, self = FALSE,
label = list(
len = "Longueur des dents",
supp = "Supplémentation",
dose = "Dose"
),
units = list(
len = "mm",
supp = NA,
dose = "mg/J"
)
)
```
Petits commentaires sur ce code :
- La fonction `labelise()` appliquée au tableau tout entier et avec l'argument `self = FALSE` s'applique aux **colonnes** du tableau, c'est-à-dire aux variables. Ensuite, les arguments `label =` et `units =` reçoivent une `list()` nommée pour en modifier les attributs (`nom = "valeur"`). C'est une manière pratique et efficace de changer tous les labels et unités des variables en une seule étape (il n'est pas indispensable de reprendre *toutes* les variables, on peut indiquer seulement celles que l'on veut modifier).
- Nous avons utilisé `as.ordered()` à la place de `as.factor()`. Les objets "facteurs ordonnés" dans R (ou **ordered**) sont identiques aux facteurs à ceci près que l'ordre de niveaux a aussi un sens du plus petit au plus grand. Ainsi, des niveaux de `supp` : soit `"VC"` pour vitamine C ou `"OJ"` pour vitamine C dans du jus d'orange n'ont pas d'ordre précis. Nous utilisons un objet **factor**. Par contre, les doses de vitamines C `0.5 < 1 < 2` exprimées en mg/J ont un ordre. Dans ce cas, nous préférons les objets **ordered**, qui s'utilisent en pratique comme les objets **factor** dans R (mais notez bien l'indication de l'ordre des niveaux de la variable à l'aide de `<` dans `Levels:`, c'est ce qui distingue un objet **ordered** d'un objet **factor**).
```{r}
head(tooth_growth)$dose
```
```{r boxplot-tooth, fig.cap=" Croissance de dents de cochons d'Inde en fonction de la supplémentation (OJ = jus d'orange, VC = vitamine C) et de la dose administrée (nombre d'observations *n* indiqué au dessus de chaque boite)."}
# Réalisation du graphique (nous réutilisons give_n() ici!)
chart(data = tooth_growth, len ~ supp %fill=% dose) +
geom_boxplot() +
stat_summary(fun.data = give_n, geom = "text", hjust = 0.5,
position = position_dodge(0.75))
```
##### À vous de jouer ! {.unnumbered}
`r learnr("A04Lb_boxplot", title = "Boites de dispersion", toc = "Boites de dispersion")`
##### Pour en savoir plus {.unnumbered}
- [Un tutoriel boites de dispersion à l'aide de `ggplot()`](https://www.datanovia.com/en/fr/lessons/ggplot-boxplot/) présentant encore bien d'autres variantes possibles, en français.
- [Box plots in `ggplot2`](https://plot.ly/ggplot2/box-plots/). Autre explication en anglais avec sortie utilisant {plotly}.
- [Grouped box plots](http://www.r-graph-gallery.com/265-grouped-boxplot-with-ggplot2/).
- [Explication plus détaillée sur les cinq nombres](https://chemicalstatistician.wordpress.com/2013/08/12/exploratory-data-analysis-the-5-number-summary-two-different-methods-in-r-2/), en anglais.
## Figures composées
Il arrive fréquemment de vouloir combiner plusieurs graphiques dans une même figure. Plusieurs fonctions sont à votre disposition pour cela. Il faut tout d'abord distinguer deux types de figures multi-graphiques :
1. Soit il s'agit d'un seul graphique que vous souhaitez subdiviser par rapport à une ou plusieurs variables facteurs.
2. Soit il s'agit de graphiques indépendants que vous souhaitez assembler dans une même figure parce que les données ont un lien entre elles, ou parce que ces graphiques sont complémentaires pour comprendre les données.
Dans le premier cas, les fonctions `facet_XXX()` comme `facet_grid()` peuvent être employées (ou l'opérateur `|` dans une formule). Dans le second cas, la fonction `combine_charts()` est l'une des alternatives possibles.
### Facettes
L'une des règles les plus importantes que vous devez impérativement garder à l'esprit lors de la réalisation de vos graphiques est *la simplicité*. Plus votre graphique contiendra d'information, plus il sera compliqué à décoder par vos lecteurs.
```{r chick1, fig.cap= "Croissance de poulets en utilisant quatre aliments différents."}
# Importation de données relative à la croissance de poulets
(chick_weight <- read("ChickWeight", package = "datasets", lang = "fr"))
# Réalisation du graphique (points semi-transparents)
chart(data = chick_weight, weight ~ time %col=% diet) +
geom_point(alpha = 0.5)
```
Le graphique à la Fig. \@ref(fig:chick1) est mal adapté pour montrer les différences entre les quatre aliments : tous les points sont entremêlés. Il peut typiquement être simplifié en utilisant des facettes pour représenter les résultats relatifs aux différents régimes alimentaires sur des graphiques séparés. L'information est la même, mais la lecture est beaucoup plus aisée.
```{r chick-facette, fig.cap= "Croissance de poulets en utilisant quatre aliments différents (1-4)."}
chart(data = chick_weight, weight ~ time | diet) +
geom_point(alpha = 0.5)
```
Vous observez que les échelles en abscisse et en ordonnée sont similaires sur tous les graphiques. Cela permet une meilleure comparaison. Notez toutefois que, plus le nombre de facettes augmente, plus chaque graphique individuel devient petit. Faites attention à ne pas finir avec des graphiques individuels tellement petits qu'ils en deviennent illisibles !
### Graphiques assemblés
La fonction `combine_charts()` permet de combiner plusieurs graphiques dans une figure unique. Nous l'avons déjà utilisée à plusieurs reprises. Cette fonction attend une liste (`list()`) de graphiques de type `chart()` à assembler, et il est possible d'en combiner les légendes à l'aide de `common.legend = TRUE`.
```{r combine-charts, fig.cap= "A) Masse d'oursins en fonction de leur taille et de leur origine. B) Masse totale en fonction de la masse des parties solides de ces mêmes oursins."}
# Importation des données
urchin <- read("urchin_bio", package = "data.io", lang = "FR")
# Réalisation des graphiques
a <- chart(data = urchin, weight ~ height %col=% origin) +
geom_point()
b <- chart(data = urchin, weight ~ solid_parts %col=% origin) +
geom_point()
# Combinaison des graphiques dans une même figure
combine_charts(list(a, b), common.legend = TRUE)
```
Il existe d'autres fonctions permettant de combiner plusieurs graphiques comme [`cowplot::plot_grid()`](https://wilkelab.org/cowplot/articles/plot_grid.html)`ou encore, les fonctions du package {patchwork}`, mais avec `combine_charts()` vous pourrez déjà faire beaucoup. De plus, un libellé sous forme d'une lettre majuscule est automatiquement associé à chaque sous-région de la figure composée. Cela permet d'y faire plus facilement référence dans le texte et/ou dans la légende.
##### À vous de jouer ! {.unnumbered}
`r h5p(213, height = 270, toc = "Paires de graphiques")`
##### À vous de jouer ! {.unnumbered}
`r learnr("A04Lc_comp_fig", title = "Graphiques composés", toc = "Graphiques composés")`
```{r assign_A04Ia_graphe_avance, echo=FALSE, results='asis'}
if (exists("assignment"))
assignment("A04Ia_graphe_avance", part = NULL,
url = "https://github.com/BioDataScience-Course/A04Ia_graphe_avance",
course.ids = c(
'S-BIOG-006' = !"A04Ia_{YY}M_graphe-avance"),
course.urls = c(
'S-BIOG-006' = "https://classroom.github.com/a/7dZBL1S6"),
course.starts = c(
'S-BIOG-006' = !"{W[10]+1} 13:00:00"),
course.ends = c(
'S-BIOG-006' = !"{W[11]+1} 23:59:59"),
term = "Q1", level = 3,
toc = "Graphiques avancés (barres boites de dispersion et composites)")
```
##### Pour en savoir plus {.unnumbered}
- [Partitionnement des graphiques en facettes](http://r.qcbs.ca/workshop03/book-fr/utiliser-des-facettes-pour-diviser-votre-figure.html). Différentes options sont présentées ici.
- [Figures composées à l'aide de `cowplot::plot_grid()`](https://wilkelab.org/cowplot/reference/plot_grid.html) avec les différentes options, aussi disponibles avec `combine_charts()`.
- [Seconde possibilité pour des figures composées à l'aide de `ggpubr::ggarrange()`](http://www.sthda.com/english/rpkgs/ggpubr/reference/ggarrange.html). `combine_charts()` fait la même chose, mais avec des valeurs par défaut légèrement différentes (`labels = "auto"` par défaut pour ce dernier, mais `labels = NULL` pour `ggpubr::ggarrange()`).
- [Site du package {patchwork}](https://patchwork.data-imaginist.com). Une façon puissante et intuitive de réaliser des graphiques composites dans R.
<!--
## Choix du graphique
Les différents graphiques que vous avez étudiés dans les trois modules ont tous des rôles différents. Il est très important de choisir le bon graphique en fonction de ce que vous voulez mettre en évidence dans les données. Nous vous proposons maintenant d'établir un **diagramme décisionnel** en fonction de :
- nombre de variables à représenter (1, 2, 3, ou 4)
- type de la ou des variables (qualitatif non ordonné **factor**, qualitatif ordonné **ordered**, quantitatif discret **integer** ou quantitatif continu **numeric**)
- quantité de données à représenter
- question que l'on se pose
Vous utilisez le logiciel que vous voulez pour créer ce diagramme. Les logiciels de "mind mapping" tel que [Coggle](https://coggle.it) se prêtent très bien à cet exercice. Une fois votre document terminé, placez-le dans le dépôt GitHub suivant afin que vous puissiez rapidement le récupérer. **Nous vous conseillons de garder ce document à disposition plus tard lorsque vous aurez des graphiques à réaliser.**
{r assign_A04Ga_decision_tree, echo=FALSE, results='asis'}
if (exists("assignment2"))
assignment2("A04Ga_decision_tree", part = NULL,
url = "https://github.com/BioDataScience-Course/A04Ga_decision_tree",
course.ids = c(
'S-BIOG-006' = !"A04Ga_{YY}M_decision tree"),
course.urls = c(
'S-BIOG-006' = "https://classroom.github.com/a/..."),
course.starts = c(
'S-BIOG-006' = !"{W[10]+5} 08:00:00"),
course.ends = c(
'S-BIOG-006' = !"{W[10]+5} 23:59:59"),
term = "Q1", level = 4, n = 5,
toc = "Diagramme décisionnel des graphiques")
-->
## Différents moteurs graphiques
<center>

</center>
Depuis le début, l'ensemble des graphiques que nous vous avons proposés utilise la fonction `chart()` du package {chart}. Cependant, il ne s'agit pas de la seule fonction permettant de réaliser des graphiques dans R, loin de là. En fait {chart} est tout récent et a été développé pour homogénéiser autant que possible les graphiques issus de trois moteurs graphiques différents : {ggplot2}, {lattice} et les graphiques de base. La fonction `chart()` a d'autres avantages également :
- Un thème par défaut qui est le plus proche possible d'un rendu typique d'une publication scientifique.
- La possibilité d'utiliser l'interface formule avec {ggplot2}.
- La cohérence des objets graphiques obtenus qui peuvent tous être combinés en une figure composite, même si ils sont produits avec des moteurs graphiques différents.
- Un libellé automatique des axes et autres éléments du graphique en fonction des attributs `label` et `units` des variables (pour l'instant, seulement les graphiques de type {ggplot2}).
```{r chart-example, fig.cap= "Graphique typique obtenu avec `chart()` : rendu par défaut publiable tel quel, et libellé automatique des axes avec les unités."}
# Importation des données
(urchin <- read("urchin_bio", package = "data.io", lang = "FR"))
# Réalisation du graphique
chart(data = urchin, height ~ weight %col=% origin) +
geom_point()
```
### {ggplot2}
Le moteur graphique {[ggplot2](https://ggplot2.tidyverse.org)} est écrit pas Hadley Wickham, un personnage emblématique de la "révolution [tidyverse](https://www.tidyverse.org)" (même si ggplot est antérieur et n'utilise pas la syntaxe commune aux packages tidyverse) qui propose une surcouche moderne au-dessus de R. {ggplot2} implémente une "grammaire graphique" particulièrement puissante et flexible, imaginée et popularisée par le statisticien Leland Wilkinson. Par défaut, `chart()` crée en réalité un graphique {ggplot2} adapté. Voici la version {ggplot2} standard du même graphique représenté à la Fig. \@ref(fig:chart-example) :
```{r ggplot2-example, fig.cap= "Graphique typique obtenu avec `ggplot()` (moteur graphique {ggplot2})."}
ggplot(data = urchin, mapping = aes(x = weight, y = height, col = origin)) +
geom_point()
```
En comparant les Figs. \@ref(fig:chart-example) et \@ref(fig:ggplot2-example) (en faisant abstraction des instructions R utilisées pour l'instant), plusieurs éléments sautent immédiatement aux yeux :
- Le thème par défaut de {ggplot2} est très reconnaissable avec un quadrillage blanc sur fond gris clair. On aime ou on n'aime pas, mais il est évident que (1) ce n'est pas une présentation "standard" d'un graphique scientifique, et (2) le thème tord un peu le cou à une règle importante pour réaliser un graphique de qualité : **minimiser la quantité d'"encre" nécessaire pour représenter un graphique**, autrement dit, plus le graphique est simple et sobre, mieux c'est. Le thème par défaut de `chart()` respecte mieux tout ceci[^04-visualisation-iii-4].
- La taille des caractères est légèrement plus grande dans la Fig. \@ref(fig:chart-example) réalisée avec `chart()` (surtout pour les nombres sur les axes). Le manque de lisibilité des parties textuelles dans un graphique est un défaut fréquent, dépendant de la résolution et de la taille de reproduction du graphique dans le document final. Le choix de `chart()` recule un peu ce risque.
- `chart()` est capable d'aller lire les métadonnées (libellés en français et unités des variables) et les utilise automatiquement pour proposer des libellés corrects et complets des axes par défaut. `ggplot()` ne peut pas le faire, et il faut utiliser la fonction `labs()` pour l'indiquer manuellement.
[^04-visualisation-iii-4]: Notez que plusieurs thèmes existent dans **ggplot2**. Il est facile d'en changer et de les personnaliser... mais c'est toujours appréciable d'avoir un rendu impeccable dès le premier essai.
```{block, type='info'}
De manière générale, par rapport à `ggplot()`, `chart()` a été conçu pour produire le graphique le plus proche d'un rendu final impeccable avec tous les paramètres par défaut.
```
Quelques règles simples vous permettent de passer des **instructions** `ggplot()` à `chart()` et *vice versa*[^04-visualisation-iii-5] :
[^04-visualisation-iii-5]: Étant donné l'abondante littérature écrite sur {ggplot2}, il est utile de pouvoir convertir des exemples {ggplot2} en graphiques `chart()`, si vous êtes convaincu par cette nouvelle interface.
1. On peut toujours remplacer `ggplot()` par `chart()` dans les instructions R (à condition que le package {chart} soit chargé bien sûr, par exemple via `SciViews::R`). Dans ce cas, le thème par défaut diffère, et le libellé automatique des axes (non disponible avec `ggplot()`) est activé.
2. Avec `chart()` on peut utiliser `aes()` pour spécifier les "esthétiques" (éléments à visualiser sur le graphique) comme pour `ggplot()`, mais on peut *aussi* utiliser une interface formule plus compacte. Cette interface formule rapproche la version `chart()` des graphiques {ggplot2} d'un autre moteur de graphique dans R : {lattice}.
3. Outre les esthétiques classiques `x` et `y`, l'interface formule de `chart()` permet d'en inclure d'autres directement dans la formule à l'aide d'opérateurs spécifiques `%<esth>%=`. Par exemple, `aes(x = weight, y = height, col = origin)` dans la Fig. \@ref(fig:ggplot2-example) se traduit en la formule plus concise `height ~ weight %col=% origin` avec `chart()` (notez la position *inversée* de `x` et `y` dans la formule puisqu'on a `y ~ x`). **Tous** les esthétiques de {ggplot2} sont supportés de cette manière.
4. Partout où `aes()` est utilisé pour les instructions {ggplot2}, on peut utiliser à la place `f_aes()` et y spécifier plutôt une formule de type `chart()`.
5. Avec `ggplot()` les facettes doivent être spécifiées à l'aide de `facet_XXX()`. À condition d'utiliser `chart()`, il est possible d'inclure les spécifications des facettes les plus utilisées directement dans la formule en utilisant l'opérateur `|`. Cette façon de procéder est identique à ce qui se fait dans {lattice} (voir plus loin).
Le point (5) mérite une petite démonstration pour comparaison :
```{r chart-ggplot2-facets, fig.cap= "Graphique à facettes. A. version `chart()`, B. version `ggplot()`."}
a <- chart(data = urchin, height ~ weight | origin) +
geom_point()
b <- ggplot(data = urchin, mapping = aes(x = weight, y = height)) +
geom_point() +
facet_grid( ~ origin)
combine_charts(list(a, b))
```
Enfin, pour ceux qui n'aiment pas la notation utilisant le `+` (car elle fait trop penser à une addition, ce qu'elle ne fait pas ici), vous pouvez aussi utiliser la fonction `Sgg()` du package {chart}. Cette fonction transforme toutes les commandes {ggplot2} / {chart} qui doivent être combinées à l'aide de `+` en commandes pouvant être combinées à l'aide de l'opérateur de pipe de R de base `|>`. Ce dernier opérateur est typiquement utilisé pour composer une instruction complexe à l'aide de plusieurs instructions simples (les blocs de construction). Il est donc plus logique ici. De l'aveu même de son auteur, {ggplot2} aurait utilisé `|>` à la place de `+` si cet opérateur existait à l'époque où ggplot a été conçu. Donc, `Sgg()` vient en quelque sorte corriger le tir. Pour utiliser `Sgg()`, vous remplacez le `+` par `|>`, et vous débutez le nom de votre fonction {ggplot2} par `Sgg$` (seule exception: `ggtitle()` ne devient pas `Sgg$ggtitle()`, mais `Sgg$title()`). C'est tout. Donc, vous écrirez :
```{r chart-ggplot2-gg, fig.cap= "Graphique réalisé en utilisant `gg$`."}
chart(data = urchin, height ~ weight) |>
Sgg$geom_point()
```
De plus, vous bénéficiez pleinement de l'aide via la complétion de RStudio avec `Sgg()`. En effet, lorsque vous avez entré `Sgg$` dans l'éditeur ou à la fenêtre console de RStudio, une liste apparaît avec toutes les options possibles.
##### Exercez-vous {.unnumbered}
```{block, type = "bdd"}
Pour vous exercer à réaliser des graphiques `chart()`/`ggplot()`, lancez votre machine virtuelle dans Saturn Cloud. Fermer le projet s'il y en a un d'ouvert. Créer un script R. Chargez les données selon votre envie et réalisez trois graphiques inédits (qui n'ont pas été vu jusqu'ici). Consultez les liens suivants pour vous inspirer :
- <https://www.r-graph-gallery.com>
- <http://r-statistics.co/Top50-Ggplot2-Visualizations-MasterList-R-Code.html>
```
##### À vous de jouer ! {.unnumbered}
Complétez votre projet de groupe en réalisant des graphiques de distribution pertinents.
```{r assign_A02Ga_analysis_III, echo=FALSE, results='asis'}
if (exists("assignment2"))
assignment2("A02Ga_analysis", part = "III",
url = "https://github.com/BioDataScience-Course/A02Ga_analysis",
course.ids = c(
'S-BIOG-006' = !"A02Ga_{YY}M_analysis"),
course.urls = c(
'S-BIOG-006' = "https://classroom.github.com/a/L0JZAlcP"),
course.starts = c(
'S-BIOG-006' = !"{W[8]+1} 13:00:00"),
course.ends = c(
'S-BIOG-006' = !"{W[15]+2} 23:59:59"),
term = "Q1", level = 4, n = 4,
toc = "Analyse de données (partie III)")
```
##### Pour en savoir plus {.unnumbered}
- Chapitre [Data visualisation](http://r4ds.had.co.nz/data-visualisation.html) de R for Data Science qui utilise `ggplot()`.
- Site rassemblant des [extensions pour ggplot2](https://exts.ggplot2.tidyverse.org/gallery/)
**La suite de cette section est facultative : elle est importante pour comprendre les différents types de graphiques que vous allez rencontrer avec R. Cependant, si vous vous cantonnez aux graphiques `chart()`/`ggplot()` vous pouvez déjà réaliser énormément de visualisations différentes sans forcément connaitre les autres moteurs graphiques existants dans R.**
### {lattice}
Autant {ggplot2} est complètement modulable en ajoutant littéralement à l'aide de l'opérateur `+` des couches successives sur le graphique, autant {lattice} vise à réaliser les graphiques **en une seule instruction**. {lattice} utilise également abondamment l'interface formule pour spécifier les variables à utiliser dans le graphique. La version {lattice} du graphique d'exemple est présentée à la Fig. \@ref(fig:lattice-example).
```{r lattice-example, fig.cap= "Graphique exemple réalisé avec **lattice**."}
xyplot(height ~ weight, data = urchin, groups = origin, auto.key = TRUE)
```
Et voici la version `chart()` utilisant le moteur {lattice}. Notez la façon d'appeler la fonction `xyplot()` de {lattice} via `chart$xyplot()` :
```{r chart-lattice-example, fig.cap= "Graphique exemple réalisé avec `chart()` A. avec le moteur **lattice**, B. avec le moteur **ggplot2**."}
theme_sciviews_lattice(n = 2)
a <- chart$xyplot(height ~ weight, data = urchin, groups = origin,
auto.key = list(space = "right", title = "Origine", cex.title = 1, columns = 1),
ylab = "Hauteur du test [mm]", xlab = "Masse totale [g]",
par.settings = list(superpose.symbol = list(col = scales::hue_pal()(2))))
b <- chart(data = urchin, height ~ weight %col=% origin) +
geom_point()
combine_charts(list(a, b))
```
La quantité d'instructions nécessaires pour rendre la version {lattice} proche de la version {ggplot2} devrait disparaître dans les prochaines versions de `chart()`. Un autre objectif est aussi de gommer le plus possible les différences entre les rendus des différents moteurs de graphiques R, et en particulier entre {ggplot2} et {lattice}. Comparez la Fig. \@ref(fig:chart-lattice-example)A avec la Fig. \@ref(fig:lattice-example) pour apprécier le gain déjà obtenu en matière d'homogénéisation.
```{block, type='info'}
Par rapport à {ggplot2}, les graphiques {lattice} sont moins flexibles du fait qu'ils doivent être spécifiés en une seule instruction. Cependant, ils sont beaucoup plus rapides à générer (appréciable quand il y a beaucoup de points à tracer) ! {lattice} offre également quelques types de graphiques non supportés par {ggplot2} comme les graphiques en 3D à facettes, par exemple.
```
Voici un graphique à facettes réalisé avec `chart()` et le moteur {lattice}. Notez que la formule utilisée est *identique* à celle employée pour la version {ggplot2} avec `chart()`.
```{r lattice-facets, fig.cap= "Graphique à facettes, avec `chart()` version {lattice}."}
chart$xyplot(data = urchin, height ~ weight | origin,
scales = list(alternating = 1),
xlab = "Masse totale [g]", ylab = "Hauteur du test [mm]")
```
Mis à part les instructions additionnelles encore nécessaires dans cette version de `chart()`, l'appel et le rendu sont très similaires par rapport à la version {ggplot2} du même graphique avec `chart()` :
```{r ggplot2-facets2, fig.cap= "Graphique à facettes, avec `chart()` version {lattice}."}
chart$xyplot(data = urchin, height ~ weight | origin,
scales = list(alternating = 1),
ylab = "Hauteur du test [mm]", xlab = "Masse totale [g]")
```
Vous noterez que pour réaliser un graphique {lattice} à l'aide de `chart()`, vous devez rajouter `$nom_de_function_lattice` après `chart$`. Aussi, le thème spécifique SciViews et les labels et unités automatiques ne sont pas encore supportés. Les graphiques {lattice} ne supportant pas l'opérateur `+` pour ajouter des couches comme pour {ggplot2}, vous devez spécifier l'ensemble des options comme arguments de la fonction `chart$fun()`. Cela peut devenir pénible s'il y en a beaucoup. Toutefois {lattice} peut rendre d'énormes services pour des graphiques très compliqués, grâce à sa vitesse nettement supérieure à {ggplot2}.
### Graphiques de base
Comme son nom le suggère, le moteur graphique de base est celui qui est implémenté de manière native dans R. Il est donc utilisé un peu partout. Il est vieillissant et est plus difficile à manipuler que {ggplot2} certainement, et même que {lattice}. Néanmoins, il est très flexible et rapide, et encore très utilisé... mais son rendu par défaut n'est plus vraiment au goût du jour. Voici notre graphique d'exemple rendu avec le moteur graphique R de base :
```{r base-example, fig.cap="Graphique exemple réalisé avec le moteur graphique R de base."}
plot(urchin$weight, urchin$height,
col = c("red", "darkgreen")[urchin$origin], pch = 1)
legend(x = 80, y = 10, legend = c("Culture", "Pêcherie"),
col = c("red", "darkgreen"), pch = 1)
```
Vous rencontrerez très fréquemment la fonction `plot()`. C'est une fonction dite **générique** dont le comportement change en fonction de l'objet fourni en premier argument. Ainsi, elle réalise le graphique le plus pertinent à chaque fois en fonction du contexte. Notez tout de suite les instructions un peu confuses nécessaires pour spécifier la couleur souhaitée en fonction de l'origine des oursins. Le moteur graphique de base ne gère **pas** automatiquement des aspects plus complexes du graphique, tels que le positionnement d'une légende. Donc, à moins d'avoir prévu la place suffisante *avant* de tracer le graphique, nous ne pouvons que l'inclure à l'intérieur du cadre du graphique dans un second temps à l'aide de la fonction `legend()`. Comme cette dernière ne comprend rien à ce qui a été réalisé jusqu'ici, il faut lui respécifier les couleurs, formes et tailles de points utilisés ! C'est un des aspects pénibles du moteur graphique R de base.
Voici maintenant une version `chart()` de ce graphique de base :
```{r chart-base, fig.keep=2, fig.cap="Graphique exemple réalisé avec le moteur graphique de base et la fonction `chart()`."}
chart$base({
par(mar = c(5.1, 4.1, 4.1, 6.1))
plot(urchin$weight, urchin$height,
col = scales::hue_pal()(2)[urchin$origin], pch = 19, cex = 0.8,
xlab = "Masse totale [g]", ylab = "Hauteur du test [mm]")
legend(x = 105, y = 20, legend = c("Culture", "Pêcherie"), title = "Origine",
col = scales::hue_pal()(2), pch = 19, bty = "n", cex = 0.8, y.intersp = 2)
})
```
```{block, type='note'}
Vous ne le voyez pas dans le bookdown, mais vous le réaliserez si vous utilisez ce genre de code dans vos propres documents Quarto ou R Markdown : le graphique est en réalité généré deux fois : une première fois dans un format propre aux graphiques R de base, et ensuite, il est traduit en une forme compatible avec les autres graphiques {ggplot2} et {lattice} (et au passage, il gagne la grille en traits grisés). Dans le chunck, nous devons spécifier `fig.keep = 2` si nous voulons éviter d'imprimer la première version dans le rapport lorsqu'on utilise `chart$base()`.
```
Pour l'instant, le seul avantage de `chart()` avec les graphiques de base est qu'il les convertit en une forme combinable avec les autres graphiques dans une figure composite (sinon, ce n'est pas possible). À part cela, il faut fournir à `chart$base()` tout le code nécessaire pour tracer et personnaliser le graphique. Comme on peut le voir sur cet exemple, cela demande une quantité considérable de code. C'est aussi un autre aspect pénible de ce moteur graphique : il est très flexible, mais l'interface n'est pas optimale. Pour finir, les graphiques de base ont plus de mal avec les facettes, mais ils peuvent quand même générer les versions les plus simples, par exemple à l'aide de la fonction `coplot()` qui accepte une formule très similaire à ce que nous avons employé jusqu'ici, mais avec un rendu différent :
```{r coplot-example, fig.cap="Graphique à facettes avec le moteur graphique de base."}
coplot(data = urchin, height ~ weight | origin)
```
À l'issue de cette comparaison, vous pourrez décider du moteur graphique que vous préférerez utiliser. Dans le cadre de ce cours, nous n'utiliserons en tous cas que quasi exclusivement des graphiques {ggplot2} créés à l'aide la fonction `chart()`.
##### Pour en savoir plus {.unnumbered}
- [Introduction rapide à lattice](https://www.statmethods.net/advgraphs/trellis.html)
- [Variantes de graphiques avec lattice](https://www.r-bloggers.com/conditioning-and-grouping-with-lattice-graphics/)
- [Comparaison de lattice et ggplot2](https://learnr.wordpress.com/2009/08/26/ggplot2-version-of-figures-in-lattice-multivariate-data-visualization-with-r-final-part/). Cette page fait aussi référence à un ensemble de graphiques différents générés en {lattice} et en {ggplot2} pour comparaison (en anglais).
- [Chapitre d'un libre sur R présentant les graphiques de base](https://bookdown.org/rdpeng/exdata/the-base-plotting-system-1.html)
- [ggplot2 comparé aux graphiques R de base](https://flowingdata.com/2016/03/22/comparing-ggplot2-and-r-base-graphics/). Un point de vue différent d'un utilisateur habitué aux graphiques R de base (en anglais).
## Challenge
Tout au long des quatre premiers modules, vous avez appris à utiliser R et RStudio, à écrire des documents en R Markdown, à utiliser git et GitHub... Vous avez également appris à réaliser des graphiques clairs qui présentent visuellement l'information contenue dans des jeux de données. Il est maintenant temps de faire un bilan de tout cela sous une forme ludique : un **challenge de rapidité pour la réalisation de graphiques.** Vous vous confronterez entre vous de manière individuelle pour déterminer celui ou celle qui arrivera à reproduire fidèlement le plus de graphiques possible.
##### À vous de jouer ! {.unnumbered}
```{r assign_A04Ca_charts, echo=FALSE, results='asis'}
if (exists("challenge"))
challenge("A04Ca_charts", part = NULL,
url = "https://github.com/BioDataScience-Course/A04Ca_charts",
course.ids = c(
'S-BIOG-006' = !"A04Ca_{YY}M_charts"),
course.urls = c(
'S-BIOG-006' = "https://classroom.github.com/a/0SFsIPXz"),
course.starts = c(
'S-BIOG-006' = !"{W[10]+5} 10:00:00"),
course.ends = c(
'S-BIOG-006' = !"{W[10]+5} 13:00:00"),
toc = "Challenge graphiques")
```
::: nocourse
Vous n'êtes pas correctement enregistré pour ce cours. Vous ne pouvez pas participer à ce challenge. Si vous êtes étudiant•e de l'UMONS, vérifiez que vous vous êtes bien enregistré•e via Moodle...
:::