3.1 Philosophie du ggplot2

Le package ggplot2 fait partie du tidyverse et dispose d’une logique de fonctionnement particulière. Cette dernière se nomme The Grammar of Graphics (les deux G sont d’ailleurs à l’origine du nom ggplot2), proposée par Hadley Wickham (le créateur du tidyverse!) dans un article intitulé A layered grammar of graphics (Wickham 2010). Nous proposons de synthétiser ici les concepts et principes centraux qui sous-tendent la production de graphiques avec ggplot2.

3.1.1 Grammaire

Hadley Wickham propose une grammaire pour unifier la création de graphiques. L’idée est donc de dépasser les simples dénominations comme un nuage de points, un diagramme en boîte, un graphique en ligne, etc., pour comprendre ce qui relie tous ces graphiques. Ces éléments communs et centraux sont les géométries, les échelles et systèmes de coordonnées, et les annotations (figure 3.1) :

Trois composantes d'un graphique, adapté de @wickham2010layered

Figure 3.1: Trois composantes d’un graphique, adapté de Wickham (2010)

  • Les géométries sont les formes utilisées pour représenter les données. Il peut s’agir de points, de lignes, de cercles, de rectangles, d’arcs de cercle, etc.
  • Les échelles et systèmes de coordonnées permettent de contrôler la localisation des éléments dans un graphique en convertissant les données depuis leur échelle originale (dollars, kilomètres, pourcentages, etc.) vers l’échelle du graphique (pixels).
  • Les annotations recoupent l’ensemble des informations complémentaires ajoutées au graphique comme son titre et sous-titre, la source des données, la mention sur les droits d’auteurs, etc.

En plus de ces trois éléments, il est bien sûr nécessaire de disposer de données. Ces dernières sont assignées à des dimensions du graphique pour être représentées (notamment les axes X et Y et la couleur). Cette étape est appelée aesthetics mapping dans ggplot2.

Lorsque nous combinons des données, leur assignation a des dimensions, un type de géométries, des échelles et un système de coordonnées, nous obtenons un calque (layer en anglais). Un graphique peut comprendre plusieurs calques comme nous le verrons dans les prochaines sections.

Prenons un premier exemple très simple et construisons un nuage de points à partir du jeu de données iris fourni de base dans R. Nous représentons la relation qui existe entre la longueur et la largeur des sépales de ces fleurs. Pour commencer, nous devons charger le package ggplot2 et instancier un graphique avec la fonction ggplot.

library(ggplot2)
data(iris)
names(iris)
## [1] "Sepal.Length" "Sepal.Width"  "Petal.Length" "Petal.Width"  "Species"
ggplot()
Base d'un graphique

Figure 3.2: Base d’un graphique

Pour le moment, le graphique est vide (figure 3.2). La seconde étape consiste à lui ajouter des données (au travers du paramètre data) et à définir les dimensions à associer aux données (avec le paramètre mapping et la fonction aes()). Dans notre cas, nous voulons utiliser les coordonnées X pour représenter la largeur des sépales, et les coordonnées Y pour représenter la longueur des sépales. Enfin, nous souhaitons représenter les observations par des points, nous utiliserons donc la géométrie geom_point.

ggplot(mapping = aes(x = Sepal.Length, y = Sepal.Width), data = iris) +
  geom_point()
Ajout des dimensions au graphique

Figure 3.3: Ajout des dimensions au graphique

Ce graphique ne comprend qu’un seul calque avec une géométrie de type point (figure 3.3). Chaque calque est ajouté avec l’opérateur + qui permet de superposer des calques, le dernier apparaissant au-dessus des autres. Les arguments mapping et data sont définis ici dans la fonction ggplot et sont donc appliqués à tous les calques qui composent le graphique. Il est aussi possible de définir mapping et data au sein des fonctions des géométries :

ggplot() +
  geom_point(mapping = aes(x = Sepal.Length, y = Sepal.Width), data = iris)
Autre spécification des arguments mapping et data

Figure 3.4: Autre spécification des arguments mapping et data

La troisième étape consiste à ajouter au graphique des annotations. Pour notre cas, il faudrait ajouter un titre, un sous-titre et des intitulés plus clairs pour les axes X et Y, ce qu’il est possible de faire avec la fonction labs (figure 3.5).

ggplot() +
  geom_point(mapping = aes(x = Sepal.Length, y = Sepal.Width), data = iris) +
  labs(title = "Morphologie des sépales des iris", subtitle = "n = 150",
       x = "Longueur des sépales", 
       y = "Largeur des sépales")
Ajout de titres

Figure 3.5: Ajout de titres

3.1.2 Types de géométries

Le package ggplot2 permet d’utiliser un très grand nombre de géométries différentes. Dans le tableau 3.1, nous avons reporté les principales géométries disponibles afin que vous puissiez vous faire une idée du « bestiaire » existant. Il ne s’agit que d’un extrait des principales fonctions. Sachez qu’il existe aussi des packages proposant des géométries supplémentaires pour compléter ggplot2.

Tableau 3.1: Principales géométries proposées par ggplot2
Géométrie Fonction
point geom_point
ligne geom_line
chemin geom_path
boîte à moustaches geom_boxplot
diagramme violon geom_violin
histogramme geom_histogram
barre geom_bar
densité geom_density
texte geom_label
barre d’erreur geom_errorbar
surface geom_ribbon

3.1.3 Habillage

Dans le premier exemple, nous avons montré comment ajouter le titre, le sous-titre et les titres des axes sur un graphique. Il est aussi possible d’ajouter du texte sous le graphique (généralement la source des données avec l’argument caption) et des annotations textuelles (annotate). Pour ces dernières, il convient de spécifier leur localisation (coordonnées x et y) et le texte à intégrer (label); elles sont ensuite ajoutées au graphique avec l’opérateur +. Ajoutons deux annotations pour identifier deux fleurs spécifiques.

ggplot() +
  geom_point(mapping = aes(x = Sepal.Length, y = Sepal.Width), data = iris) +
  labs(title = "Morphologie des sépales des iris", subtitle = "n = 150",
       x = "Longueur des sépales", 
       y = "Largeur des sépales",
       caption = "Source : jeu de données iris") +
  annotate("text", x = 6.7, y = 2.5, # position de la note
           label = "une virginica",  # texte de la note
           hjust = "left", vjust = "top", # ajustement
           size = 3, fontface = "italic") + 
  annotate("text", x = 5.7, y = 4.4, # position de la note
           label = "une setosa",  # texte de la note
           hjust = "left", vjust = "top", # ajustement
           size = 3, fontface = "italic") 
Ajout d'annotations textuelles

Figure 3.6: Ajout d’annotations textuelles

Comme vous pouvez le constater, de nombreux paramètres permettent de contrôler le style des annotations. Pour avoir la liste des arguments disponibles, n’hésitez pas à afficher l’aide de la fonction : help(annotate).

En plus des annotations de type texte, il est possible d’ajouter des annotations de type géométrique. Nous pourrions ainsi délimiter une boîte encadrant les fleurs de l’espère setosa.

setosas <- subset(iris, iris$Species == "setosa")
sepal.length_extent <- c(min(setosas$Sepal.Length),max(setosas$Sepal.Length))
sepal.width_extent <- c(min(setosas$Sepal.Width),max(setosas$Sepal.Width))

ggplot() +
  geom_point(mapping = aes(x = Sepal.Length, y = Sepal.Width), data = iris) +
  labs(title = "Morphologie des sépales des iris", subtitle = "n = 150",
       x = "Longueur des sépales", 
       y = "Largeur des sépales",
       caption = "Source : jeu de données iris") +
  annotate("text", x = 6.7, y = 2.5, # position de la note
           label = "une virginica",  # texte de la note
           hjust = "left", vjust = "top", # ajustement
           size = 3, fontface = "italic") + 
  annotate("text", x = 5.7, y = 4.4, # position de la note
           label = "une setosa",  # texte de la note
           hjust = "left", vjust = "top", # ajustement
           size = 3, fontface = "italic") +
  annotate("rect", 
           ymin = sepal.width_extent[[1]],
           ymax = sepal.width_extent[[2]],
           xmin = sepal.length_extent[[1]],
           xmax = sepal.length_extent[[2]],
           fill = rgb(0.7,0.7,0.7,.5), # remplissage transparent à 50%
           color = "black") # contour de couleur verte
Ajout d'annotations géométriques

Figure 3.7: Ajout d’annotations géométriques

Comme le dernier calque ajouté au graphique est le rectangle, vous noterez qu’il recouvre tous les calques existant, y compris les précédentes annotations. Pour corriger cela, il suffit de changer l’ordre des calques.

ggplot() +
  annotate("rect", 
           ymin = sepal.width_extent[[1]],
           ymax = sepal.width_extent[[2]],
           xmin = sepal.length_extent[[1]],
           xmax = sepal.length_extent[[2]],
           fill = rgb(0.7,0.7,0.7,.5), # remplissage transparent à 50%
           color = "green") + # contour de couleur verte
  geom_point(mapping = aes(x = Sepal.Length, y = Sepal.Width), data = iris) +
  labs(title = "Morphologie des sépales des iris", subtitle = "n = 150",
       x = "Longueur des sépales", 
       y = "Largeur des sépales",
       caption = "Source : jeu de données iris") +

  annotate("text", x = 6.7, y = 2.5, # position de la note
           label = "une virginica",  # texte de la note
           hjust = "left", vjust = "top", # ajustement
           size = 3, fontface = "italic") + 
  annotate("text", x = 5.7, y = 4.4, # position de la note
           label = "une setosa",  # texte de la note
           hjust = "left", vjust = "top", # ajustement
           size = 3, fontface = "italic")
Gestion de l'ordre des annotations

Figure 3.8: Gestion de l’ordre des annotations

3.1.4 Utilisation des thèmes

De nombreux autres éléments peuvent être modifiés dans un graphique comme les paramètres des polices, l’arrière-plan, la grille de repères, etc. Il peut être fastidieux de paramétrer tous ces éléments. Une option intéressante est d’utiliser des thèmes déjà préconstruits. Le package ggplot2 propose une dizaine de thèmes : constatons leur impact sur le graphique précédent.

  • Le thème classique (theme_classic) (figure 3.9)
ggplot() +
  geom_point(mapping = aes(x = Sepal.Length, y = Sepal.Width), data = iris) +
  labs(title = "Morphologie des sépales des iris", subtitle = "n = 150",
       x = "Longueur des sépales", 
       y = "Largeur des sépales",
       caption = "Source : jeu de données iris") + 
  theme_classic()
Thème classique

Figure 3.9: Thème classique

  • Le thème gris (theme_gray) (figure 3.10)
ggplot() +
  geom_point(mapping = aes(x = Sepal.Length, y = Sepal.Width), data = iris) +
  labs(title = "Morphologie des sépales des iris", subtitle = "n = 150",
       x = "Longueur des sépales", 
       y = "Largeur des sépales",
       caption = "Source : jeu de données iris") + 
  theme_gray()
Thème gris

Figure 3.10: Thème gris

  • Le thème noir et blanc (theme_bw) (figure 3.11)
ggplot() +
  geom_point(mapping = aes(x = Sepal.Length, y = Sepal.Width), data = iris) +
  labs(title = "Morphologie des sépales des iris", subtitle = "n = 150",
       x = "Longueur des sépales", 
       y = "Largeur des sépales",
       caption = "Source : jeu de données iris") + 
  theme_bw()
Thème noir et blanc

Figure 3.11: Thème noir et blanc

  • Le thème minimal (theme_minimal) (figure 3.12)
ggplot() +
  geom_point(mapping = aes(x = Sepal.Length, y = Sepal.Width), data = iris) +
  labs(title = "Morphologie des sépales des iris", subtitle = "n = 150",
       x = "Longueur des sépales", 
       y = "Largeur des sépales",
       caption = "Source : jeu de données iris") + 
  theme_minimal()
Thème minimal

Figure 3.12: Thème minimal

Il est aussi possible d’utiliser le package ggthemes qui apporte des thèmes complémentaires intéressants dont :

  • Le thème tufte (theme_tufte, à l’ancienne…) (figure 3.13)
library(ggthemes)

ggplot() +
  geom_point(mapping = aes(x = Sepal.Length, y = Sepal.Width), data = iris) +
  labs(title = "Morphologie des sépales des iris", subtitle = "n = 150",
       x = "Longueur des sépales", 
       y = "Largeur des sépales",
       caption = "Source : jeu de données iris") + 
  theme_tufte()
Thème tufte

Figure 3.13: Thème tufte

  • Le thème economist (theme_economist, inspiré de la revue du même nom) (figure 3.14)
ggplot() +
  geom_point(mapping = aes(x = Sepal.Length, y = Sepal.Width), data = iris) +
  labs(title = "Morphologie des sépales des iris", subtitle = "n = 150",
       x = "Longueur des sépales", 
       y = "Largeur des sépales",
       caption = "Source : jeu de données iris") + 
  theme_economist()
Thème economist

Figure 3.14: Thème economist

  • Le thème solarized (theme_solarized, plus original) (figure 3.15)
ggplot() +
  geom_point(mapping = aes(x = Sepal.Length, y = Sepal.Width), data = iris) +
  labs(title = "Morphologie des sépales des iris", subtitle = "n = 150",
       x = "Longueur des sépales", 
       y = "Largeur des sépales",
       caption = "Source : jeu de données iris") + 
  theme_solarized()
Thème solarized

Figure 3.15: Thème solarized

Il en existe bien d’autres et vous pouvez composer vos propres thèmes. N’hésitez pas à explorer la documentation de ggplot2 et de ggthemes pour en apprendre plus!

3.1.5 Composition d’une figure avec plusieurs graphiques

Il est très fréquent de vouloir combiner plusieurs graphiques dans une même figure. Deux cas se distinguent :

  1. Les données pour les différents graphiques proviennent du même DataFrame et peuvent être distinguées selon une variable catégorielle. L’objectif est alors de dupliquer le même graphique, mais pour des sous-groupes de données. Dans ce cas, nous recommandons d’utiliser la fonction facet_wrap de ggplot2.

  2. Les graphiques sont complètement indépendants. Dans ce cas, nous recommandons d’utiliser la fonction ggarrange du package ggpubr.

3.1.5.1 ggplot2 et ses facettes

Nous pourrions souhaiter réaliser une figure composite avec le jeu de données iris et séparer notre nuage de points en trois graphiques distincts selon l’espèce des iris (figure 3.16). Pour cela, il faut au préalable convertir la variable espèce en facteur.

iris$Species_fac <- as.factor(iris$Species)

ggplot() +
  geom_point(mapping = aes(x = Sepal.Length, y = Sepal.Width), data = iris) +
  labs(title = "Morphologie des sépales des iris", subtitle = "n = 150",
       x = "Longueur des sépales", 
       y = "Largeur des sépales",
       caption = "Source : jeu de données iris") +
  facet_wrap(vars(Species_fac), ncol=2)
Graphique à facettes

Figure 3.16: Graphique à facettes

Notez que le nom de la variable (ici Species_fac) doit être spécifié au sein d’une sous-fonction vars : vars(Species_fac). Nous aurions aussi pu réaliser le graphique sur une seule ligne en spécifiant ncol = 3 (figure 3.17).

ggplot() +
  geom_point(mapping = aes(x = Sepal.Length, y = Sepal.Width), data = iris) +
  labs(title = "Morphologie des sépales des iris", subtitle = "n = 150",
       x = "Longueur des sépales", 
       y = "Largeur des sépales",
       caption = "Source : jeu de données iris") +
  facet_wrap(vars(Species_fac), ncol=3)
Graphique à facettes en une ligne

Figure 3.17: Graphique à facettes en une ligne

3.1.5.2 Arrangement des graphiques

La solution avec les facettes est très pratique, mais également très limitée puisqu’elle ne permet pas de créer une figure avec des graphiques combinant plusieurs types de géométries. ggarrange du package ggpubr permet tout simplement de combiner des graphiques déjà existant. Créons deux nuages de points comparant plusieurs variables en fonction de l’espèce des iris, puis combinons-les (figure 3.18). Attribuons également aux points une couleur en fonction de l’espèce des fleurs, afin de mieux les distinguer en associant la variable Species au paramètre color.

library(ggpubr)

plot1 <- ggplot(data = iris) +
  geom_point(aes(x = Sepal.Length, y = Sepal.Width, color = Species)) +
  labs(subtitle = "Caractéristiques des sépales",
       x = "Longueur", 
       y = "Largeur",
       color = "Espèce")

plot2 <- ggplot(data = iris) +
  geom_point(aes(x = Petal.Length, y = Petal.Width, color = Species)) +
  labs(subtitle = "Caractéristiques des pétales",
       x = "Longueur", 
       y = "Largeur",
       color = "Espèce")

liste_plots <- list(plot1, plot2)
comp_plot <- ggarrange(plotlist = liste_plots, ncol = 2, nrow = 1,
          common.legend = TRUE, legend = "bottom") #gérer la légende

annotate_figure(comp_plot,
                top = text_grob("Morphologie des sépales et pétales des iris",
                                face = "bold", size = 12, just = "center"),
                bottom = text_grob("Source : jeu de données iris",
                                face = "italic", size = 8, just = "left")
                )
Figure avec plusieurs graphiques avec ggarrange

Figure 3.18: Figure avec plusieurs graphiques avec ggarrange

Quatre étapes sont nécessaires :

  1. Créer les graphiques et les enregistrer dans des objets (ici plot1 et plot2).
  2. Encapsuler ces objets dans une liste (ici liste_plots).
  3. Composer la figure finale avec la fonction ggarrange.
  4. Ajouter les annotations à la figure composite.

L’argument common.legend permet d’indiquer à la fonction ggarrange de regrouper les légendes des deux graphiques. Dans notre cas, les deux graphiques ont les mêmes légendes, il est donc judicieux de les regrouper. L’argument legend contrôle la position de la légende et peut prendre les valeurs : top, bottom, left, right ou none (absence de légende). La fonction annotate_figure permet d’ajouter des éléments de texte au-dessus, au-dessous et sur les cotés de la figure composite.

3.1.6 Couleur

Dans un graphique, la couleur peut être utilisée à la fois pour représenter une variable quantitative (dégradé de couleur ou mise en classes), ou une variable qualitative (couleur par catégorie). Dans ggplot2, il est possible d’attribuer une couleur au contour des géométries avec l’argument color et au remplissage avec l’argument fill. Il est possible de spécifier une couleur de trois façons dans R :

  • En utilisant le nom de la couleur dans une chaîne de caractère : "chartreuse4". R dispose de 657 noms de couleurs prédéfinis. Pour tous les afficher, utilisez la fonction colors(), qui permet de les visualiser (figure 3.19).
Couleurs de base

Figure 3.19: Couleurs de base

  • En indiquant le code hexadécimal de la couleur. Il s’agit d’une suite de six lettres et de chiffres précédée par un dièse : "#99ff33".

  • En utilisant une notation RGB (rouge, vert, bleu, transparence). Cette notation doit contenir quatre nombres entre 0 et 1 (0 % et 100 %), indiquant respectivement la quantité de rouge, de vert, de bleu et la transparence. Ces quatre nombres sont donnés comme argument à la fonction rgb : rgb(0.6, 1, 0.2, 0).

Le choix des couleurs est un problème plus complexe que la manière de les spécifier. Il existe d’ailleurs tout un pan de la sémiologie graphique dédié à la question du choix et de l’association des couleurs. Une première ressource intéressante est ColorBrewer. Il s’agit d’une sélection de palettes de couleurs particulièrement efficaces et dont certaines sont même adaptées pour les personnes daltoniennes (figure 3.20). Il est possible d’accéder directement aux palettes dans R grâce au package RColorBrewer et la fonction brewer.pal :

library(RColorBrewer)
display.brewer.all()
Palette de couleurs de ColorBrewer

Figure 3.20: Palette de couleurs de ColorBrewer

Une autre ressource pertinente est le site web coolors.co qui propose de nombreuses palettes à portée de clic.

Références

Wickham, Hadley. 2010. « A layered grammar of graphics ». Journal of Computational and Graphical Statistics 19 (1): 3‑28. http://dx.doi.org/10.1198/jcgs.2009.07098.