tp ggplot2 - rochevin/formation_ggplot2_2023 GitHub Wiki
library(tidyverse)
library(palmerpenguins) # des MANCHOTS, pas des pingouins !
MonPremierGraphique <- ggplot(data=penguins)
MonPremierGraphique
Je ne vois rien ! Pourtant l’objet MonPremierGraphique
existe et il
contient plein de choses.
summary(MonPremierGraphique)
data: species, island, bill_length_mm, bill_depth_mm,
flipper_length_mm, body_mass_g, sex, year [344x8]
faceting: <ggproto object: Class FacetNull, Facet, gg>
compute_layout: function
draw_back: function
draw_front: function
draw_labels: function
draw_panels: function
finish_data: function
init_scales: function
map_data: function
params: list
setup_data: function
setup_params: function
shrink: TRUE
train_scales: function
vars: function
super: <ggproto object: Class FacetNull, Facet, gg>
typeof(MonPremierGraphique)
[1] "list"
names(MonPremierGraphique)
[1] "data" "layers" "scales" "mapping" "theme"
[6] "coordinates" "facet" "plot_env" "labels"
class(MonPremierGraphique)
[1] "gg" "ggplot"
head(MonPremierGraphique$data)
# A tibble: 6 × 8
species island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
<fct> <fct> <dbl> <dbl> <int> <int>
1 Adelie Torgersen 39.1 18.7 181 3750
2 Adelie Torgersen 39.5 17.4 186 3800
3 Adelie Torgersen 40.3 18 195 3250
4 Adelie Torgersen NA NA NA NA
5 Adelie Torgersen 36.7 19.3 193 3450
6 Adelie Torgersen 39.3 20.6 190 3650
# ℹ 2 more variables: sex <fct>, year <int>
MonPremierGraphique$layers
list()
HW a dit : The plot is not ready to be displayed until one layer is added.
OK, donc c’est normal si je ne vois rien, car le composant layers
de
l’objet MonPremierGraphique
ne contient rien.
MonPremierGraphique <- ggplot(data = penguins,
aes(x = bill_length_mm,
y = bill_depth_mm))
MonPremierGraphique
C’est mieux, je vois des axes avec les variables que je veux représenter en x et y, mais rien de plus.
HW a dit : A minimal layer may do nothing more than specify a
geom
, a way of visually representing the data.
Alors, je vais ajouter un layer
. Pour cela, plein de fonctions
existent pour dire quels objets géométriques je veux utiliser pour la
représentation. Soyons modestes, commençons par des points avec la
fonction geom_point()
.
MonPremierGraphique + geom_point()
Youpi, trompettes et confettis ! Voilà, mon premier graphique
réalisé avec ggplot2
.
Sauvons-le et auscultons-le.
MonPremierGraphiqueLeVrai <- MonPremierGraphique + geom_point()
summary(MonPremierGraphiqueLeVrai)
data: species, island, bill_length_mm, bill_depth_mm,
flipper_length_mm, body_mass_g, sex, year [344x8]
mapping: x = ~bill_length_mm, y = ~bill_depth_mm
faceting: <ggproto object: Class FacetNull, Facet, gg>
compute_layout: function
draw_back: function
draw_front: function
draw_labels: function
draw_panels: function
finish_data: function
init_scales: function
map_data: function
params: list
setup_data: function
setup_params: function
shrink: TRUE
train_scales: function
vars: function
super: <ggproto object: Class FacetNull, Facet, gg>
-----------------------------------
geom_point: na.rm = FALSE
stat_identity: na.rm = FALSE
position_identity
MonPremierGraphiqueLeVrai$layers
[[1]]
geom_point: na.rm = FALSE
stat_identity: na.rm = FALSE
position_identity
Il a bien un layer
et c’est pourquoi je peux voir quelque chose.
Reprenons l’objet MonPremierGraphique
MonPremierGraphique
On sait qu’il n’affiche rien car il n’a pas de layer
. On a ajouté un
layer
avec la fonction geom_point()
.
MonPremierGraphique + geom_point()
Mais on aurait aussi bien pu le faire avec la fonction layer()
,
fonction dans laquelle on spécifie tous les attributs d’un layer
:
mapping
, data
(ici c’est inutile car on les a déjà spécifiés dans
l’objet MonPremierGraphique
, geom
, stat
et position
.
MonPremierGraphique +
layer(
mapping = NULL,
data = NULL,
geom = "point",
stat = "identity",
position = "identity"
)
En fait, la fonction geom_point()
nous simplifie la vie en appelant
elle-même la fonction layer()
avec les paramètres qu’il faut.
A noter qu’il existe une façon de faire la même chose en cachant encore
plus les choses : la fonction qplot()
.
qplot(bill_length_mm, bill_depth_mm, data = penguins)
Mais, dans la préface à la 2eme édition de ggplot:
Elegant Graphics
for Data Analysis
HW a dit : Switched from
qplot()
toggplot()
in the introduction, Chapter 2. Feedback indicated thatqplot()
was a crutch: it makes simple plots a little easier, but it doesn’t help with mastering the grammar.
Et effectivement, en utilisant qplot()
, on garde à peu près nos
habitudes liées à plot()
mais la layered grammar est complètement
cachée.
Pour faire un graphique :
-
les données sont stockées dans un
data.frame
et pas autrement, ce n’est pas négociable ! -
les
aesthetics mappings
lient des variables du jeu de données à des propriétés visuelles (utiliser la fonctionaes()
). -
seul l’ajout d’un
layer
permet de visualiser quelque chose (utiliser les fonctionsgeom_*()
). -
Ce qui donne une syntaxe comme celle-ci :
ggplot(data = penguins, aes(x = bill_length_mm, y = bill_depth_mm)) + geom_point()
On est bon ? Oui ? alors, on continue…
J’aimerais bien que les points soient colorés en fonction de la variable
species
du jeu de données penguins
.
Il suffit pour cela de préciser un lien entre cette variable du jeu de
données et la propriété visuelle colour
au moyen de la fonction
aes()
.
MonPremierGraphiqueLeVraiEnCouleur <-
MonPremierGraphiqueLeVrai + aes(colour = species)
MonPremierGraphiqueLeVraiEnCouleur
En fait, les couleurs c’est bien, mais si mes lecteurs les distinguent
mal, je préfère changer la forme (shape
) plutôt que la couleur.
MonPremierGraphiqueLeVraiEnFormes <-
MonPremierGraphiqueLeVrai + aes(shape = species)
MonPremierGraphiqueLeVraiEnFormes
Mouais, pas très lisible. Et si je combinais les deux…
On peut utiliser la même variable pour donner à la fois la couleur et la forme. Dans ce cas, une seule légende est affichée.
MonPremierGraphiqueLeVraiEnCouleurEtFormes <- ggplot(data = penguins,
aes(x = bill_length_mm, y = bill_depth_mm,
colour = species, shape = species)) +
geom_point()
MonPremierGraphiqueLeVraiEnCouleurEtFormes
Les noms des graphiques deviennnent un peu long, je vais me calmer un peu et reprendre depuis le début pour voir si j’ai compris. Je vais pousser un peu le zèle jusqu’à utiliser 2 variables qualitatives (1 pour la couleur, l’autre pour la forme), mais je suis confiant.
PointCouleurForme <- ggplot(data = penguins,
aes(x = bill_length_mm, y = flipper_length_mm,
colour = species,
shape = island)) + geom_point()
PointCouleurForme
Et bien voilà ! C’est pas mal tout ça.
Juste pour voir, si j’inverse les 2 variables qualitatives.
PointCouleurForme <- ggplot(data = penguins,
aes(x = bill_length_mm, y = flipper_length_mm,
colour = island,
shape = species)) + geom_point()
PointCouleurForme
L’impression est différente. A méditer…
ggplot(data = penguins ,aes(x = flipper_length_mm, y = bill_length_mm,
colour = bill_depth_mm,
size = body_mass_g)) + geom_point()
Ouh là, là… c’est joli (ou pas), mais qu’ai-je donc fait ? Reprenons calmement :
-
je suis toujours dans un nuage de points (
geom_point()
) avecflipper_length_mm
en abscisses (aes(x= )
) etbill_length_mm
en ordonnées (aes(y= )
). -
dans la fonction
aes()
, j’ai lié la couleur à la variablebill_depth_mm
obtenant ainsi une dégradé de bleu (plus la valeur debill_depth_mm
est élevée, plus le symbole est clair). -
toujours dans la fonction
aes()
, j’ai lié la taille à la variablebody_mass_g
obtenant ainsi des points d’autant plus grands que la valeur debody_mass_g
est élevée. -
pour le même prix, j’ai obtenu les deux légendes associées.
-
tous ces éléments illustrent la notion de
scaling
qui contrôle le lien entre les données et un attributaesthetic
. Tout attributaesthetic
a besoin d’une échelle. Parfois (souvent), on ne s’en rend pas compte parce qu’une valeur par défaut est prévue.
Ok, je peux m’amuser à personnaliser mon graphique avec des couleurs,
des formes, des tailles différentes. Et si je restais sobre en
présentant un graphique par modalité de la variable species
.
Très facile (comme le reste !), il suffit d’utiliser le facetting
.
Repartons de MonPremierGraphiqueLeVrai
et utilisons, simplement, la
fonction facet_wrap()
.
MonPremierGraphiqueLeVrai + facet_wrap(~species)
Il va de soi que l’on peut combiner tout ça.
MonPremierGraphiqueLeVrai +
aes(colour = bill_depth_mm) +
facet_wrap(~species)
Avec un deuxième facteur, on peut réaliser (très facilement, on s’en
doute) toute une planche de graphiques, cette fois-ci avec
facet_grid()
.
MonPremierGraphiqueLeVrai + facet_grid(species ~ island)
MonPremierGraphiqueLeVrai + facet_grid(island ~ species)
MonPremierGraphiqueLeVrai + facet_grid(species ~ sex)
Pour réaliser un histogramme, il suffit… de le demander. On utilise
toujours le data.frame penguins
avec cette fois-ci seulement une
variable (ici flipper_length_mm
en x
).
Histogramme <- ggplot(penguins, aes(x = flipper_length_mm)) +
geom_histogram()
Histogramme
OK, pas génial, mais on a bien un histogramme. Histogramme sur lequel, on peut appliquer les recettes précédentes.
- Le
facetting
pour avoir un histogramme par modalité du facteurspecies
Histogramme + facet_wrap(~species)
- et aussi des couleurs (même si l’interprétation n’en est pas forcément améliorée)
Histogramme + aes(colour = species)
Histogramme + aes(fill = species)
Au fait, vous avez vu qu’il nous embête avec un warning qui parle de
stat_bin()
.
HW a dit : The choice of bins is very important for histograms […]. In
ggplot2
, a very simple heuristic is used for the default number of bins: it uses 30, regardless of the data. This is perverse, and ignores all of the research on selecting good bin sizes automatically, but sends a clear message to the user that they need to think about and experiment with the bin width.
Et bim !
Alors, on va essayer de penser un peu et diminuer la largeur des
intervalles (binwidth
).
ggplot(penguins, aes(x = bill_length_mm)) +
geom_histogram(binwidth = 0.5)
OK, ça a l’air d’aller mieux. Et, juste manière, avec des couleurs.
ggplot(penguins, aes(x = bill_length_mm, fill = species)) +
geom_histogram(binwidth = 0.5)
Au fait, juste pour voir si on a compris le principe, comparons
ggplot(penguins, aes(x = bill_length_mm)) + geom_histogram()
ggplot(penguins, aes(x = bill_length_mm)) + geom_bar(stat = "bin")
C’est la même chose ! Logique non ? Un histogramme n’est rien d’autre qu’un diagramme en bâtons prenant comme données le nombre de valeurs dans chaque bin.
On peut ainsi s’amuser avec d’autres représentations graphiques de ces counts.
ggplot(penguins, aes(x = bill_length_mm)) + geom_point(stat = "bin")
ggplot(penguins, aes(x = bill_length_mm)) + geom_line(stat = "bin")
Puisque que je viens de voir passer geom_bar
, je vais m’attarder un
peu dessus pour représenter 2 variables qualitatives.
table(penguins$island, penguins$species)
Adelie Chinstrap Gentoo
Biscoe 44 0 124
Dream 56 68 0
Torgersen 52 0 0
ggplot(penguins, aes(x=species, fill=island)) + geom_bar()
ggplot(penguins, aes(x=species, fill=island)) + geom_bar(position = "dodge")
ggplot(penguins, aes(x=species, fill=island)) + geom_bar(position = "fill")
ggplot(penguins, aes(x=species, fill=sex)) + geom_bar(position = "dodge")
Je poursuis autour de la distribution des données avec la représentation
de densité grâce à la fonction… devinez… geom_density()
.
Pour démarrer, c’est facile, maintenant on gère ça tranquillement.
ggplot(penguins, aes(x = bill_length_mm)) + geom_density()
Je vais le faire aussi en couleur par modalité de species
, maintenant
que je suis à l’aise.
ggplot(penguins, aes(x = bill_length_mm, colour = species)) +
geom_density()
Pour avoir des lignes dans la légende on doit passer par
stat_density()
:
ggplot(penguins, aes(x = bill_length_mm, colour = species)) +
stat_density(geom="line", position = "identity")
Au fait, on a vu fill
tout à l’heure, regardons ce que ça donne ici
ggplot(penguins, aes(x = bill_length_mm, fill = species)) +
geom_density()
Superbe ! Euh oui, sauf que je m’attendais à mieux en fait, mais je ne
sais pas trop quoi ? Que les courbes ne se cachent les unes derrière les
autres par exemple ? Oui c’est exactement ça ! Et alors, on fait comment
? On ajoute de la transparence avec le paramètre alpha
.
ggplot(penguins, aes(x = bill_length_mm, fill = species)) +
geom_density(alpha = 0.4)
Ah ouais, là chapeau. Je prends.
Poursuivons avec des boxplots. D’abord un seul
MonGraphe <- ggplot(penguins)
MonGraphe + geom_boxplot(aes(y = bill_length_mm))
puis par modalité de species
encore une fois. Pour cela, il suffit de
préciser que l’axe des x du graphique est lié à la variable species
(oui, elle est qualitative, et alors ?).
MonGraphe + geom_boxplot(aes(x = species, y = bill_length_mm))
Et si je prefère des boxplots à l’horizontale, il suffit d’échanger les coordonnées
MonGraphe + geom_boxplot(aes(y = species, x = bill_length_mm))
ou d’utiliser la fonction coord_flip()
.
MonGraphe + geom_boxplot(aes(x = species, y = bill_length_mm)) +
coord_flip()
Ce qui est bien avec les boxplots c’est quand on peut aussi voir les
points. Simple, il suffit d’ajouter un layer
au graphique des
boxplots.
MonGraphe +
geom_boxplot(aes(x = species, y = bill_length_mm)) +
geom_point(aes(x = species, y = bill_length_mm))
Euh oui d’accord, mais c’est pas terrible, on peut pas faire mieux ? Je reformule : comment peut-on faire mieux ?
En évitant la superposition des points par exemple et en jouant aussi sur la tranparence.
MonGraphe +
geom_boxplot(aes(x = species, y = bill_length_mm)) +
geom_point(aes(x = species, y = bill_length_mm),
alpha = 0.2, position = "jitter")
J’ai écrit deux fois la même chose dans ma commande
(aes(x = species, y = bill_length_mm)
), ne pouvais-je pas m’en passer
? Si bien sûr, comme pour les calculs, on factorise. Reprenons depuis le
début.
ggplot(penguins, aes(x = species, y = bill_length_mm)) +
geom_boxplot() +
geom_point(alpha = 0.2, position = "jitter")
Le fait d’utiliser species
en x et bill_length_mm
en y étant commun
aux deux graphiques que je veux réaliser, je positionne ces informations
à un niveau supérieur.
Mais je ne suis pas obligé de tout remonter, il peut y avoir des
aesthetics
propres à un layer
comme ici où on utilise la variable
bill_depth_mm
pour la taille des points (et cela n’a rien à voir avec
les boxplots).
ggplot(penguins, aes(x = species, y = bill_length_mm)) +
geom_boxplot() +
geom_point(aes(size = bill_depth_mm),
alpha = 0.2, position = "jitter")
Sinon, il y aussi la fonction geom_jitter()
qui fait le boulot aussi
bien (mieux ?) et on peut même resserrer un peu les points.
ggplot(penguins, aes(x = species, y = bill_length_mm)) +
geom_boxplot() +
geom_jitter(alpha = 0.2, width = 0.1)
Et si je remplaçais les boîtes par des violons ! Il suffit de remplacer
geom_boxplot
par… geom_violin
.
ggplot(penguins, aes(x = species, y = bill_length_mm)) +
geom_violin() +
geom_point(aes(size = bill_length_mm),
alpha = 0.2, position = "jitter")
Pour les mêmes en couleur, avec un peu de transparence sur les violons :
ggplot(penguins, aes(x = species, y = bill_length_mm)) +
geom_violin(aes(fill = species), alpha = 0.2) +
geom_jitter(aes(size = bill_length_mm, colour = species),
width = 0.2, alpha = 0.6)
C’est presque trop facile.
Au cas où vous ne seriez pas encore convaincus par ggplot2
, on va
revenir un moment sur les nuages de points. Et là, normalement, c’est
l’effet waouh garanti.
On a vu quelque part qu’un layer
a 5 parties : 1/ les données 2/ les
aesthetics
, 3/ les geoms
, 4/ les transformations statistiques et 5/
les ajustements de position. Jusqu’à présent, on a vu un peu de tout ça,
sauf des statistiques, non ? Alors, allons-y !
MonNuage <- ggplot(penguins, aes(x = flipper_length_mm, y = bill_length_mm)) +
geom_point()
MonNuage
OK, jusqu’ici, on connaît, pas de quoi s’enflammer. Attention, le spectacle va commencer.
MonNuage + geom_smooth()
Euh oui d’accord, c’est bien mais il sort d’où ce truc ? Et bien, il
nous l’a dit geom_smooth() using method = 'loess' and formula 'y ~ x'
.
Un peu trop lisse ?
MonNuage + geom_smooth(span = 0.2)
Pas assez ?
MonNuage + geom_smooth(span = 1.2)
OK, si vous préférez une régression linéaire plutôt qu’un loess, il suffit de demander.
MonNuage + geom_smooth(method = "lm")
Et d’autres, voir ?geom_smooth
.
Déclinons là-dessus ce que l’on sait faire.
- En facettes :
MonNuage + geom_smooth() + facet_wrap(~species)
MonNuage + geom_smooth() + facet_grid(species~island)
MonNuage + geom_smooth() + facet_wrap(species~island)
On note que l’échelle en x et en y est la même pour tous les sous-graphiques. On peut “libérer” l’une et/ou l’autre de ces échelles
MonNuage + geom_smooth() + facet_wrap(~species, scale = "free")
MonNuage + geom_smooth() + facet_wrap(~species, scale = "free_x")
MonNuage + geom_smooth() + facet_wrap(~species, scale = "free_y")
- En couleurs
MonNuage + aes(colour = species) + geom_smooth()
Ca commence à être sympa non ? Et noter l’aspect synthétique et concis du code.
Remarque: On peut faire de l’extrapolation (seulement avec la méthode
lm
):
MonNuage + aes(colour = species) + geom_smooth(method = "lm")
MonNuage + aes(colour = species) + geom_smooth(method = "lm", fullrange = TRUE)
On peut apprécier ou pas les diagrammes circulaires ou camembert ou pie
chart, mais ce serait bien de savoir en faire avec ggplot2
. Cherchez
bien dans l’aide, vous ne trouverez pas de fonction geom_pie()
.
Alors que R de base le fait très simplement.
pie(table(penguins$species))
Et là, on ne serait pas un peu déçu de ne pas avoir de légende
automatiquement ? C’est qu’on y prend goût à ggplot2
.
Au fait, si ces graphiques ne sont pas trop appréciés, c’est parce qu’on leur reproche le fait de devoir interpréter des angles ce que l’oeil humain ne parvient pas forcément très bien à faire. Et c’est pourquoi, on peut lui préférer un simple diagramme en bâtons, que R de base fait aussi très bien.
barplot(table(penguins$species))
Ouais, c’est quand même un peu tristounet. Mais les diagrammes en
bâtons, je sais faire avec ggplot2
ggplot(penguins, aes(x = species)) + geom_bar()
Et là, révélation ! Un diagramme circulaire, c’est un diagramme en bâtons ! euh, non… Mais si, mais dans un repère de coordonnées polaires ! Ah, oui, euh, peut-être… [Sceptique].
HW a dit : In the grammar, a pie chart is a stacked bar geom drawn in a polar coordinate system.
ggplot(penguins, aes(x = species)) + geom_bar() + coord_polar()
Ah oui ! Bizarre non ? OK, on arrange un peu tout ça.
ggplot(penguins, aes(x = species, fill = species)) +
geom_bar(width = 1) +
coord_polar()
Tada !
En fait, c’était juste pour parler de systèmes de coordonnées…
Choisis ton thème ! Voir help(theme_bw)
.
MonNuageTest <- MonNuage + aes(colour = species) + geom_smooth()
MonNuageTest + theme_bw()
MonNuageTest + theme_dark()
MonNuageTest + theme_light()
MonNuageTest + theme_gray()
Pour jouer avec les échelles, il y a de la matière ! Il suffit de
regarder le nombre de fonctions scale_*()
. Et comme on l’a vu plus
tôt, chaque aesthetic
a une échelle.
Reprenons le graphique MonNuage
. Et (re-)constatons qu’il suffit de
saisir son nom pour qu’il apparaisse.
MonNuage
Allons bricoler l’axe des abscisses. En parcourant la liste des
fonctions scale_*()
, on peut deviner (non ?) que la fonction à
manipuler est scale_x_continuous()
et en consultant sa fiche d’aide,
on peut aisément voir comment modifier le titre (name
), le découpage
(breaks
), les limites (limits
) et d’autres choses…
MonNuage + scale_x_continuous(name = "Longueur de nageoire (mm)",
breaks = seq(170, 230, by = 10),
limits = c(170, 230))
Question : comment modifier l’axe des ordonnées ? Il serait quasiment insultant de fournir ici une réponse à cette question.
Important !!
Pour zoomer, toujours utiliser
coord_cartesian
, et surtout pasxlim
,ylim
, ouscale_*
p_boxplot <- ggplot(penguins, aes(x = species, y = bill_length_mm)) +
geom_boxplot() +
geom_point(aes(size = bill_depth_mm),
alpha = 0.2, position = "jitter")
p_boxplot_1 <- p_boxplot + ylim(c(40,50))
p_boxplot_2 <- p_boxplot +
coord_cartesian(ylim=c(40,50))
MonNuage + aes(x = flipper_length_mm^2, y = bill_length_mm^3) +
scale_x_log10() + scale_y_log10()
On va ajouter un aesthetics
à notre graphique pour continuer à
explorer les échelles.
MonNuageCouleur <- MonNuage + aes(colour = species)
MonNuageCouleur
Comme dit et re-dit précédemment, à chaque aesthetics
est associée une
échelle. On a ajouté l’aesthetics colour, donc il y a une échelle
associée.
Pour la modifier et utiliser par exemple des palettes (qualitatives dans
le cas présent) de couleur du package RcolorBrewer
library("RColorBrewer")
display.brewer.all()
utilisons la fonction scale_colour_brewer()
MonNuageCouleur + scale_colour_brewer(palette = "Set2")
MonNuageCouleur + scale_colour_brewer(palette = "Set3")
ou d’autres fonctions
MonNuageCouleur + scale_colour_grey()
MonNuageCouleur + scale_colour_hue(l = 50, c = 30)
Et pour mettre ses couleurs favorites, on peut même le faire à la main.
MonNuageCouleur +
scale_colour_manual(values = c("rosybrown", "tomato", "midnightblue"))
En cas de manque d’inspiration, la fonction colours()
est là.
Et pour une légende en couleur liée à une variable quantitative, comment ça marche ? Pareil !
Modifions l’aesthetic colour
MonNuageCouleurQuanti <- MonNuage + aes(colour = bill_depth_mm)
MonNuageCouleurQuanti
On a, comme on l’a déjà vu, une teinte de bleu qui s’éclaircit quand la
valeur de bill_depth_mm
augmente. Et si je veux changer du bleu ?
On peut encore aller piocher dans les palettes (séquentielles cette
fois-ci) du package RColorBrewer
, mais, là dans ggplot2
, on ne
brasse plus, on distille.
MonNuageCouleurQuanti + scale_colour_distiller(palette = "Greens")
MonNuageCouleurQuanti + scale_colour_distiller(palette = "Purples")
Pas mal, mais je ne vois pas bien les points les plus clairs. Pas de
problème, je vais passer en theme_dark
.
MonNuageCouleurQuanti +
scale_colour_distiller(palette = "Purples") +
theme_dark()
On peut aussi récupérer d’autres palettes de R
MonNuageCouleurQuanti + scale_colour_gradientn(colors = terrain.colors(5))
et faire en fait tout ce que l’on veut ! (mais ça, on le savait déjà).
MonNuageCouleurQuanti + scale_colour_gradient(low = "red", high = "blue")
MonNuageCouleurQuanti +
scale_colour_gradient(low = "red", high = "blue", breaks = seq(12, 20, by = 3))
A un aesthetic
correspond une échelle, donc si mon aesthetic
est une
forme, alors une échelle lui est associée (pas de discrimination !) et
on peut donc la modifier avec les fonctions scale_shape_*()
.
MonNuageForme <- MonNuage + aes(shape = species)
MonNuageForme + scale_shape(solid = FALSE)
MonNuageForme + scale_shape_manual(values = c(4, 8, 15))
MonNuageForme + scale_shape_manual(values = c("*", "@", "x"))
Et si je veux modifier quelques bricoles dans ma légende, aucun problème, je peux aussi faire ce genre de choses :
MonNuageForme + scale_shape(name = "Espèces",
labels = c("Ad.","Ch.","Ge."))
Pour aller plus loin dans les modifications d’apparence de la légende
(et d’autres choses, en fait de tout ce que l’on voit), il faut aller
bricoler le thème… Ca devient très touchy et pas forcément d’une grande
utilité, mais voyons quand même 2 ou 3 trucs. Un petit coup d’oeil à
l’aide de la fonction theme()
peut suffire à se convaincre de ne pas
aller plus loin.
MonNuageCouleur + ggtitle("Mon joli graphique") +
theme(legend.position = "bottom",
legend.title = element_text(face = "bold"),
axis.title.x = element_text(face = "italic"),
plot.title = element_text(colour = "purple",
size = rel(1.5),
hjust = 0.5)) # centrage du titre
Deux solutions possibles :
-
Je sauve l’objet dans mon environnement de travail : fonction
save()
, en fait comme n’importe quel objetR
. En fait, ça, on l’a déjà pratiqué. -
Je sauvegarde la figure dans un fichier graphique : fonction
ggsave()
. Consulter l’aide deggsave()
pour en savoir plus, mais ne vous attendez pas à de grandes surprises. Seule précaution à signaler : il faut réaliser le graphique, puis le sauvegarder (pas comme avec les fonctionspdf()
,jpeg()
,png()
qu’il faut appeler avant de réaliser le graphique et qu’il faut ensuite fermer avecdev.off()
).
ggsave("perfect!.png")
- {patchwork}: Combine/Assemble ggplots.
- {ggforce}: Provide missing
functionality to
ggplot2
. - {ggtext}: Improved text rendering
support for
ggplot2
.
- {plotly}: makes interactive,
publication-quality graphs (with
ggplot2
or not). - {ggedit}: Interactively edit ggplot layers, scales and themes aesthetics.
- {esquisse}: Interactively explore your data by visualizing it with the ggplot2 package.
- Themes: {ggthemr} and {ggthemes}.
- Colors: {MetBrewer}.
- Add statistics on
ggplot2
: {ggpubr} and {ggstatsplot}.
library(patchwork)
Le package patchwork
permet de disposer plusieurs graphiques de façon
très simple. Démonstration avec des +
et des `/’ !
MonNuage + MonNuageCouleur
MonNuage + MonNuageCouleur + MonNuageForme
MonNuage / MonNuageCouleur
MonNuage / MonNuageCouleur / MonNuageForme
MonNuage + (MonNuageCouleur / MonNuageForme)
MonNuage / (MonNuageCouleur + MonNuageForme)
Avec un peu de |
aussi !
MonNuage + MonNuageCouleur + MonNuageForme + MonNuageTest
MonNuage | MonNuageCouleur | MonNuageForme | MonNuageTest
MonNuage + MonNuageCouleur + MonNuageForme + MonNuageTest + plot_layout(ncol = 3)
MonNuage + MonNuageCouleur + MonNuageForme + MonNuageTest + plot_layout(width = c(2,1))
Et ce qui est bien aussi, c’est de pouvoir étiqueter chaque graphique…
patchwork <- (MonNuage + MonNuageCouleur) / MonNuageForme
patchwork +
plot_layout(guides = "collect") +
plot_annotation(tag_levels = 'A',title="un super graphique") & theme(legend.position = "bottom")
…et de les aérer un peu.
MonNuage + plot_spacer() + MonNuageCouleur + plot_spacer() + MonNuageForme
ou au contraire de les superposer
MonNuageCouleur + inset_element(MonNuage, left = 0.6, bottom = 0.5, right = 1, top = 0.85)
Pour en apprendre plus : https://patchwork.data-imaginist.com/articles/patchwork.html
library(cowplot)
- Pour changer de thème
MonNuageCouleur + theme_cowplot()
MonNuageCouleur + theme_minimal_grid()
MonNuageCouleur + theme_minimal_hgrid()
- Pour disposer plusieurs graphiques (comme
patchwork
)
plot_grid(MonNuageCouleurQuanti, MonNuageCouleur, labels = c('1', '2'))
plot_grid(MonNuageCouleurQuanti, MonNuageCouleur, labels = "AUTO")
plot_grid(MonNuageCouleurQuanti, MonNuageCouleur, labels = "auto")
Jusqu’ici tout va bien, mais là, ça coince un peu non ?
MonNuageCouleurAxe <- MonNuageCouleur + theme(axis.text.x = element_text(size = rel(3), angle = 90),
axis.text.y = element_text(size = rel(3)))
plot_grid(MonNuageCouleurAxe, MonNuageCouleur)
Non ? pas de souci ? et là, n’est-ce pas mieux ?
plot_grid(MonNuageCouleurAxe, MonNuageCouleur, align = "h")
Et oui, c’est quand même mieux d’aligner les 2 graphiques !
Et dans l’autre sens :
plot_grid(MonNuageCouleurAxe, MonNuageCouleur, ncol = 1)
plot_grid(MonNuageCouleurAxe, MonNuageCouleur, ncol = 1, align = "v")
Pour en savoir plus : https://cran.r-project.org/web/packages/cowplot/vignettes/introduction.html
Une librairie très complète qui ajoute beaucoup de fonctionnalités
manquantes à ggplot2
.
Voir ici : https://ggforce.data-imaginist.com/reference/index.html.
-
facet_zoom()
: Create a zoom facet for a plot. -
facet_matrix()
: Allow to put differents columns into different rows and columns in a grid of panels. -
geom_mark_*
: Graphical annotation of data.
require(ggforce)
ggplot(penguins, aes(bill_length_mm, flipper_length_mm, colour = species)) +
geom_point() +
facet_zoom(xy = species == "Adelie")
ggplot(penguins, aes(bill_length_mm, flipper_length_mm, colour = species)) +
geom_point() +
facet_zoom(xlim = c(40, 50))
p_boxplot <- ggplot(penguins, aes(x = species, y = bill_length_mm)) +
geom_boxplot() +
geom_point(aes(size = bill_depth_mm),
alpha = 0.2, position = "jitter")+ theme(legend.position ="bottom")
p_boxplot + facet_zoom(ylim = c(40,50))
ggplot(penguins %>% tidyr::drop_na(), aes(col = sex, fill = sex)) +
geom_autopoint() +
geom_autodensity() +
facet_matrix(rows = vars(island:body_mass_g), layer.diag = 2)
penguins %>%
ggplot(aes(x=species,y=bill_length_mm,fill=species)) +
geom_boxplot() +
ggforce::facet_col(~island)
iris
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1 5.1 3.5 1.4 0.2 setosa
2 4.9 3.0 1.4 0.2 setosa
3 4.7 3.2 1.3 0.2 setosa
4 4.6 3.1 1.5 0.2 setosa
5 5.0 3.6 1.4 0.2 setosa
6 5.4 3.9 1.7 0.4 setosa
7 4.6 3.4 1.4 0.3 setosa
8 5.0 3.4 1.5 0.2 setosa
9 4.4 2.9 1.4 0.2 setosa
10 4.9 3.1 1.5 0.1 setosa
11 5.4 3.7 1.5 0.2 setosa
12 4.8 3.4 1.6 0.2 setosa
13 4.8 3.0 1.4 0.1 setosa
14 4.3 3.0 1.1 0.1 setosa
15 5.8 4.0 1.2 0.2 setosa
16 5.7 4.4 1.5 0.4 setosa
17 5.4 3.9 1.3 0.4 setosa
18 5.1 3.5 1.4 0.3 setosa
19 5.7 3.8 1.7 0.3 setosa
20 5.1 3.8 1.5 0.3 setosa
21 5.4 3.4 1.7 0.2 setosa
22 5.1 3.7 1.5 0.4 setosa
23 4.6 3.6 1.0 0.2 setosa
24 5.1 3.3 1.7 0.5 setosa
25 4.8 3.4 1.9 0.2 setosa
26 5.0 3.0 1.6 0.2 setosa
27 5.0 3.4 1.6 0.4 setosa
28 5.2 3.5 1.5 0.2 setosa
29 5.2 3.4 1.4 0.2 setosa
30 4.7 3.2 1.6 0.2 setosa
31 4.8 3.1 1.6 0.2 setosa
32 5.4 3.4 1.5 0.4 setosa
33 5.2 4.1 1.5 0.1 setosa
34 5.5 4.2 1.4 0.2 setosa
35 4.9 3.1 1.5 0.2 setosa
36 5.0 3.2 1.2 0.2 setosa
37 5.5 3.5 1.3 0.2 setosa
38 4.9 3.6 1.4 0.1 setosa
39 4.4 3.0 1.3 0.2 setosa
40 5.1 3.4 1.5 0.2 setosa
41 5.0 3.5 1.3 0.3 setosa
42 4.5 2.3 1.3 0.3 setosa
43 4.4 3.2 1.3 0.2 setosa
44 5.0 3.5 1.6 0.6 setosa
45 5.1 3.8 1.9 0.4 setosa
46 4.8 3.0 1.4 0.3 setosa
47 5.1 3.8 1.6 0.2 setosa
48 4.6 3.2 1.4 0.2 setosa
49 5.3 3.7 1.5 0.2 setosa
50 5.0 3.3 1.4 0.2 setosa
51 7.0 3.2 4.7 1.4 versicolor
52 6.4 3.2 4.5 1.5 versicolor
53 6.9 3.1 4.9 1.5 versicolor
54 5.5 2.3 4.0 1.3 versicolor
55 6.5 2.8 4.6 1.5 versicolor
56 5.7 2.8 4.5 1.3 versicolor
57 6.3 3.3 4.7 1.6 versicolor
58 4.9 2.4 3.3 1.0 versicolor
59 6.6 2.9 4.6 1.3 versicolor
60 5.2 2.7 3.9 1.4 versicolor
61 5.0 2.0 3.5 1.0 versicolor
62 5.9 3.0 4.2 1.5 versicolor
63 6.0 2.2 4.0 1.0 versicolor
64 6.1 2.9 4.7 1.4 versicolor
65 5.6 2.9 3.6 1.3 versicolor
66 6.7 3.1 4.4 1.4 versicolor
67 5.6 3.0 4.5 1.5 versicolor
68 5.8 2.7 4.1 1.0 versicolor
69 6.2 2.2 4.5 1.5 versicolor
70 5.6 2.5 3.9 1.1 versicolor
71 5.9 3.2 4.8 1.8 versicolor
72 6.1 2.8 4.0 1.3 versicolor
73 6.3 2.5 4.9 1.5 versicolor
74 6.1 2.8 4.7 1.2 versicolor
75 6.4 2.9 4.3 1.3 versicolor
76 6.6 3.0 4.4 1.4 versicolor
77 6.8 2.8 4.8 1.4 versicolor
78 6.7 3.0 5.0 1.7 versicolor
79 6.0 2.9 4.5 1.5 versicolor
80 5.7 2.6 3.5 1.0 versicolor
81 5.5 2.4 3.8 1.1 versicolor
82 5.5 2.4 3.7 1.0 versicolor
83 5.8 2.7 3.9 1.2 versicolor
84 6.0 2.7 5.1 1.6 versicolor
85 5.4 3.0 4.5 1.5 versicolor
86 6.0 3.4 4.5 1.6 versicolor
87 6.7 3.1 4.7 1.5 versicolor
88 6.3 2.3 4.4 1.3 versicolor
89 5.6 3.0 4.1 1.3 versicolor
90 5.5 2.5 4.0 1.3 versicolor
91 5.5 2.6 4.4 1.2 versicolor
92 6.1 3.0 4.6 1.4 versicolor
93 5.8 2.6 4.0 1.2 versicolor
94 5.0 2.3 3.3 1.0 versicolor
95 5.6 2.7 4.2 1.3 versicolor
96 5.7 3.0 4.2 1.2 versicolor
97 5.7 2.9 4.2 1.3 versicolor
98 6.2 2.9 4.3 1.3 versicolor
99 5.1 2.5 3.0 1.1 versicolor
100 5.7 2.8 4.1 1.3 versicolor
101 6.3 3.3 6.0 2.5 virginica
102 5.8 2.7 5.1 1.9 virginica
103 7.1 3.0 5.9 2.1 virginica
104 6.3 2.9 5.6 1.8 virginica
105 6.5 3.0 5.8 2.2 virginica
106 7.6 3.0 6.6 2.1 virginica
107 4.9 2.5 4.5 1.7 virginica
108 7.3 2.9 6.3 1.8 virginica
109 6.7 2.5 5.8 1.8 virginica
110 7.2 3.6 6.1 2.5 virginica
111 6.5 3.2 5.1 2.0 virginica
112 6.4 2.7 5.3 1.9 virginica
113 6.8 3.0 5.5 2.1 virginica
114 5.7 2.5 5.0 2.0 virginica
115 5.8 2.8 5.1 2.4 virginica
116 6.4 3.2 5.3 2.3 virginica
117 6.5 3.0 5.5 1.8 virginica
118 7.7 3.8 6.7 2.2 virginica
119 7.7 2.6 6.9 2.3 virginica
120 6.0 2.2 5.0 1.5 virginica
121 6.9 3.2 5.7 2.3 virginica
122 5.6 2.8 4.9 2.0 virginica
123 7.7 2.8 6.7 2.0 virginica
124 6.3 2.7 4.9 1.8 virginica
125 6.7 3.3 5.7 2.1 virginica
126 7.2 3.2 6.0 1.8 virginica
127 6.2 2.8 4.8 1.8 virginica
128 6.1 3.0 4.9 1.8 virginica
129 6.4 2.8 5.6 2.1 virginica
130 7.2 3.0 5.8 1.6 virginica
131 7.4 2.8 6.1 1.9 virginica
132 7.9 3.8 6.4 2.0 virginica
133 6.4 2.8 5.6 2.2 virginica
134 6.3 2.8 5.1 1.5 virginica
135 6.1 2.6 5.6 1.4 virginica
136 7.7 3.0 6.1 2.3 virginica
137 6.3 3.4 5.6 2.4 virginica
138 6.4 3.1 5.5 1.8 virginica
139 6.0 3.0 4.8 1.8 virginica
140 6.9 3.1 5.4 2.1 virginica
141 6.7 3.1 5.6 2.4 virginica
142 6.9 3.1 5.1 2.3 virginica
143 5.8 2.7 5.1 1.9 virginica
144 6.8 3.2 5.9 2.3 virginica
145 6.7 3.3 5.7 2.5 virginica
146 6.7 3.0 5.2 2.3 virginica
147 6.3 2.5 5.0 1.9 virginica
148 6.5 3.0 5.2 2.0 virginica
149 6.2 3.4 5.4 2.3 virginica
150 5.9 3.0 5.1 1.8 virginica
iris %>% ggplot() +
geom_boxplot(aes(x = .panel_x, y = .panel_y, fill=Species)) +
facet_matrix(rows = vars(-Species),cols = vars(Species))
ggplot(penguins, aes(x = flipper_length_mm, y = bill_length_mm)) +
geom_mark_hull(aes(fill = species, label = species)) +
geom_point()
ggplot(penguins %>% drop_na(), aes(bill_depth_mm, body_mass_g)) +
geom_point() +
geom_mark_rect(aes(filter = species == "Gentoo",
label = "Gentoo",
description = "Gentoo penguins seem quite different from the other species"),
label.fill = "pink",
label.colour = "red",
con.colour = "red",
con.linetype = 2,
expand = unit(0.01, "mm"),
fill = "pink")
From: https://ihaddadenfodil.com/post/it-s-a-bird-it-s-a-plane-it-s-a-ggforce-function/
Origine du code et des images: Laura Navarro
require(ggtext)
labels <- c(
Adelie = "<img src='imgs/adelie.png'
width='80' /><br>Adelie",
Gentoo = "<img src='imgs/gentoo_1.png'
width='80' /><br>Gentoo",
Chinstrap = "<img src='imgs/chinstrap.png'
width='80' /><br>Chinstrap"
)
pggtext <- penguins %>% drop_na() %>% ggplot(aes(x=species,y = bill_length_mm, col = sex)) +
geom_boxplot() +
scale_color_manual(values = c("#66b3ff", "#8c8c8c"))+
ylab("length (mm)") +
xlab("Species") +
theme_minimal() +
theme(legend.position = "bottom",
legend.text = element_text(size = 11),
legend.title = element_blank(),
panel.grid.minor = element_blank(),
axis.title = element_text(color = "white", size = 10),
plot.title = element_text(size = 20),
plot.subtitle = element_text( size = 12, hjust = 1)
)
pggtext +
scale_x_discrete(
name = NULL,
labels = labels
) +
theme(
axis.text.x = element_markdown()
)
df_penguins <- penguins %>%
select(species, bill_length_mm:flipper_length_mm,sex) %>%
pivot_longer(bill_length_mm:flipper_length_mm) %>%
drop_na(sex) %>%
mutate(
name = name %>%
str_replace_all("_", " ") %>%
str_remove_all(pattern = " mm") %>%
str_to_title()
) %>%
mutate(name = glue::glue("**{name}** (*mm*)")) %>%
mutate(my_color = case_when(
sex == "male" ~ "#66b3ff",
sex == "female" ~ "#8c8c8c"
)) %>%
mutate(sex = glue::glue("<span style = 'color:{my_color};'>{sex}</span>"))
df_penguins
# A tibble: 999 × 5
species sex name value my_color
<fct> <glue> <glue> <dbl> <chr>
1 Adelie <span style = 'color:#66b3ff;'>male</span> **Bill L… 39.1 #66b3ff
2 Adelie <span style = 'color:#66b3ff;'>male</span> **Bill D… 18.7 #66b3ff
3 Adelie <span style = 'color:#66b3ff;'>male</span> **Flippe… 181 #66b3ff
4 Adelie <span style = 'color:#8c8c8c;'>female</span> **Bill L… 39.5 #8c8c8c
5 Adelie <span style = 'color:#8c8c8c;'>female</span> **Bill D… 17.4 #8c8c8c
6 Adelie <span style = 'color:#8c8c8c;'>female</span> **Flippe… 186 #8c8c8c
7 Adelie <span style = 'color:#8c8c8c;'>female</span> **Bill L… 40.3 #8c8c8c
8 Adelie <span style = 'color:#8c8c8c;'>female</span> **Bill D… 18 #8c8c8c
9 Adelie <span style = 'color:#8c8c8c;'>female</span> **Flippe… 195 #8c8c8c
10 Adelie <span style = 'color:#8c8c8c;'>female</span> **Bill L… 36.7 #8c8c8c
# ℹ 989 more rows
df_penguins %>% ggplot(aes(x=name,y=value,col = sex,fill=after_scale(colorspace::lighten(color, .5)))) +
geom_bar(position="dodge",stat = "summary", fun = "mean") +
facet_wrap(~species,labeller = labeller(species = labels)) +
scale_color_manual(values = c("#66b3ff", "#8c8c8c"))+
theme_minimal() +theme(
axis.text.y = element_markdown(),
legend.text = element_markdown(),
strip.text = element_markdown(),
legend.position = "bottom"
) +coord_flip()
library(plotly)
L’utilisation de ggplot2
avec plotly
n’est pas obligatoire, mais
maintenant que l’on connaît, on ne va quand même pas s’en priver.
Alors allons-y !
ggplotly(MonNuageCouleur)
Il n’y a plus qu’à promener sa souris sur le graphique pour voir ce que cela donne.
En explorant un peu l’aide en ligne, on peut gérer ce qui apparaît sous le curseur de la souris quand on le déplace.
ggplotly(MonNuageCouleur, tooltip = c("x","y"))
ggplotly(MonNuageCouleur, tooltip = c("colour"))
Et on peut bien sûr l’utiliser sur des graphiques autres que nuages de points.
MesBoxplots <- ggplot(penguins,aes(x=species, y=body_mass_g,
colour=species)) + geom_boxplot()
ggplotly(MesBoxplots)
ggplotly(Histogramme)
Noter que l’interactivité reste disponible dans un fichier html.
The ggpubr
package provides some easy-to-use functions for creating
and customizing ggplot2
- based publication ready plots.
C’est un peu dommage de se priver de la syntaxe de ggplot2
une fois
qu’on la connaît, mais il y a peut-être des choses sympas à trouver dans
ce package.
library(ggpubr)
ggboxplot(penguins, x = "species", y = "body_mass_g")
MonGraphiqueggpubr <- ggboxplot(penguins, x = "species", y = "body_mass_g",
color = "species", add = "jitter")
MonGraphiqueggpubr
Pour ajouter la p-value d’un test de Kruskal-Wallis (par défaut), c’est immédiat.
MonGraphiqueggpubr + stat_compare_means()
et si on préfère l’ANOVA, pas de souci
MonGraphiqueggpubr + stat_compare_means(method = "anova")
Jusqu’ici, pas de quoi sauter au plafond, on aurait gérer plus ou moins bien le truc avec un peu plus de code. En revanche, pour les comparaisons 2 à 2 , c’est quand même pas mal.
MesComparaisons <- list( c("Adelie", "Chinstrap"),
c("Adelie", "Gentoo"),
c("Gentoo", "Chinstrap") )
MonGraphiqueggpubr + stat_compare_means(comparisons = MesComparaisons)
Pour la totale :
MonGraphiqueggpubr +
stat_compare_means(comparisons = MesComparaisons) +
stat_compare_means(label.y = 5)
avec des étoiles selon le découpage indiqué dans l’aide de la fonction
stat_compare_means
:
symnum.args <- list(cutpoints = c(0, 0.0001, 0.001, 0.01, 0.05, 1), symbols = c("****", "***", "**", "*", "ns"))
MonGraphiqueggpubr +
stat_compare_means(comparisons = MesComparaisons, label = "p.signif") +
stat_compare_means(label.y = 5)
Et si je préfère les violons (avec les boxplots à l’intérieur !)
ggviolin(penguins, x = "species", y = "body_mass_g",
color = "species", add = "boxplot") +
stat_compare_means(comparisons = MesComparaisons, label = "p.signif") +
stat_compare_means(label.y = 5)
Et d’autres choses plus ou moins utiles à voir ici : https://rpkgs.datanovia.com/ggpubr
Voici quelques liens pour étoffer le survol que nous venons de faire.
-
Le site web https://ggplot2.tidyverse.org/
-
La documentation https://ggplot2.tidyverse.org/reference/
-
Le livre ggplot2 Elegant Graphics for Data Analysis https://ggplot2-book.org/
-
La cheatsheet de
ggplot2
https://raw.githubusercontent.com/rstudio/cheatsheets/master/data-visualization.pdf -
Des packages de l’écosystème
ggplot2
https://exts.ggplot2.tidyverse.org/gallery