SAVOR: Part IV
ggplot2
Introduction
Ce document donne quelques éléments introductifs sur l’utilisation du package ggplot2
.
Le package ggplot2
ggplot2
est un package de plus en plus utilisé pour tracer des graphiques sur R. La syntaxe est différente des fonctions graphiques de base, mais s’avère très puissante en pratique. Une très bonne référence pour commencer à utiliser ce package se trouve ici. L’aide en ligne est plus complète (mais très fournie). On pourra également se reporter à cet aide-mémoire
Installer le package puis le charger.
Importer ensuite le jeu de données naissance.txt
documentant le poids à la naissance de 189 nouveaux-nés en fonctions d’un certains nombres de variable (dont l’âge de la mère, son poids ainsi que l’historique d’hypertension), puis transformer la variable SMOKE
pour illustrer le codage “Non-Fumeur” (codé 0
) et “Fumeur” (codé 1
), ainsi que la variable HT
codée “Non” (0
) et “Oui” (1
).
naissance <- read.delim("../Data/naissance.txt")
naissance <- transform(naissance,
SMOKE = factor(SMOKE, labels = c("Non-Fumeur", "Fumeur")),
HT = factor(HT, labels = c("Non", "Oui")))
str(naissance)
'data.frame': 189 obs. of 10 variables:
$ ID : int 85 86 87 88 89 91 92 93 94 95 ...
$ AGE : int 19 33 20 21 18 21 22 17 29 26 ...
$ LWT : int 182 155 105 108 107 124 118 103 123 113 ...
$ RACE : int 2 3 1 1 1 3 1 3 1 1 ...
$ SMOKE: Factor w/ 2 levels "Non-Fumeur","Fumeur": 1 1 2 2 2 1 1 1 2 2 ...
$ PTL : int 0 0 0 0 0 0 0 0 0 0 ...
$ HT : Factor w/ 2 levels "Non","Oui": 1 1 1 1 1 1 1 1 1 1 ...
$ UI : int 1 0 0 1 1 0 0 0 0 0 ...
$ FVT : int 0 3 1 2 0 0 1 1 1 0 ...
$ BWT : int 2523 2551 2557 2594 2600 2622 2637 2637 2663 2665 ...
La fonction ggplot()
ggplot2
fonctionne par un système de couches (“layers”). Ce système permet de rajouter des couches avec +
.
La fonction principale du package ggplot2
est la fonction ggplot()
. À cette fonction, qui a pour but de spécifier et de préparer les données pour le graphique, on ajoute donc des couches grâce aux fonctions geom_...()
, qui vont spécifier le type de graphique à afficher (par exemple geom_point()
ou geom_bar()
).
De plus, ggplot2
permet de rajouter des couches à des graphiques déjà existants sauvegardés dans des objets. C’est un comportement très différent de la gestion des graphiques comparé au R
de base.
Nuage de points
Exercice 1 :
Tracer le nuage de point du poids à la naissance (variable
BWT
) en fonction de l’âge (variableAGE
) grâce à la fonctiongeom_point()
.Ajouter une couleur selon le statut tabagique de la mère (
SMOKE
) (on remarque que la légende se trace automatiquement).Rajouter une droite de régression avec la fonction
geom_smooth()
.Consulter l’aide de la fonction
geom_smooth()
pour enlever l’intervalle de confiance et tracer la droite en rouge.A l’aide de
geom_hline()
, ajouter une droite horizontale d’ordonnée 2500, bleue et de style “tirets”.Créer un objet
p
qui contient le graphique du nuage de points uniquement comme demandé à la question 1 (taper le nom de l’objet pour vérifier que le graphique a été correctement créé.). Rajouter ensuite la droite de régression en modifiantp
.Préciser le nom des axes.
Ajouter le thème
theme_bw()
afin de se rapprocher d’un style plus classique de graphique.
ggplot(naissance, aes(x=AGE, y=BWT)) +
geom_point(aes(color=SMOKE)) +
geom_smooth(method = lm, se = FALSE, colour = "red") +
geom_hline(aes(yintercept=2500), colour = "blue", linetype = "dashed") +
theme_bw() +
ylab("Poids à la naissance (en g)") +
xlab("Âge de la mère")
`geom_smooth()` using formula = 'y ~ x'
Stratification
Le package ggplot2
rend très facile la stratification des représentations selon des groupes définis par une variable catégorielle.
Exercice 2 :
- Reproduire le graphique précédent en traçant 2 droites de regression selon le statut tabagique de la mère (en introduisant l’estéthique
group
dans la fonctionaes()
de la fonctiongeom_smooth()
).
ggplot(naissance, aes(x=AGE, y=BWT)) +
geom_point(aes(color=SMOKE)) +
geom_smooth(method = lm, se = FALSE, aes(group=SMOKE)) +
geom_hline(aes(yintercept=2500), colour = "blue", linetype = "dashed") +
theme_bw() +
ylab("Poids à la naissance (en g)") +
xlab("Âge de la mère")
Utiliser la fonction
facet_wrap()
pour tracer 2 graphiques distincts.Laisser l’échelle s’adapter à chacun des 2 graphiques.
ggplot(naissance, aes(x=AGE, y=BWT)) +
geom_point() +
geom_smooth(method = lm, se = FALSE, colour = "red") +
geom_hline(aes(yintercept=2500), colour = "blue", linetype = "dashed") +
theme_bw() +
ylab("Poids à la naissance (en g)") +
xlab("Âge de la mère") +
facet_wrap(~SMOKE, scales="free")
Autres types de graphiques
Exercice 3 :
- Tracer un boxplot du poids du bébé à la naissance (on utilisera
x=""
dans les esthétiques degeom_boxplot()
).
ggplot(naissance) +
geom_boxplot(aes(x="", y=BWT)) +
theme_bw() +
ylab("Poids à la naissance") +
xlab("")
Stratifier selon le statut fumeur de la mère (on pourra s’intéresser à l’esthétique
fill
).Ajouter un titre avec la fonction
ggtitle()
.
ggplot(naissance) +
geom_boxplot(aes(x=SMOKE, y=BWT, fill = SMOKE)) +
theme_bw() +
ylab("Poids à la naissance") +
xlab("Statut tabagique de la mère") +
ggtitle("Boxplot du poids à la naissance")
- Tracer un histogramme du poids à la naissance avec la fonction
geom_histogram()
. Ajouter un contour blanc pour une meilleure lisibilité. Ajouter une ligne verticale indiquant la limite inférieure de 2,5 kg.
ggplot(naissance) +
geom_histogram(aes(x=BWT), color="white") +
geom_vline(aes(xintercept=2500), colour = "black", linetype = "dashed") +
theme_bw() +
xlab("Poids à la naissance") +
ylab("Effectif") +
ggtitle("Histogramme du poids à la naissance")
Remplir les barres de l’histogramme selon l’historique d’hypertension.
Changer le code couleur grâce à la fonction
scale_fill_manual()
.
ggplot(naissance) +
geom_histogram(aes(x=BWT, fill=HT), color="white") +
geom_vline(aes(xintercept=2500), colour = "black", linetype = "dashed") +
theme_bw() +
xlab("Poids à la naissance") +
ylab("Effectif") +
ggtitle("Histogramme du poids à la naissance") +
scale_fill_manual("Historique d'hypertension", values=c("skyblue", "red2"))
- Tracer une estimation de la densité de probabilité du poids à la naissance, dans chaque groupe de statut tabagique (sur le même graphique) avec la fonction
geom_density()
.
ggplot(naissance) +
geom_density(aes(x=BWT, fill=SMOKE, color=SMOKE), alpha=0.3) +
theme_bw() +
xlab("Poids à la naissance") +
ylab("Densité") +
ggtitle("Estimateur non-paramétrique de la densité du poids à la naissance") +
scale_fill_discrete("Statut tabagique") +
scale_color_discrete("Statut tabagique")
Passer à l’échelle logarithmique avec la fonction
scale_x_log10()
.Exporter le graphique au fromat .png avec la fonction
ggsave()
, avec une résolution de 300 dpi, puis au format .eps.Ajouter une ligne verticale indiquant la limite inférieure de 2,5 kg, puis ajouter une légende pour la ligne verticale.
ggplot(naissance) +
geom_density(aes(x=BWT, fill=SMOKE), alpha=0.3) +
geom_vline(aes(xintercept=2500, linetype = "dashed"), colour = "blue") +
theme_bw() +
xlab("Poids à la naissance") +
ylab("Densité") +
ggtitle("Estimateur non-paramétrique de la densité du poids à la naissance") +
scale_fill_discrete("Statut tabagique") +
scale_x_log10() +
annotation_logticks(sides="b") +
scale_linetype_manual(values="dashed", name="Poids de 2.5kg",
labels="")
Exercice 4: patchwork
Le package patchwork
permet de simplement accoler plusieurs ggplot
s pour former une seule figure.
Afficher les graphiques obtenus aux questions Ex 2.1, Ex 4.6 et Ex 4.10 sur un même graphique (n’hésitez pas à vous référer à l’aide Get Started de
patchwork
)Agencer les légende via la fonction
plot_layout()
(n’hésitez pas à vous référer à la section Controlling guides de l’aide sur les layout. Vous pouvez utilisez&
au lieu de plus+
afin d’appliquer un élément (comme untheme()
par exemple) à tous les plots du patchwork.À l’aide de la fonction
guides()
supprimer les légendes superflues (e.g. en utilisantguides(fill="none"))
).Ajouter une lettre pour identifier chaque graphique de votre figure finale grâce à la fonction
plot_annotation()
.
phist <- ggplot(naissance) +
geom_histogram(aes(x=BWT, fill=HT), color="white") +
geom_vline(aes(xintercept=2500), colour = "black", linetype = "dashed") +
theme_bw() +
xlab("Poids à la naissance (en g)") +
ylab("Effectif") +
ggtitle("Histogramme du poids à la naissance") +
scale_fill_manual("Historique d'hypertension", values=c("skyblue", "red2"))
plm <- ggplot(naissance, aes(x=AGE, y=BWT)) +
geom_point(aes(color=SMOKE)) +
geom_smooth(method = lm, se = FALSE, aes(group=SMOKE)) +
geom_hline(aes(yintercept=2500), colour = "blue", linetype = "dashed") +
scale_color_discrete(name="Statut tabagique") +
theme_bw() +
ylab("Poids à la naissance (en g)") +
xlab("Âge de la mère") +
scale_linetype_manual(values="dashed", name="Poids de 2.5kg",
labels="")
pdens <-ggplot(naissance) +
geom_density(aes(x=BWT, fill=SMOKE), alpha=0.3) +
geom_vline(aes(xintercept=2500, linetype = "dashed"), colour = "blue") +
theme_bw() +
xlab("Poids à la naissance (en g - échelle log)") +
ylab("Densité") +
ggtitle("Estimateur non-paramétrique de la\ndensité du poids à la naissance") +
scale_x_log10() +
annotation_logticks(sides="b") +
scale_linetype_manual(values="dashed", name="Poids de 2.5kg",
labels="") +
guides(fill="none")
(phist + pdens) / plm +
plot_layout(guides = "collect") +
plot_annotation(tag_levels = 'A') & theme(legend.direction = 'vertical', legend.position = 'bottom')
NB: D’autres packages comme cowplot
ou ggpubr
servent le même objectifs mais avec une syntaxe plus complexe.
Exercice 5: gganimate
Objective
Transform static graphics into dynamic ones with gganimate
Prerequisites
Packages installation
if (!require("gapminder", quietly = TRUE))
install.packages("gapminder")
if (!require("ggplot2", quietly = TRUE))
install.packages("ggplot2")
if (!require("gganimate", quietly = TRUE))
install.packages("gganimate")
if (!require("gifski", quietly = TRUE))
install.packages("gifski")
if (!require("transformr", quietly = TRUE))
install.packages("transformr")
if (!require("MetBrewer", quietly = TRUE))
install.packages("MetBrewer")
if (!require("ggrepel", quietly = TRUE))
install.packages("ggrepel")
if (!require("dplyr", quietly = TRUE))
install.packages("dplyr")
gganimate
documentation is here: https://gganimate.com/
Dataset
We will use the gapminder
dataset throughout this tutorial
country continent year lifeExp
Afghanistan: 12 Africa :624 Min. :1952 Min. :23.60
Albania : 12 Americas:300 1st Qu.:1966 1st Qu.:48.20
Algeria : 12 Asia :396 Median :1980 Median :60.71
Angola : 12 Europe :360 Mean :1980 Mean :59.47
Argentina : 12 Oceania : 24 3rd Qu.:1993 3rd Qu.:70.85
Australia : 12 Max. :2007 Max. :82.60
(Other) :1632
pop gdpPercap
Min. :6.001e+04 Min. : 241.2
1st Qu.:2.794e+06 1st Qu.: 1202.1
Median :7.024e+06 Median : 3531.8
Mean :2.960e+07 Mean : 7215.3
3rd Qu.:1.959e+07 3rd Qu.: 9325.5
Max. :1.319e+09 Max. :113523.1
The “problem” with static graphics
👉 TODO n°1: with ggplot2
, plot the life expectancy according to the GDP of the country
ggplot(gapminder, aes(gdpPercap, lifeExp)) +
geom_point() +
scale_x_log10() +
labs(x = "GDP per capita", y = "Life expectancy") +
theme_bw()
👉 TODO n°2: using the scale_size
argument, change the previous graph to account for the total population in a given year (e.g. 2007).
Bonus: Add a 4^th^ dimension and color your plot by continent.
#> label: todo2
library(MetBrewer)
ggplot(data = gapminder %>% filter(year == 2007),
mapping = aes(gdpPercap, lifeExp, size = pop, color = continent)) +
geom_point(alpha=0.7) +
scale_x_log10() +
labs(title="2007", x = "GDP per capita", y = "Life expectancy") +
scale_size(name = "Population", breaks = c(10^7, 5*10^7, 10^8, 5*10^8, 10^9, 5*10^9), range=c(1, 15)) +
scale_color_manual("Continent", values = MetBrewer::met.brewer("Cross", n=5, type = "d"))+
theme_classic()
#theme(legend.position = "bottom")
👉 TODO n°3 : Make a graph to visualize life expectancy acoording the country’s GDP each year.
#> label: todo3
ggplot(gapminder, aes(gdpPercap, lifeExp, size = pop, color = continent)) +
geom_point(alpha=0.7) +
scale_x_log10() +
labs(x = "GDP per capita", y = "Life expectancy") +
scale_size(name = "Population", breaks = c(10^7, 5*10^7, 10^8, 5*10^8, 10^9, 5*10^9), range=c(0.3, 5)) +
scale_color_manual("Continent", values = MetBrewer::met.brewer("Cross", n=5, type = "d")) +
theme_classic() +
facet_wrap(~ year)
Animated graphics first take: transition_time()
We will now use a dynamic tool so that we don’t have to generate 55 graphs or assign a color for each years.
👉 TODO n°4: follow the example below that leverage gganimate
to generate a .gif
of animated plots.
# necessary libraries
library(gganimate)
library(gifski)
p <- ggplot(gapminder, aes(gdpPercap, lifeExp, size = pop, color = continent)) +
geom_point(alpha = 0.7) +
scale_x_log10() +
theme_minimal() +
# gganimate specific
labs(title = 'Year: {frame_time}', x = 'GDP per capita', y = 'Life expectancy') +
scale_size(name = "Population", breaks = c(10^7, 5*10^7, 10^8, 5*10^8, 10^9, 5*10^9), range=c(1, 15)) +
scale_color_manual("Continent", values = MetBrewer::met.brewer("Cross", n=5, type = "d")) +
transition_time(year) +
ease_aes('linear')
p
animate(p, duration = 5, fps = 20, width = 500, height = 500,
renderer = gifski_renderer())
# Save as a .gif
anim_save("../Results/gganimate1.gif")
⚠️ Dynamic graphics are not only exclusively for time varying quantities: for instance one can add each continent after another on the graphic.
👉 your turn !
pbis <- ggplot(gapminder, aes(gdpPercap, lifeExp, size = pop, color = country)) +
geom_point(alpha = 0.7) +
scale_x_log10() +
theme_bw() +
# gganimate specifics
labs(title = 'Year: {frame_time}', x = 'GDP per capita', y = 'Life expectancy') +
scale_size(name = "Population", breaks = c(10^7, 5*10^7, 10^8, 5*10^8, 10^9, 5*10^9), range=c(1, 15)) +
guides(color="none") +
scale_colour_manual(values = country_colors) +
theme(axis.text.x = element_text(angle=45, hjust=1)) +
facet_grid(.~continent) +
transition_time(year) +
ease_aes('linear')
pbis
animate(pbis, duration = 5, fps = 20, width = 500, height = 500)
Advanced animated graphics: more transitions
transition_layers()
👉 TODO n°5: with ggplot2
, generate a boxplot of lifeExpectancy per continent.
ggplot(gapminder, aes(continent, lifeExp, color=continent, fill=continent)) +
geom_boxplot(alpha=0.4) +
labs(x = NULL, y = "Life expectancy") +
guides(color = "none", fill="none") +
scale_color_manual("Continent", values = MetBrewer::met.brewer("Cross", n=5, type = "d")) +
scale_fill_manual("Continent", values = MetBrewer::met.brewer("Cross", n=5, type = "d")) +
theme_classic()
👉 TODO n°5 bis: make this graphic dynamic (start from the median and generate a “powerpoint-like” fading)
ggplot(gapminder, aes(continent, lifeExp, color=continent, fill=continent)) +
labs(x = NULL, y = "Life expectancy") +
guides(color = "none", fill="none") +
geom_boxplot(alpha=0.4) +
scale_color_manual("Continent", values = MetBrewer::met.brewer("Cross", n=5, type = "d")) +
scale_fill_manual("Continent", values = MetBrewer::met.brewer("Cross", n=5, type = "d")) +
theme_classic() +
theme(panel.grid = element_blank()) +
transition_layers(layer_length = 0.2, from_blank=TRUE) +
enter_grow() +
enter_fade()
animate(anim_bp, rewind=TRUE, duration=5)
transition_reveal()
👉 TODO n°6: with ggplot2
, plot lines to study the life expectancy according to time for each continent
ggplot(gapminder %>% group_by(continent, year) %>% summarise(lifeExp = median(lifeExp)),
aes(x=year, y=lifeExp, color = continent)) +
geom_line() +
xlab(NULL) +
ylab("Median life expectancy") +
scale_color_manual(name = "Continent", values = MetBrewer::met.brewer("Cross", n=5, type = "d")) +
guides(color="none") +
theme_classic() +
scale_x_continuous(expand=expansion(mult = c(.05, .09))) +
scale_y_continuous(expand = expansion(mult = c(.05, .08))) +
ggrepel::geom_text_repel(data = gapminder %>% group_by(continent, year) %>%
summarise(lifeExp = median(lifeExp)) %>% filter(year==max(year)),
aes(label = continent, x=year-2), direction="y", hjust=0, force=1.5,
seed=452023)
`summarise()` has grouped output by 'continent'. You can override using the
`.groups` argument.
`summarise()` has grouped output by 'continent'. You can override using the
`.groups` argument.
👉 TODO n°6 bis: Do it again, but this time as a animated graphic
p_reveal <- ggplot(gapminder %>% group_by(continent, year) %>% summarise(lifeExp = median(lifeExp)),
aes(x=year, y=lifeExp, color = continent)) +
geom_line() +
xlab(NULL) +
ylab("Median life expectancy") +
scale_color_manual(name = "Continent", values = MetBrewer::met.brewer("Cross", n=5, type = "d")) +
guides(color="none") +
theme_classic() +
scale_x_continuous(expand=expansion(mult = c(.05, .10))) +
scale_y_continuous(expand = expansion(mult = c(.05, .08))) +
geom_text_repel(data = gapminder %>% group_by(continent, year) %>%
summarise(lifeExp = median(lifeExp)),
aes(label = continent, x=year+1), direction="y", hjust=0,
seed=452023) +
transition_reveal(year) +
view_follow(fixed_x = TRUE,
fixed_y = FALSE)
p_reveal
animate(p_reveal, end_pause=3)
transition_states()
& shadow_mark()
👉 TODO n°7: Show the life-expectancy density over the years
library(ggridges)
gapminder$year_fact=factor(gapminder$year,
levels=sort(unique(gapminder$year), decreasing = TRUE),
ordered=TRUE)
p7_ridges <- ggplot(gapminder) +
#geom_density(aes(lifeExp, group = factor(year), fill=year), alpha = 0.2) +
ggridges::geom_density_ridges(aes(x=lifeExp, y=year_fact, fill=year, color=year), alpha = 0.6) +
labs(x = "Life expectancy", y = NULL) +
scale_fill_gradient("Year", low = "firebrick1", high="dodgerblue3") +
scale_color_gradient("Year", low = "firebrick1", high="dodgerblue3") +
guides(color="none", fill="none") +
theme_classic()
👉 TODO n°7 bis: animate it !
ggplot(gapminder) +
geom_density(aes(lifeExp, fill=year, color=year), alpha = 0.2) +
labs(title = 'Year: {closest_state}', x = "Life expectancy", y = "Density") +
scale_fill_gradient("", low = "firebrick1", high="dodgerblue3") +
scale_color_gradient("", low = "firebrick1", high="dodgerblue3") +
theme_bw() +
theme(panel.grid = element_blank()) +
transition_states(year, wrap = FALSE) +
ease_aes('cubic-in-out') +
shadow_mark(color="grey95", fill="grey60",alpha=0.05, future = FALSE)
👉 BONUS: Draw me a dinosaur (check the datasauRus
package) !
library(datasauRus)
dino_data <- datasaurus_dozen %>%
filter(dataset == "dino")
dino_data_fromrandom <- rbind.data.frame(dino_data, cbind.data.frame(dataset = "random",
x = runif(nrow(dino_data))*100,
y = runif(nrow(dino_data))*100))
ggplot(dino_data_fromrandom,
aes(x, y)) +
geom_point(color="green3") +
theme_void() +
transition_states(dataset, 0.5,1)