---
title: "SAVOR: Part IV"
subtitle: "`ggplot2`"
author: "Robin Genuer & Boris Hejblum"
date: "06/06/2024"
highlight-style: pygments
format: 
  html:
    toc: true
    code-link: true
    tidy: true
    message: true
    embed-resources: true
    fig.align: center
    fig.height: 4
    fig.width: 6
editor: source
---

```{r}
#| include: false
#| label: "setup"
correction <- TRUE
```

# 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](http://www.cookbook-r.com/Graphs/).
L'[aide en ligne](http://docs.ggplot2.org/current/) est plus complète
(mais très fournie). On pourra également se reporter à cet [aide-mémoire](https://rstudio.github.io/cheatsheets/data-visualization.pdf)



Installer le package puis le charger.

```{r, echo = correction}
# install.packages("ggplot2")
library(ggplot2)
```

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`).
 
```{r, echo = correction}
#| tidy: false
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)
```


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

 1. Tracer le nuage de point du poids à la naissance (variable `BWT`) en fonction de l'âge (variable `AGE`) grâce à la fonction `geom_point()`.

2. Ajouter une couleur selon le statut tabagique de la mère (`SMOKE`) (*on remarque que la légende se trace automatiquement*).

3. Rajouter une droite de régression avec la fonction `geom_smooth()`.

4. Consulter l'aide de la fonction `geom_smooth()` pour enlever l'intervalle
de confiance et tracer la droite en rouge.

5. A l'aide de `geom_hline()`, ajouter une droite
horizontale d'ordonnée 2500, bleue et de style "tirets".

6. 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 modifiant `p`.

7. Préciser le nom des axes.

8. Ajouter le thème `theme_bw()` afin de se rapprocher d'un style plus classique
de graphique.

```{r, echo=correction}
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")
```


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

1. 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 fonction `aes()`
de la fonction `geom_smooth()`).

```{r, echo=correction}
#| message: false
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")
```

2. Utiliser la fonction `facet_wrap()` pour tracer 2 graphiques distincts. 

3. Laisser l'échelle s'adapter à chacun des 2 graphiques.

```{r, echo=correction}
#| message: false
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 :

1. Tracer un boxplot du poids du bébé à la naissance (on utilisera `x=""` dans 
les esthétiques de `geom_boxplot()`).

```{r, echo=correction, fig.width=3}
ggplot(naissance) + 
  geom_boxplot(aes(x="", y=BWT)) + 
  theme_bw() + 
  ylab("Poids à la naissance") +
  xlab("")
```

2. Stratifier selon le statut fumeur de la mère (on pourra s'intéresser à 
l'esthétique `fill`).

3. Ajouter un titre avec la fonction `ggtitle()`.

```{r, echo=correction}
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")
```

4. 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.

```{r, echo=correction, fig.width=5}
#| message: false
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")
```

5. Remplir les barres de l'histogramme selon l'historique d'hypertension.

6. Changer le code couleur grâce à la fonction `scale_fill_manual()`.

```{r, echo=correction}
#| message: false
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"))
```


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

```{r, echo=correction}
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")
```

8. Passer à l'échelle logarithmique avec la fonction `scale_x_log10()`.

9. Exporter le graphique au fromat *.png* avec la fonction `ggsave()`, avec une résolution de 300 dpi, puis au format *.eps*.

10. Ajouter une ligne verticale indiquant la limite inférieure de 2,5 kg, puis ajouter une légende pour la ligne verticale.

```{r, echo=correction}
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`](https://patchwork.data-imaginist.com/) permet de simplement accoler plusieurs `ggplot`s pour former une seule figure. 

```{r}
#| label: "patchwork load"
#| echo: true
library(patchwork)
```


1. 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*](https://patchwork.data-imaginist.com/articles/patchwork.html) de `patchwork`)

2. Agencer les légende via la fonction `plot_layout()` (n'hésitez pas à vous référer à la section [*Controlling guides*](https://patchwork.data-imaginist.com/articles/guides/layout.html#controlling-guides) de l'aide sur les *layout*. Vous pouvez utilisez `&`au lieu de plus `+` afin d'appliquer un élément (comme un `theme()` par exemple) à tous les *plots* du *patchwork*.

3. À l'aide de la fonction `guides()` supprimer les légendes superflues (e.g. en utilisant `guides(fill="none"))`).

4. Ajouter une lettre pour identifier chaque graphique de votre figure finale grâce à la fonction `plot_annotation()`.

```{r, echo=correction, fig.width=8, fig.height=10}
#| label: "patchwork ex1"
#| message: false
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`](https://cran.r-project.org/web/packages/cowplot/vignettes/introduction.html) ou [`ggpubr`](https://rpkgs.datanovia.com/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
```{r message=FALSE}
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/](https://gganimate.com/)



## Dataset

We will use the `gapminder` dataset throughout this tutorial
```{r}
#| label: "data load and overview"
library(gapminder)
summary(gapminder)
```


# The "problem" with static graphics

`r emoji::emoji("point_right")` TODO n°1: with `ggplot2`, plot the life expectancy according to the GDP of the country

```{r, eval=correction, echo=correction}
#| label: todo1
ggplot(gapminder, aes(gdpPercap, lifeExp)) +
  geom_point() +
  scale_x_log10() +
  labs(x = "GDP per capita", y = "Life expectancy") +
  theme_bw()
```


`r emoji::emoji("point_right")` 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. 

```{r, eval=correction, echo=correction}
#> 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")
```



`r emoji::emoji("point_right")` TODO n°3 : Make a graph to visualize life expectancy acoording the country's GDP each year.

```{r, eval=correction, echo=correction}
#> 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.


`r emoji::emoji("point_right")` TODO n°4: follow the example below that leverage `gganimate` to generate a `.gif` of animated plots.

```{r}
#| label: todo4
#| echo: true
#| eval: true
# 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
```


```{r, echo=TRUE, eval=FALSE}
animate(p, duration = 5, fps = 20, width = 500, height = 500,
        renderer = gifski_renderer())

# Save as a .gif
anim_save("../Results/gganimate1.gif")
```

`r emoji::emoji("warning")` Dynamic graphics are not only exclusively for time 
varying quantities: for instance one can add each continent after another on 
the graphic.  
`r emoji::emoji("point_right")` your turn ! 

```{r, echo=correction}
#| eval: true 
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
```

```{r echo=correction}
#| eval: false
animate(pbis, duration = 5, fps = 20, width = 500, height = 500)
```



# Advanced animated graphics: more `transitions`

##  `transition_layers()`
 
`r emoji::emoji("point_right")` TODO n°5: with `ggplot2`, generate a boxplot of lifeExpectancy per continent.

```{r, eval=correction, echo=correction}
#| label: todo5
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()
```


`r emoji::emoji("point_right")` TODO n°5 bis: make this graphic dynamic (start from the median and generate a "powerpoint-like" fading)
```{r, echo=correction}
#| label: todo5bis
#| eval: true
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()
```


```{r, echo=correction}
#| eval: false
animate(anim_bp, rewind=TRUE, duration=5)
```

## `transition_reveal()`

`r emoji::emoji("point_right")` TODO n°6: with `ggplot2`, plot lines to study the life expectancy according to time for each continent

```{r, eval=correction, echo=correction}
#| label: todo6
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)
```


`r emoji::emoji("point_right")` TODO n°6 bis: Do it again, but this time as a animated graphic

```{r, echo=correction, message=FALSE}
#| label: todo6bis
#| cache: true
#| eval: true
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
```


```{r, eval=correction, echo=correction, message=FALSE}
animate(p_reveal, end_pause=3)
```


## `transition_states()` & `shadow_mark()`
`r emoji::emoji("point_right")` TODO n°7: Show the life-expectancy density over the years

```{r, echo=FALSE}
#| label: todo7simple
#| eval: true
p7_simple <- ggplot(gapminder) +
  geom_density(aes(lifeExp, group = factor(year), fill=year), alpha = 0.2) +
  labs(x = "Life expectancy", y = "Density") +
  scale_fill_gradient(low = "firebrick1", high="dodgerblue3") +
  theme_bw()
```


```{r, echo=correction}
#| message: false
#| eval: true
#| label: todo7
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()
```

```{r, echo=correction}
#| label: patchwork
#| eval: true
#| message: false
library(patchwork)
p7_simple / p7_ridges
```



`r emoji::emoji("point_right")` TODO n°7 bis: animate it !

```{r, echo=correction}
#| label: todo7bis
#| eval: true
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)
```


`r emoji::emoji("point_right")` ***BONUS:*** Draw me a dinosaur  (check the `datasauRus` package) !

```{r, echo=correction}
#| label: dinosaur
#| eval: true
#| cache: true
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)
```



