9 Les graphiques avec ggplot

Dans ce chapitre, nous allons apprendre à créer des graphiques en utilisant le package spécialisé ggplot2 (souvent abrégé sous le nom de ggplot). ggplot est un package très complet et très célèbre, parmi les utilisateurs de R, qui repose sur les principes de la “grammaire des graphiques”. L’idée principale est de concevoir un graphique comme une succession de couches (layers, en anglais) qui se superposent pour, au final, donner le résultat escompté, un peu comme on peut décomposer la structure d’une phrase à l’aide de règles grammaticales.

Pratiquement, tout graphique que l’on peut faire avec R de base (càd. avec le package graphics) peut se faire avec ggplot et vise versa. Mais, lorsqu’il s’agit de réaliser des graphiques plus complexes et plus attractifs, ggplot est souvent plus facile (lorsqu’on le maîtrise!).

Pour commencer, nous avons besoin d’installer, si ce n’est pas déjà fait, et de charger le package ggplot2.

install.packages("ggplot2")
library(ggplot2)

Nous allons continuer à utiliser le jeu de données mpg, disponible dans le package ggplot2, que nous avons utilisé au chapitre précédent. Rappelons rapidement à quoi ressemble la structure des données.

mpg <- transform(mpg, cyl = factor(cyl), trans = factor(trans), drv = factor(drv), fl = factor(fl), class = factor(class))
levels(mpg$trans) <- c(rep("auto", 8), rep("manual", 2))
str(mpg)
'data.frame':   234 obs. of  11 variables:
 $ manufacturer: chr  "audi" "audi" "audi" "audi" ...
 $ model       : chr  "a4" "a4" "a4" "a4" ...
 $ displ       : num  1.8 1.8 2 2 2.8 2.8 3.1 1.8 1.8 2 ...
 $ year        : int  1999 1999 2008 2008 1999 1999 2008 1999 1999 2008 ...
 $ cyl         : Factor w/ 4 levels "4","5","6","8": 1 1 1 1 3 3 3 1 1 1 ...
 $ trans       : Factor w/ 2 levels "auto","manual": 1 2 2 1 1 2 1 2 1 2 ...
 $ drv         : Factor w/ 3 levels "4","f","r": 2 2 2 2 2 2 2 1 1 1 ...
 $ cty         : int  18 21 20 21 16 18 18 18 16 20 ...
 $ hwy         : int  29 29 31 30 26 26 27 26 25 28 ...
 $ fl          : Factor w/ 5 levels "c","d","e","p",..: 4 4 4 4 4 4 4 4 4 4 ...
 $ class       : Factor w/ 7 levels "2seater","compact",..: 2 2 2 2 2 2 2 2 2 2 ...

9.1 Les bases de ggplot

La création d’un graphique ggplot débute avec l’appel de la fonction ggplot(). Les couches supplémentaires (layers) sont ajoutées en ayant recours au symbole +. La syntaxe est la suivante :

ggplot(<data.name>) + aes(<variables>) + geom_<xxx>(...)
# où, de manière équivalente,
ggplot(data = <data.name>, mapping = aes(<variables>)) +
  geom_<xxx>(...)

Pour améliorer la lisibilité du code, il est conseillé d’utiliser le dernier format, à savoir commencer par écrire ggplot(..., aes(...)), puis ajouter une nouvelle ligne à l’ajout de chaque nouvelle couche. Dans ce cas, + doit figurer à la fin de la ligne et non au début.

Dans la syntaxe ci-dessus

  • ggplot() spécifie le jeu de données <data.name> à analyser. <data.name> doit être un objet de type data frame.

  • aes() spécifie les variables à visualiser et associe à chaque variable un emplacement ou un rôle: les axes (x, y), couleur (color), forme (shape), taille (size), etc.; voir la Section 9.2 pour plus à ce sujet.

  • geom_<xxx>(), où <xxx> doit être remplacée par le nom d’une forme géométrique, spécifie le type de représentation graphique souhaitée: point_point() pour tracer des points, geom_line() pour des lignes, point_bar() pour des barres, geom_histogram() pour un histogramme, point_boxplot() pour un boxplot, geom_density() pour une densité, geom_smooth() pour une courbe de tendance, etc.; voir la Section 9.3 pour plus à ce sujet.

Nous allons commencer par réaliser un diagramme à point (scatter plot) en utilisant geom_point(). Voici le code.

ggplot(data   = mpg,              # spécifier les données
      mapping = aes(x = displ,    # mapper 'displ' à l'axe des x
                    y = hwy)) +   # mapper 'hwy' à l'axe des y
  geom_point()                    # tracer les points

Le code ci-dessus peut être décomposé comme suite (exécutez le script ligne par ligne)

ggplot(mpg)                                          # gauche
ggplot(mpg) + aes(x = displ, y = hwy)                # milieu
ggplot(mpg) + aes(x = displ, y = hwy) + geom_point() # droite

Nous pouvons sauvegarder un ggplot dans un objet comme n’importe quel autre objet R.

p <- ggplot(mpg, aes(x = displ, y = "")) + geom_point()
p

Si vous examinez la structure de l’objet p, en tapant str(p), vous constateriez que p est une liste de 9 éléments dont les noms sont

names(p)
[1] "data"        "layers"      "scales"      "mapping"     "theme"      
[6] "coordinates" "facet"       "plot_env"    "labels"     

C’est l’ensemble de ces éléments qui définissent de façon unique le ggplot p.
Par la suite, le ggplot sauvegardé peut être modifié en y ajoutant/supprimant des éléments. Voici un exemple.

p + aes(y = hwy)                # gauche
p + aes(y = hwy) + geom_line()  # droite

Titres et noms des axes

La fonction labs() permet de titrer un ggplot, lui donner un sous titre, modifier les noms des axes, etc.. Reprenons notre précédent graphique et améliorons le.

p <- p + aes(y = hwy) + labs(
  title    = "Fuel economy for 38 popular models of cars",
  subtitle = "data from 1999 to 2008",
  x        = "Engine displacement, in litres",
  y        = "Highway miles per gallon",
  caption  = "mpg data from the ggplot2 package")
p

Modifier le thème

On peut personnaliser un ggplot en lui donnant un look particulier. La façon la plus simple pour faire cela est d’utiliser l’un des nombreux thèmes existants, que l’on peut facilement appliquer à l’aide des fonctions theme_<xxx>().

p + theme_gray()    # gauche  : the default theme
p + theme_bw()      # droite  : black & white theme

On peut changer le thème pour la session R en cours en utilisant la fonction theme_set() comme suit :

theme_set(theme_bw())

La fonction theme() permet de modifier les composantes d’un thème. Voici un exemple

p + theme_bw() + theme(
  axis.title = element_text(colour = "blue", face = "bold"),
  plot.title = element_text(colour = "blue", face = "bold"),
  plot.subtitle = element_text(colour = "blue", face = "bold"))

Et la fonction theme_set() permet d’appliquer de tels changements globalement (pour tous les ggplot de la session R en cours).

theme_set(theme_bw() + theme(
  axis.title = element_text(colour = "blue", face = "bold"),
  plot.title = element_text(colour = "blue", face = "bold"),
  plot.subtitle = element_text(colour = "blue", face = "bold")))

La fonction qplot()

Une façon souvent plus simple pour réaliser des ggplots est la fonction qplot(), càd. quick plot. Sa syntaxe est très simple et très similaire à celui de la fonction R de base plot(). Voici quelques exemples.

qplot(x = displ, y = hwy, data = mpg)
Warning: `qplot()` was deprecated in ggplot2 3.4.0.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
generated.
qplot(x = displ, y = hwy, data = mpg, color = I("blue"))
qplot(x = displ, y = hwy, data = mpg, color = drv)
qplot(x = displ, y = hwy, data = mpg, color = drv, geom = "line")

Cette fonction peut être utilisée pour tracer tout type de graphiques, mais elle est moins robuste que ggplot() surtout lorsqu’il s’agit de faire des graphiques complexes. C’est pour cette raison que nous n’allons pas l’utiliser par la suite.

9.2 Propriétés esthétiques

Dans un ggplot, le terme “esthétique” fait référence aux éléments de style utilisés pour visualiser des variables du data.frame de travail et non à l’affichage des éléments externes (non-data), tels que les titres, les étiquettes des axes, la couleur d’arrière-plan, etc., discuté ci-dessus.

Voici un exemple qui illustre comment contrôler les esthétiques d’un ggplot.

ggplot(mpg, aes(x = displ, y = hwy)) + geom_point(shape = 18, size = 3, color = "blue")
ggplot(mpg, aes(x = displ, y = hwy)) + geom_line(color = "red", linetype = 3)

En plus de l’axe des abscisses (x) et l’axe des ordonnées (y), les arguments esthétiques dans ces deux graphiques sont shape (forme), color (couleur), size (taille des symboles), et linetype (type de ligne). Il existe d’autres arguments qui permettent de contrôler d’autres aspects de l’affichage des données, comme par exemple l’argument fill pour la couleur du remplissage, et l’argument alpha pour la transparence. Nous allons découvrir plus sur ces arguments et leurs utilisations au fur et à mesure.

Les arguments esthétiques qu’on peut utiliser dans un ggplot dépendent des geoms (points, lines, bars, etc.) souhaités. Beaucoup de geoms partagent les mêmes arguments esthétiques. Par exemple, en plus de x et y, les arguments esthétiques clés de geom_point() sont color, shape, size, et alpha; pour geom_line() ces arguments sont color, linetype, size , et alpha. Pour connaitre tous les arguments d’une fonction geom_<xxx>(), consultez son Help (?geom_<xxx>) ou sa page web. Une façon interactive de découvrir ces arguments est disponible via ce lien.

Les arguments esthétiques n’ayant pas été modifiés conservent leurs valeurs par défaut. Par exemple, pour les points ggplot utilise, par défaut, shape = 19 et pour les lignes ggplot utilise linetype = 1. Voici la liste des formes et lignes (standards) disponible dans R; consultez ce lien pour plus d’information sur ce sujet.

Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
ℹ Please use `linewidth` instead.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
generated.

Setting vs mapping

Le contrôle sur les esthétiques peut s’effectuer de deux manières: à l’intérieur ou à l’extérieur d’un aes(). Considérant par exemple le cas où nous voulons changer la forme (shape) des points d’un graphique. Il y a deux situations possibles à considérer:

  • on désire une forme statique (fixe) à appliquer à l’ensemble des points. Dans ce cas il faut placer l’argument shape à l’intérieur du geom_point() mais en dehors de tout fonction aes(...),
  • on désir une forme dynamique qui varie en fonction des valeurs prises par une variable de notre data frame; càd on désire mapper (associer) une variable à shape. Dans ce cas il faut placer shape à l’intérieur d’une fonction aes(). Comme expliqué par la suit, dans un ggplot, il y a plusieurs endroits ou l’on peut placer cette fonction.

On désigne la première situation par le terme setting et la deuxième par le terme mapping. Un mapping est une mise en correspondance entre un argument esthétique (shap, color, size, etc.) et une variable du data frame, alors qu’un setting est le fait d’assigner une valeur (fixe) à l’argument esthétique. Contrairement au setting, un mapping doit toujours se faire à l’intérieur de la fonction aes(). En résumé, les variables doivent toujours figurer à l’intérieur d’un aes() alors que les constantes doivent être placées à l’extérieur.

Voici un exemple qui illustre les deux cas pour l’argument shape.

# gauche : setting
ggplot(mpg, aes(x = displ, y = hwy)) +
  geom_point(shape = 1)         
# droite : mapping
ggplot(mpg, aes(x = displ, y = hwy, shape = drv)) +
  geom_point()                                       

Notez que lors d’un mapping, une légende est automatiquement ajoutée au graphique.

Un mapping ou un setting mal spécifié entraîne typiquement une erreur ou un résultat atypique. Voici trois exemples qui illustrent cela.

p <- ggplot(mpg, aes(x = displ, y = hwy)) + geom_point()
# Exemple (1): mauvais setting (Error) 
p + geom_point(shape = drv)
Error in eval(expr, envir, enclos): object 'drv' not found

Cette erreur est due au fait que ggplot interprète drv comme un objet (chiffre) existant, or cela n’est pas le cas.

# Exemple (2): mauvais mapping (Error)
p + aes(shape = 1)
Error in `geom_point()`:
! Problem while computing aesthetics.
ℹ Error occurred in the 1st layer.
Caused by error in `scale_f()`:
! A continuous variable cannot be mapped to the shape aesthetic
ℹ choose a different aesthetic or use `scale_shape_binned()`

Ici ggplot interprète le chifre 1 comme une variable numérique dont la valeur est fixée à 1, or shape n’accepte pas une telle entrée.

# Exemple (3): mauvais mapping (Résultat atypique)
p + aes(shape = '1')

Ici, shape = '1' n’a aucun impact sur la forme des points (la forme par défaut est utilisée), mais on constate l’apparition d’une légende. Cela est dû au fait que ggplot interprète '1' comme un facteur, qui ne contient qu’un seul niveau (“1”). En pratique, on peut tirer avantage de cela pour, par exemple, ajouter facilement une légende; un exemple concret est fourni plus loin.

Autres arguments esthétiques

On peut modifier les autres arguments esthétiques, en suivant le même principe que celui appliqué à shape, Voici quelques exemples.

+ color
p <- ggplot(mpg, aes(x = displ, y = hwy)) + geom_point()

# top
p + geom_point(color = "red")  # gauche : setting color
p + aes(color = drv)           # droite : mapping color with a factor
# bas
p + aes(color = cty)           # gauche : mapping color with a numeric
p + aes(color = cty > 20)      # droite : mapping color with a logical  

Tapez colours() pour voir la liste complète des couleurs standard dans R.

+ size
p + geom_point(size = 3)   # gauche : setting size
p + aes(size = cty)        # droite : mapping size

+ linetype
# gauche : setting linetype
ggplot(mpg, aes(x = displ, y = hwy)) +
  geom_line(linetype = 5)
# droite : mapping linetype
ggplot(mpg, aes(x = displ, y = hwy, linetype = trans)) +
  geom_line()

+ alpha

Un autre argument esthétique souvent utilisé en ggplot est le alpha qui contrôle la transparence d’un remplissage. Il prend une valeur comprise entre 0 (transparence totale) et 1 (opacité totale) et permet de réduire le problème du chevauchement des points. Pour mieux comprendre cet aspect, considérant la variable displ. Cette variable contient beaucoup de valeurs identiques impossibles à voir sur un plot classique, mais plus facile à repérer avec un alpha adéquat.

xtabs(~displ, data = mpg)
displ
1.6 1.8 1.9   2 2.2 2.4 2.5 2.7 2.8   3 3.1 3.3 3.4 3.5 3.6 3.7 3.8 3.9   4 4.2 
  5  14   3  21   6  13  20   8  10   8   6   9   4   5   2   3   8   3  15   4 
4.4 4.6 4.7   5 5.2 5.3 5.4 5.6 5.7 5.9   6 6.1 6.2 6.5   7 
  1  11  17   2   5   6   8   1   8   2   1   1   2   1   1 
# gauche
ggplot(mpg, aes(x = drv, y = displ)) +
  geom_point()
# droite
ggplot(mpg, aes(x = drv, y = displ)) +
  geom_point(alpha = 0.1, size = 3)

Multiple geoms, multiple aes()

Notez qu’on peut mixer plusieurs geoms et/ou plusieurs arguments esthétiques pour construire des graphiques de plus en plus riches. Mais attention, “trop d’information tue l’information”.

ggplot(mpg, aes(x = displ, y = hwy, color = drv, linetype = trans)) +
  geom_point(size = 1.5) +
  geom_line(alpha = 0.5)

Mapping global vs mapping local

Jusqu’à pressent nous avons systématiquement réalisé nos mappings en plaçant la fonction aes() à l’intérieur de ggplot() (pour rappelle les écritures ggplot(df, aes(vars)) et ggplot(df) + aes(vars) sont équivalentes). Mais ceci n’est pas la seule façon disponible. En effet, toutes les fonctions geom_<xxx>() possèdent l’argument optionnel mapping. Si cet argument est omis, sa valeur est héritée de celle de la fonction ggplot(). Si à l’inverse il est renseigné, alors sa valeur vient remplacer celle héritée. Autrement dit, un mapping déclaré dans ggplot() est globale et s’applique à toutes les couches du graphique alors qu’un mapping déclaré localement, à l’intérieur d’un geom_<xxx>(), ne s’applique qu’à ce dernier.

Pour comprendre cet aspect, comparez le dernier graphique ci-dessus avec le graphique suivant.

ggplot(mpg, aes(x = displ, y = hwy)) +
  geom_point(aes(color = drv), size = 1.5) +
  geom_line(aes(linetype = trans), alpha = 0.5)

De même, toutes les fonctions geom_<xxx>() possèdent l’argument optionnel data. Lorsque celui-ci est spécifié dans ggplot() alors il s’applique, par défaut, à tous les geoms, mais il est possible de spécifier des données différentes pour chaque geom. Voici un exemple.

# gauche
ggplot() +
  geom_point(data = mpg, mapping = aes(x = displ, y = hwy))
# droite
ggplot(mpg, aes(x = displ, y = hwy)) +
  geom_point(alpha = 0.1, color = "blue") +
  geom_point(data = subset(mpg, displ == 4), mapping = aes(x = displ + 0.1, y = hwy + 15), color = "red")

9.3 Fonctions géométriques

En plus de geom_point() et geom_line(), que nous avons déjà rencontrées, nous allons rapidement parcourir dans cette section les principales fonctions geom_<xxx>(). Pour la liste complète des geoms voir ce lein

+ geom_jitter()

C’est la même chose que geom_point() sauf qu’elle permet d’écarter les points, verticalement et horizontalement, en y ajoutant un bruit aléatoire, permettant ainsi de réduire le problème du chevauchement des points.

p <- ggplot(mpg, aes(x = displ, y = "")) + labs(y = NULL)

# gauche : bruit horizontal et vertical
p +  geom_jitter()
# droite : bruit vertical uniquement
p + geom_jitter(width = 0, height = 0.3)

+ geom_boxplot()
# gauche
ggplot(mpg, aes(x = "", y = hwy))  + geom_boxplot() + labs(x = NULL)
# droite
ggplot(mpg, aes(x = drv, y = hwy)) + geom_boxplot()

geom_boxplot() accepte d’autres arguments, comme, par exemple,varwidth = TRUE qui permet de varier la largeur des boîtes en fonction des effectifs de chaque classe et outlier.shape = NA qui supprime l’affichage des outliers; voir le Help.

ggplot(mpg, aes(x = drv, y = hwy)) +
  geom_boxplot(varwidth = TRUE, outlier.shape = NA) +
  geom_jitter(alpha = 0.2, size = 2, width = 0.1, height = 0) +
  stat_summary(fun = mean, shape = 13, size = 1, colour = "red") # notez l'utilisation de stat_summary()

+ geom_smooth()

Cette fonction permet de tracer une courbe de tendance à partir d’un nuage de points \((x,y)\). Par défaut, cette courbe est accompagnée par un intervalle de confiance à \(95\%\), pour le retirer, il faut ajouter se = FALSE. L’argument method contrôle la méthode utilisée pour tracer la courbe. Par défaut, cet argument est fixé à “loess”, ce qui signifie que la fonction loess() est appelée pour produire une courbe lissée non-paramétriquement. Parmi les autres méthodes qu’il est possible d’utiliser, on retrouve lm, pour un modèle linéaire, et glm, pour un modèle linéaire généralisé.

p <- ggplot(mpg, aes(x = displ, y = hwy)) + geom_point()

p + geom_smooth()                                             # gauche
p + aes(color = drv) + geom_smooth(method = "lm", se = FALSE) # droite

+ geom_histogram()
ggplot(mpg, aes(x = hwy)) +
  geom_histogram()

L’un des problèmes avec l’histogramme est le choix du nombre de classes (bins, en anglais). Par défaut, geom_histogram() utilise 30 bins - ce qui est rarement une bonne valeur !
De nombreuses règles existent pour choisir une valeur plus ou moins optimale pour ce paramètre, l’une des formules les plus utilisées est celle de Sturge que l’on peut calculer à l’aide de la fonction (de base) nclass.Sturges().

nclass.Sturges(mpg$hwy)
[1] 9
# gauche
ggplot(mpg, aes(x = hwy)) +
  geom_histogram(bins  = 9, color = "black", fill = "lightgray") +
  labs(title = "Histogramme des fréquences")
# droite
ggplot(mpg, aes(x = hwy)) +
  geom_histogram(aes(y = ..density..), bins  = 9, color = "black", fill = "lightgray") +
  labs(title = "Histogramme des densités")
Warning: The dot-dot notation (`..density..`) was deprecated in ggplot2 3.4.0.
ℹ Please use `after_stat(density)` instead.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
generated.

Ci-dessus, ..density.. fait référence à la variable density créée et stockée automatiquement, en interne, par ggplot. En générale, pour accéder à une variable interne à ggplot, il faut entourer son nom par deux points de chaque côté.

+ geom_density()
ggplot(mpg, aes(x = hwy)) + geom_density()

Un des arguments les plus importants de geom_density() est bw (bandwidth) qui permet de contrôler le lissage de la courbe. Par défaut, ggplot utilise la méthode de Scott tel que fournit par la fonction (de base) bw.nrd(); voir le help de geom_density() pour plus d’information.

Le graphique suivant illustre l’impact du bandwidth sur la courbe de densité estimée. Il illustre aussi l’utilité de faire un mapping à une constante pour créer facilement une légende.

ggplot(mpg, aes(x = hwy)) +
  geom_density(aes(color = "'nrd'", linetype = "'nrd'")) +
  geom_density(bw = 0.5, aes(color = "0.5", linetype = "0.5")) +
  geom_density(bw = 5, aes(color = "5", linetype = "5")) +
  labs(color = "Bandwidth", linetype = "Bandwidth")

On peut aussi superposer un histogramme et une densité ou plusieurs densités dans un seul et même graphe.

# gauche
ggplot(mpg, aes(x = hwy)) +
  geom_histogram(aes(y = ..density..), bins = 9, color = "black", fill = "lightgray") +
  geom_density(size = 1)
# droite
ggplot(mpg, aes(x = hwy, color = drv, fill = drv)) +
  geom_density(size = 1, alpha = 0.05)

+ geom_violin()

Un graphique en violon (violin plot, en anglais) est constitué de deux graphiques de densité identique retournés et affichés en miroir l’un en face de l’autre.

# gauche
ggplot(mpg, aes(x = drv, y = hwy)) + geom_violin(trim = FALSE)
# droite
ggplot(mpg, aes(x = drv, y = hwy)) +
  geom_violin(color = NA, fill = "lightgray", alpha = 0.5, trim = FALSE) +
  geom_boxplot(width = 0.2, varwidth = TRUE, outlier.shape = NA, fill = NA) +
  geom_jitter(width = 0.1, height = 0, alpha = 0.2)

+ geom_bar()
# gauche
ggplot(mpg, aes(x = drv)) + geom_bar() + labs(title = "Barplot des fréquences")
# droite
ggplot(mpg, aes(x = drv)) + geom_bar(aes(y = ..count.. / sum(..count..))) + labs(title = "Barplot des proportions", y = "prop")

Le graphique en haut à droite représente les proportions de drv, càd. les chiffres donnés dans le tableau suivant.

xtabs(~drv, data = mpg) |> proportions()
drv
    4     f     r 
0.440 0.453 0.107 

Une alternative à geom_bar() est la fonction geom_col() qui est plus facile à utiliser lorsqu’on dispose déjà des statistiques que l’on désire représenter. Ainsi, on peut reproduire la figure ci-dessus avec le code suivant.

xtabs(~drv, data = mpg) |> data.frame() |>
  ggplot() + geom_col(aes(x = drv, y = Freq)) + labs(y = "count")

xtabs(~drv, data = mpg) |> proportions() |> data.frame() |>
  ggplot() + geom_col(aes(x = drv, y = Freq)) + labs(y = "prop")

Il est aussi possible de créer des Barplots en croisant deux facteurs, il suffit pour cela d’utiliser l’argument esthétique fill. Par défaut, ggplot présente des bares empilées. Pour les disposer côte à côte, il faut indiquer position = "dodge"; par défaut, position = "stack". Une autre alternative est position = "fill" pour des barres empilées mais qui occupent l’axe des y entièrement, ce qui simplifier la comparaison.

# gauche
ggplot(mpg, aes(x = drv, fill = cyl)) + geom_bar(position = "dodge")
# droite
ggplot(mpg, aes(x = drv, fill = cyl)) +
  geom_bar(position = "fill") + labs(y = "prop(cyl)")

Le graphique en haut à droite représente les proportions de cyl par catégorie de drv. Ces proportions sont données dans le tableau suivant.

xtabs(~ drv + cyl, data = mpg) |> proportions(margin = "drv")
   cyl
drv       4       5       6       8
  4 0.22330 0.00000 0.31068 0.46602
  f 0.54717 0.03774 0.40566 0.00943
  r 0.00000 0.00000 0.16000 0.84000
+ stat_function()

stat_function() peut être utiler pour tracer les courbes de fonctions.

# gauche
ggplot() + xlim(-2, 2) + stat_function(fun = \(x) x^2)
# droite
ggplot() + xlim(-5, 5) + stat_function(fun = dnorm, args = list(mean = 0, sd = 2))

Par défaut, stat_function() trace une courbe, mais il est possible de préciser d’autres geoms. Voici deux exemples.

# gauche
ggplot() + xlim(-5, 5) +
  stat_function(fun = dnorm, geom = "line", size = 1.2, mapping = aes(color = "N(0,1)")) +
  stat_function(fun = dnorm, args = list(mean = 0, sd = 2), geom = "point", size = 1.2, shape = 3, mapping = aes(color = "N(0,4)")) +
  labs(y = "densité", color = "Distribution")
# droite
ggplot() + xlim(-3, 3) +
  stat_function(fun = dnorm) +
  stat_function(fun = dnorm, xlim = c(-1, 1), geom = "area", fill = "gray", alpha = .3)

9.4 Faceting

Le faceting sert à découper un graphique en une figure à plusieurs panneaux, chaque panneau (facette) correspond à un sous-ensemble de données. ggplot propose deux fonctions pour faire du faceting : facet_grid() et facet_wrap(). Ces deux fonctions sont très similaires mais facet_grid() réalise une matrice de graphiques alors que facet_wrap() réalise une suite de graphiques. Les deux arguments principaux de ces fonctions sont : rows et cols pour facet_grid() et facets pour facet_wrap(). Le graphique suivant illustre ces deux fonctions et leurs arguments.

# gauche
ggplot(mpg, aes(displ, hwy)) + geom_point() +
  facet_grid(cols = vars(trans), row = vars(drv))
# droite
ggplot(mpg, aes(displ, hwy)) + geom_point() +
  facet_wrap(facets = vars(trans, drv))

Par défaut, chaque facette est fournie avec un label qui montre la valeur du facteur de découpage. Par défaut aussi, les échelles des axes affichées sont les mêmes (fixes) pour toutes les groupes/facettes. Les arguments labeller et scales peuvent être évoqués pour modifier/contrôler ces deux éléments.

ggplot(mpg, aes(displ, hwy)) + geom_point() +
  facet_grid(cols = vars(trans), row = vars(drv), labeller = "label_both", scales = "free")

9.5 Programmer des fonctions avec ggplot

Si vous vous retrouvez à écrire sans cesse les mêmes lignes de code ggplot pour créer des graphiques, il serait peut-être utile de penser à d’autres pistes.

Considérons le plot suivant.

ggplot(mpg, aes(x = displ, y = hwy)) + geom_point(color = "blue", size = 2) +
  theme_bw() + facet_wrap(vars(trans))

Si, par la suite, vous devez, par exemple, retaper geom_point(color = "blue", size = 2) + theme_bw() à multiples reprises, alors sauvegardez ce bout de code dans un objet et réutilisez-le, comme ceci.

mygpt <- list(geom_point(color = "blue", size = 2), theme_bw())
ggplot(mpg, aes(x = cty, y = hwy)) + facet_wrap(vars(drv)) + mygpt

Pour aller plus loin, on peut envisager d’écrire une fonction qui pourrait être utilisée pour réaliser le même type de graphique encore et encore. Dès lors, il semble logique de recourir à la syntaxe suivante.

mygptFun <- function(data, x, y, grp) {
  ggplot(data, aes(x, y)) + facet_wrap(vars(grp)) + mygpt
}

Mais cela ne peut pas fonctionner. En effet,

mygptFun(mpg, displ, hwy, trans)
Error in `combine_vars()`:
! At least one layer must contain all faceting variables: `grp`
✖ Plot is missing `grp`
✖ Layer 1 is missing `grp`

La raison est que aes() et vars(), comme beaucoup d’autres fonctions de la collection des packages tidyverse, recourent à l’évaluation dite non-standard. C’est ce qui nous permet de simplifier le code puisqu’on n’a pas besoin de répéter systématiquement le nom du data.frame que l’on exploite, càd on n’a pas besoin d’écrire

ggplot(mpg, aes(x = mpg$displ, y = mpg$hwy)) + facet_wrap(vars(mpg$trans)) + mygpt
# ou
ggplot(mpg, aes(x = mpg[["displ"]], y = mpg[["hwy"]])) + facet_wrap(vars(mpg[["trans"]])) + mygpt

Mais cette facilité génère des difficultés lorsqu’on souhaite créer une fonction: lorsque l’on tape mygptFun(mpg, displ, hwy, trans), R cherche les variables displ, hwy et trans dans le GlobalEnvironment ce qui engendre l’erreur constatée.

Il y a plusieurs façon pour résoudre ce problème. Parmi lesquels citons l’opérateur {{ et le pronom .data. Les deux blocs du code suivant montrent comme utilisés respectivement l’un et l’autre.

mygptFun1 <- function(data, x, y, grp) {
  ggplot(data, aes({{ x }}, {{ y }})) + facet_wrap(vars({{ grp }})) + mygpt
}

mygptFun1(mpg, displ, hwy, trans)
mygptFun2 <- function(data, x, y, grp) {
  ggplot(data, aes(.data[[x]], .data[[y]])) + facet_wrap(vars(.data[[grp]])) +
    mygpt
}

mygptFun2(mpg, "displ", "hwy", "trans")

Finalement, pour gagner en robustesse, envisagez aussi d’utiliser l’argument spécial .... Ainsi, nous pouvons, par exemple, définir notre fonction comme ceci.

mygptFun3 <- function(data, x, y, grp, ...) {
  mygpt <- list(geom_point(...), theme_bw())
  ggplot(data, aes({{ x }}, {{ y }})) + facet_wrap(vars({{ grp }})) + mygpt
}

Ce qui nous permet de générer une large gamme de graphiques avec le moindre d’effort possible. Voici deux exemples.

mygptFun3(mpg, displ, hwy, trans, color = "red", shape = 15)

mygptFun3(mpg, cty, hwy, trans, aes(color = drv))

Pour plus de détails à ce sujet, lisez cet article et/ou celui-ci.

9.6 Extensions ggplot

Il existe un grand nombre d’extensions ggplot. Consultez cette galerie pour voir une liste de ce qui est possible. Dans ce qui suit, nous allons en parcourir quelques-unes.

Créer des ggplots à l’aide esquisse

esquisse est un package R qui permet de créer des graphiques ggplot de façon interactive simple et rapide et fournit aussi la syntaxe à utiliser pour aboutir au même résultat.

Après l’avoir installé, vous pouvez commencer à l’utiliser à l’aide de la commande

esquisse::esquisser(<data>)

, où <data> est le nom du jeu de données à anlayser. Essayez en remplaçant <data> par le data frame mpg. Alternativement, vous pouvez utiliser la commande esquisse::esquisser(), sans spécifier un data frame, et introduire ce dernier par la suite. Dans ce cas, une fenêtre s’ouvrira, en superposition, vous invitant à choisir les données.

La liste des variables de data frame apparaît en haut de la fenêtre de “Esquisse”, et vous pouvez les faire glisser dans les zones “X”, “Y”, “Fill”, “Color”, etc. pour créer des correspondances (mapping) entre ces éléments esthétiques et les variables choisies. Le graphique se met automatiquement à jour (cliquez sur l’image ci-dessus pour plus de détails) :

Image name

Par défaut, esquisse sélectionne le type de graphique le plus approprié selon la nature de vos variables. Mais vous pouvez choisir un autre type de graphique à l’aide de l’icône en haut à gauche (Auto).

Les menus en bas de l’interface vous permettent de personnaliser les titres, les thèmes, les couleurs et d’autres éléments affectant l’aspect global du graphique. Quand vous avez fini, si vous le souhaitiez, vous pouvez conserver le code R (généré automatiquement) en utilisant la rubrique “Code” en bas de la fenêtre de “Esquisse”. Pour plus d’information, consultez cette page et celle-ci.

Aménagement des graphes: package patchwork

Il est très facile de combiner plusieurs ggplots dans le même graphique à l’aide du package patchwork. Commencer par installer et charger ce package. Créez quelques ggplots et assignez-les à des objets (comme ci-dessous).

p1 <- ggplot(mpg, aes(x = displ, y = hwy, col = drv)) + geom_point()
p2 <- ggplot(mpg, aes(x = hwy)) + geom_histogram(bins  = 9)
p3 <- ggplot(mpg, aes(x = drv, y = hwy, col = drv)) + geom_boxplot()

patchwork fournit, entres autres, les opérateurs + et / pour les agencements horizontaux et verticaux, respectivement, qui peuvent être combinés pour obtenir le résultat souhaité. Voici quelques exemples.

# gauche
p2 + p3
# droite
p2 / p3

p1 + (p2 / p3) + plot_layout(guides = "collect")

(p2  + (p3 + guides(colour = "none"))) / p1 + plot_layout(guides = "collect")

Pour plus de détails consultez cette page.

Graphiques interactifs: packege plotly

Avec la fonction ggplotly() du package plotly on peut transformer la majorité des ggplots en des graphiques interactifs. Voici quelques exemples.

ggplotly(p1)
ggplotly(p2)
ggplotly(p3)

Il aussi possible de créer des animations dans ggplot2 avec Plotly. Voici un exemple

p <- ggplot(mpg, aes(x = displ, y = hwy)) + geom_point()
ggplotly(p + aes(frame = manufacturer)) |> animation_opts(frame = 1500, transition = 500)


Pour plus de détails consultez cette page.