"une annotation explicative m'accompagne à droite"- 1
- I appear when you hover your mouse over me 🐭 !
ggplot2NC
In this practical session, We will learn how to create synthetic graphical representations with , which is very well equipped for this task thanks to the ggplot2 library. This library implements a grammar of graphics that is flexible, consistent, and easy to use.
If you are interested in Python , a very similar version of this practical session is available in my ENSAE course.
In this course, the practice of visualization will involve replicating charts found on the Paris Open Data page here.
This practical session aims to introduce:
ggplot2 package for creating static plots;plotly package for interactive HTML plots. We will deepen our knowledge of HTML plots in a later chapter using Observable.In this chapter, we will use the following libraries:
Then, we will see how to easily create maps in equivalent formats.
Being able to create interesting data visualizations is an essential skill for any data scientist or researcher. To improve the quality of these visualizations, it is recommended to follow some advice given by data visualization specialists on graphical semiology.
Good data visualizations, such as those in the New York Times, rely not only on appropriate tools (JavaScript libraries) but also on certain rules of representation that allow the message of a visualization to be understood in seconds.
This blog post is a useful resource to consult regularly. This blog post by Albert Rapp demonstrates how to gradually build a good data visualization.
A subset of Paris Open Data has been made available to facilitate import.
It is an extraction, somewhat dated, of the original dataset where only the columns used in this exercise have been retained.
We propose downloading these data and saving them to a local file before importing them1. However, we will not do this manually but rather use . Doing this manually would be bad practice in terms of reproducibility.
Trying to produce a perfect visualization on the first attempt is unrealistic. It is more realistic to gradually improve a graphic representation to highlight structural effects in a dataset step by step.
We will therefore start by representing the distribution of traffic at the main measurement stations.
To do this, we will quickly produce a barplot and then improve it gradually.
In this section, we will reproduce the first two charts from the data analysis page: The 10 counters with the highest hourly average and The 10 counters with the highest total counts. The numeric values in the plots will differ from those on the online page, which is normal because we are working with older data.
The data contains several dimensions suitable for statistical analysis. Therefore, it is necessary first to summarize them with aggregations to create a readable plot.
bike.gz file;# A tibble: 6 × 2
`Nom du compteur` `Comptage horaire`
<chr> <dbl>
1 Totem 73 boulevard de Sébastopol S-N 197.
2 Totem 73 boulevard de Sébastopol N-S 148.
3 89 boulevard de Magenta NO-SE 144.
4 Totem 64 Rue de Rivoli O-E 140.
5 102 boulevard de Magenta SE-NO 137.
6 72 boulevard Voltaire NO-SE 124.
On va maintenant pouvoir se concentrer sur la production de la représentation
La suite de l’exercice consiste à améliorer graduellement cette représentation pour converger vers la reproduction de la version en open data. Il ne s’agit pas encore de se concentrer sur l’esthétique de la figure mais de la rendre intelligible, à gros trait.
reorder. Cela rendra le message de la figure plus intelligible.On commence à avoir quelque chose qui commence à transmettre un message synthétique sur la nature des données. On peut néanmoins remarquer plusieurs éléments problématiques (par exemple les labels) mais aussi des éléments ne correspondant pas (les titres des axes, etc.) ou manquants (le nom du graphique…)
La figure comporte maintenant un message mais il est encore peu lisible.
ggplot2 mais ce n’est pas très soigné. Utiliser un thème plus minimaliste afin d’avoir un fond blanc.theme(
axis.text.x = element_text(angle = 45, hjust = 1, color = "red"),
axis.title.x = element_text(color = "red"),
plot.title = element_text(hjust = 0.5),
plot.margin = margin(1, 4, 1, 1, "cm")
)On comprend ainsi que le boulevard de Sébastopol est le plus emprunté, ce qui ne vous suprendra pas si vous faites du vélo à Paris. Néanmoins, si vous n’êtes pas familiers avec la géographie parisienne, cela sera peu informatif pour vous, vous allez avoir besoin d’une représentation graphique supplémentaire: une carte ! Nous verrons ceci lors d’un prochain chapitre.
Faire la même chose pour la figure 2 (“Les 10 compteurs ayant comptabilisés le plus de vélos”), afin d’obtenir une figure similaire.
# Create a horizontal bar plot
figure2 <- ggplot(df2, aes(y = reorder(`Nom du compteur`, `Comptage horaire`), x = `Comptage horaire`)) +
geom_bar(stat = "identity", fill = "forestgreen") +
labs(title = "Les 10 compteurs ayant comptabilisés le plus de vélos",
x = "Nom du compteur",
y = "La somme des vélos comptabilisés sur la période sélectionnée") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1),
axis.title.x = element_text(color = "forestgreen"),
plot.title = element_text(hjust = 0.5),
plot.margin = margin(1, 4, 1, 1, "cm"))Les diagrammes en batons (barplot) sont extrêmement communs mais qu’ils transmettent. Sur le plan sémiologique, les lollipop charts sont préférables: ils transmettent la même information mais avec moins de bruit (la largeur des barres du barplot noie un peu l’information).
Voici, par exemple, la deuxième figure de la page, rendue non plus sous forme de barplot mais sous forme de lollipop chart:
df2_lollipop <- df2 %>%
mutate(x = fct_reorder(`Nom du compteur`, `Comptage horaire` ), y = `Comptage horaire`)
figure2_lollipop <- ggplot(df2_lollipop, aes(x=x, y=y)) +
geom_segment( aes(xend=x, yend=0), alpha = 0.4) +
geom_point( size=5, color="forestgreen") +
coord_flip() +
labs(title = "Les 10 compteurs ayant comptabilisés le plus de vélos",
x = "Nom du compteur",
y = "La somme des vélos comptabilisés sur la période sélectionnée") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1),
axis.title.x = element_text(color = "forestgreen"),
plot.title = element_text(hjust = 0.5),
plot.margin = margin(1, 4, 1, 1, "cm")) +
scale_y_continuous(labels = unit_format(unit = "M", scale=1e-6))Reprendre l’exercice 2 mais à la place d’un barplot, produire un lollipop chart.
On va maintenant se concentrer sur la dimension spatiale de notre jeu de données à travers deux approches:
Pour commencer, reproduisons la troisième figure qui est, encore une fois, un barplot. La première question implique une première rencontre avec une donnée temporelle à travers une opération assez classique en séries temporelles: changer le format d’une date pour pouvoir faire une agrégation à un pas de temps plus large.
format pour créer une variable month dont le format respecte, par exemple, le schéma 2019-08 ;Appliquer les conseils précédents pour construire et améliorer graduellement un graphique afin d’obtenir une figure similaire à la 3e production sur la page de l’open data parisien.
figure3 <- ggplot(comptage_horaires_mois) +
geom_bar(aes(x = month, y = value), fill = "#ffcd00", stat = "identity") +
labs(x = "Date et heure de comptage", y = "Moyenne mensuelle du comptage par heure\nsur la période sélectionnée",
title = "Moyenne mensuelle des comptages vélos") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1),
axis.title.y = element_text(color = "#ffcd00", face = "bold"),
plot.title = element_text(hjust = 0.5),
plot.margin = margin(1, 4, 1, 1, "cm"))Si vous préférez représenter cela sous forme de lollipop3:
ggplot(comptage_horaires_mois, aes(x = month, y = value)) +
geom_segment(aes(xend = month, yend = 0)) +
geom_point( color="#ffcd00", size=4) +
labs(x = "Date et heure de comptage", y = "Moyenne mensuelle du comptage par heure\nsur la période sélectionnée",
title = "Moyenne mensuelle des comptages vélos") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1),
#axis.title.y = element_text(color = "#ffcd00", face = "bold"),
plot.title = element_text(hjust = 0.5),
plot.margin = margin(1, 4, 1, 1, "cm"))
Il est plus commun de représenter sous forme de série les données ayant une dimension temporelle.
day qui transforme l’horodatage en format journalier du type 2021-05-01.figure4 <- figure4 +
labs(x = "Date et heure de comptage (Jour)", y = "Moyenne journalière du comptage par heure\nsur la période sélectionnée",
title = "Moyenne journalière des comptages vélos") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(hjust = 0.5),
plot.margin = margin(1, 4, 1, 1, "cm"))Voici quelques aides pour cet exercice
day du package lubridate
stackoverflow peut vous aider.
A l’issue de cet exercice, on obtient ainsi une figure prenant la forme suivante:
PlotlyL’inconvénient des figures avec ggplot est que celles-ci ne permettent pas d’interaction avec le lecteur. Toute l’information doit donc être contenue dans la figure ce qui peut la rendre difficile à lire. Si la figure est bien faite, avec différents niveaux d’information, cela peut bien fonctionner.
Il est néanmoins plus simple, grâce aux technologies web, de proposer des visualisations à plusieurs niveaux. Un premier niveau d’information, celui du coup d’oeil, peut suffire à assimiler les principaux messages de la visualisation. Ensuite, un comportement plus volontaire de recherche d’information secondaire peut permettre d’en savoir plus. Les visualisations réactives, qui sont maintenant la norme dans le monde de la dataviz, permettent ce type d’approche: le lecteur d’une visualisation peut passer sa souris à la recherche d’information complémentaire (par exemple les valeurs exactes) ou cliquer pour faire apparaître des informations complémentaires sur la visualisation ou autour.
Ces visualisations reposent sur le même triptyque que l’ensemble de l’écosystème web: HTML, CSS et JavaScript. Les utilisateurs de ne vont jamais manipuler directement ces langages, qui demandent une certaine expertise, mais vont utiliser des librairies au niveau de qui génèreront automatiquement tout le code HTML, CSS et JavaScript permettant de créer la figure.
Plotly basique pour représenter sous forme de série temporelle la figure 4, sans se préoccuper du stylehovertemplate et hoverinfofig <- plot_ly(
moyenne_quotidienne, x = ~day, y = ~value,
color = I("magenta"),
hovertemplate = ~paste(day, ": ", round(value), " passages de vélo en moyenne par heure"),
hoverinfo = "text",
fill = 'tozeroy',
type = 'scatter', mode = 'lines')
fig4 <- fig %>%
layout(title = "Moyenne journalière des comptages vélos",
xaxis = list(title = "Date et heure de comptage (Jour)"),
yaxis = list(title = "Moyenne journalière du comptage par heure\nsur la période sélectionnée"))La version réactive de la figure est ainsi
Cette représentation montre bien le caractère spécial de l’année 2020. Pour rappeller au lecteur distrait la nature particulière de la période, marquée par un premier confinement qu’on voit bien dans les données, on peut, avec l’aide de la documentation, ajouter deux barres verticales pour marquer les dates de début et de fin de cette période:
vline <- function(x = 0, color = "royalblue") {
list(
type = "line",
y0 = 0,
y1 = 1,
yref = "paper",
x0 = x,
x1 = x,
line = list(color = color, dash="dot")
)
}
fig4 %>% layout(shapes = list(vline("2020-03-17"), vline("2020-05-11")))Comme dernier exercice, voici comment reproduire cette figure avec Plotly:
df1 <- df1 %>% mutate(`Nom du compteur` = fct_reorder(`Nom du compteur`, `Comptage horaire`))
fig <- plot_ly(
df1,
x = ~ `Comptage horaire`, y = ~`Nom du compteur`,
color = I("red"),
hovertext = ~paste0(`Nom du compteur`, ": ", round(`Comptage horaire`)),
hoverinfo = 'text',
type = 'bar',
name = 'Principales stations')
fig <- fig %>% layout(
yaxis = list(title = 'Moyenne horaire'),
xaxis = list(title = 'Nom du compteur', color = "red")
)Plotly
fct_reorder du package forcats pour réoordonner les valeurs du dataframe issu de l’exercice 1Plotly pour créer votre figure.PlotlyNormally, we recommend using the download URL directly to avoid creating an intermediate file on the disk. However, direct import with readr will not work here because the library does not recognize that the file is compressed without the .gz extension.↩︎
Ce n’est pas forcément une bonne pratique de dataviz de faire cela. En effet, cela signifie que l’échelle et la diversité des données dans celle-ci ne sont pas directement intelligibles.↩︎
J’ai retiré la couleur sur l’axe des ordonnées qui, je trouve, apporte peu à la figure voire dégrade la compréhension du message.↩︎