4.5 Différents moteurs graphiques

Prolifération des standards d’après xkcd.

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 êtres combinés en une figure composée, 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}).
# Importation des données
(urchin <- read("urchin_bio", package = "data.io", lang = "FR"))
# # A tibble: 421 x 19
#    origin diameter1 diameter2 height buoyant_weight weight solid_parts
#    <fct>      <dbl>     <dbl>  <dbl>          <dbl>  <dbl>       <dbl>
#  1 Pêche…       9.9      10.2    5               NA  0.522       0.478
#  2 Pêche…      10.5      10.6    5.7             NA  0.642       0.589
#  3 Pêche…      10.8      10.8    5.2             NA  0.734       0.677
#  4 Pêche…       9.6       9.3    4.6             NA  0.370       0.344
#  5 Pêche…      10.4      10.7    4.8             NA  0.610       0.559
#  6 Pêche…      10.5      11.1    5               NA  0.610       0.551
#  7 Pêche…      11        11      5.2             NA  0.672       0.605
#  8 Pêche…      11.1      11.2    5.7             NA  0.703       0.628
#  9 Pêche…       9.4       9.2    4.6             NA  0.413       0.375
# 10 Pêche…      10.1       9.5    4.7             NA  0.449       0.398
# # … with 411 more rows, and 12 more variables: integuments <dbl>,
# #   dry_integuments <dbl>, digestive_tract <dbl>, dry_digestive_tract <dbl>,
# #   gonads <dbl>, dry_gonads <dbl>, skeleton <dbl>, lantern <dbl>, test <dbl>,
# #   spines <dbl>, maturity <int>, sex <fct>
# Réalisation du graphique
chart(data = urchin, height ~ weight %col=% origin) + 
  geom_point() 
Graphique typique obtenu avec `chart()` : rendu par défaut publiable tel quel, et libellé automatique des axes avec les unités.

Figure 4.23: Graphique typique obtenu avec chart() : rendu par défaut publiable tel quel, et libellé automatique des axes avec les unités.

4.5.1 {ggplot2}

Le moteur graphique {ggplot2} est écrit pas Hadley Wickham, un personnage emblématique de la “révolution tidyverse” qui propose une surcouche moderne au dessus de R. {ggplot2} implémente une “grammaire graphique” particulièrement puissante et flexible, proposé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. 4.23 :

ggplot(data = urchin, mapping = aes(x = weight, y = height, col = origin)) + 
  geom_point() 
Graphique typique obtenu avec `ggplot()` (moteur graphique {ggplot2}).

Figure 4.24: Graphique typique obtenu avec ggplot() (moteur graphique {ggplot2}).

En comparant les Figs. 4.23 et 4.24 (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 ceci16.

  • La taille des caractères est légèrement plus grande dans la Fig. 4.23 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 utilisent 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.

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 versa17 :

  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. 4.24 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(). A 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, encore une fois, identique à ce qui se fait dans {lattice} (voir plus loin).

Le point (5) mérite une petite démonstration pour comparaison :

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))
Graphique à facettes. A. version `chart()`, B. version `ggplot()`.

Figure 4.25: Graphique à facettes. A. version chart(), B. version ggplot().

Exercez-vous

Afin de vous exercer à réaliser des graphiques chart()/ggplot(), proposez trois graphiques inédits (qui n’ont pas été vu jusqu’ici) dans vos différents projets (assignations GitHub Classroom). Employez par exemple les liens suivants pour vous inspirer :

Dans le cadre d’une collaboration entre le cours English For Science et le cours de Science des données I, vous êtes amené créer et à maintenir un blog scientifique.

Ce blog va comprendre des articles qui traitent d’analyse de la littérature de vulgarisation scientifique et de description de données.

Vous devez à présent ajouter un nouvel article qui va décrire un graphique de votre choix.

Les consignes sont disponible dans le README du template https://github.com/BioDataScience-Course/blog-template
Pour en savoir plus

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 existant dans R.

4.5.2 {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. 4.26.

xyplot(height ~ weight, data = urchin, groups = origin, auto.key = TRUE)
Graphique exemple réalisé avec **lattice**.

Figure 4.26: Graphique exemple réalisé avec lattice.

Et voici la version chart() utilisant le moteur {lattice}. Notez la façon d’appeler la fonction xyplot() de {lattice} via chart$xyplot() :

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))
Graphique exemple réalisé avec `chart()` A. avec le moteur **lattice**, B. avec le moteur **ggplot2**.

Figure 4.27: Graphique exemple réalisé avec chart() A. avec le moteur lattice, B. avec le moteur ggplot2.

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 particuliers entre {ggplot2} et {lattice}. Comparez la Fig. 4.27A avec la Fig. 4.26 pour apprécier le gain déjà obtenu en matière d’homogénéisation.

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().

chart$xyplot(data = urchin, height ~ weight | origin,
  scales = list(alternating = 1),
  xlab = "Masse totale [g]", ylab = "Hauteur du test [mm]")
Graphique à facettes, avec `chart()` version {lattice}.

Figure 4.28: Graphique à facettes, avec chart() version {lattice}.

Mise à 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() :

chart(data = urchin, height ~ weight | origin) + 
  geom_point()
Graphique à facettes, avec `chart()` version {ggplot2}.

Figure 4.29: Graphique à facettes, avec chart() version {ggplot2}.

4.5.3 Graphiques de base

Comme son nom le suggère, le moteur graphique de base est celui qui est implémenté de manière natif 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 beaucoup 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 :

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)
Graphique exemple réalisé avec le moteur graphique R de base.

Figure 4.30: Graphique exemple réalisé avec le moteur graphique R de base.

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, telle 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 :

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)
})
Graphique exemple réalisé avec le moteur graphique de base et la fonction `chart()`.

Figure 4.31: Graphique exemple réalisé avec le moteur graphique de base et la fonction chart().

Vous ne le voyez pas dans le bookdown, mais vous le réaliserez si vous utilisez ce genre de code dans vos propres documents 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). A 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 il 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 :

coplot(data = urchin, height ~ weight | origin)
Graphique à facettes avec le moteur graphique de base.

Figure 4.32: Graphique à facettes avec le moteur graphique de base.

A 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

  1. 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.↩︎

  2. É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.↩︎