# Visualisation de données dans R avec ggplot2 et autres packages Au même titre que la préparation de données avec le tidyverse présentée dans cette [page Web](https://hackmd.io/zoXTvwg8SqiFZ-pw9-98AA?view), la visualisation de données avec R nécessite l’apprentissage d’un nouveau langage, mais permet la construction rapide de **graphiques complexes et personnalisables**. ![](https://i.imgur.com/9GzFShN.png) Cette page Web centralise une série d'exemples de datavisualisations produites dans l'environnement R avec `ggplot2` et d'autres packages dédiés. **ggplot2** est une librairie R de **visualisation de données**. La librairie est développée selon les principes développés par Leland Wilkinson dans son ouvrage The Grammar of Graphics2. Les commandes pour la **préparation** des jeux de données et la **création** des dataviz sont fournies. ![](https://i.imgur.com/fnC4QfH.png) cheatsheets ggplot2 en français : https://raw.githubusercontent.com/rstudio/cheatsheets/master/translations/french/ggplot2-french-cheatsheet.pdf --- ## Les données Dans cet exemple nous nous basons sur le jeu de données des signalements de [l'application Dans Ma Rue de la ville de Paris](https://teleservices.paris.fr/dansmarue/). Ce jeu de données mis à jour quotidiennement est [disponible sur le portail open data de la ville de Paris](https://opendata.paris.fr/explore/dataset/dans-ma-rue/table/?disjunctive.type&disjunctive.soustype&disjunctive.code_postal&disjunctive.ville&disjunctive.arrondissement&disjunctive.prefixe&disjunctive.conseilquartier) Ce jeu de données est particuliérement intéressant à plusieurs titres : * il contient plus de 900 000 lignes (anomalies) * il est bien structuré et complet * les anomalies sont caractérisées par de nombreux champs * comme des champs temporels * comme des champs qualitatifs * comme des champs géographiques * et même des coordonnées géographiques --- ## BAR CHART Un **diagramme à barres** (*bar chart*) est un graphique qui présente des variables catégorielles avec des barres rectangulaires avec des hauteurs ou des longueurs proportionnelles aux valeurs qu'elles représentent. ``` geom_col geom_bar ``` ### Bar chart pour représenter la répartition des signalements par type Etape1 : créer le dataframe adéquat ``` Type <- DMR %>% group_by(TYPE) %>% summarise( nb = n()) ``` Etape 2 : Faire le graphique ``` ggplot(Type) + geom_col(aes(x = TYPE, y = nb), fill="blue", width = .9) ``` ![](https://i.imgur.com/3yxQj8W.png) On peux **passer le graphique à l'horizontal** en ajoutant la fonction `+ coord_flip()` ``` ggplot(Type) + geom_col(aes(x = TYPE, y = nb), fill="blue", width = .9) + coord_flip() ``` ![](https://i.imgur.com/Wz3VbiY.png) Pour finaliser le graphique on peux ajouter divers élements comme: ``` reorder() > pour ordonner par valeur les modalités dans le graphique fill() > pour modifier la couleur des barres ggtitle > pour ajouter un titre xlab > pour peronnaliser le nom de l'axe x ylab > pour peronnaliser le nom de l'axe y labs > pour ajouter une source theme_bw() > pour personnaliser le thème ``` Plus d'informations sur les thèmes ggplot2 https://ggplot2.tidyverse.org/reference/ggtheme.html ``` ggplot(Type, aes(reorder(TYPE, nb), nb, width = .9)) + geom_col(fill="#0074D9") + coord_flip() + ggtitle("Nombre de signalements par type") + xlab("Type") + ylab("Nb de signalements") + labs(caption="Source : Ville de Paris") + theme_bw() ``` ![](https://i.imgur.com/I8yxF01.png) Pour finaliser on peut afficher les valeurs numériques en entier ``` install.packages("scales") library(scales) ``` Ajouter dans le script ` scale_y_continuous(labels = comma)` ``` ggplot(Type, aes(reorder(TYPE, nb), nb, width = .9)) + geom_col(fill="#0074D9") + coord_flip() + scale_y_continuous(labels = comma) + ggtitle("Nombre de signalements par type") + xlab("Type") + ylab("Nb de signalements") + labs(caption="Source : Ville de Paris") + theme_bw() ``` ![](https://i.imgur.com/TtqeXhR.png) --- ### Bar chart pour représenter la répartition des signalements par arrondissement Etape1 : créer le dataframe adéquat ``` ARDT <- DMR %>% group_by(CODE_POSTAL) %>% summarise( nb = n()) %>% mutate(CODE_POSTAL = as.character(CODE_POSTAL)) ``` Etape 2 : Faire le graphique ``` ggplot(ARDT) + geom_col(aes(x = CODE_POSTAL, y = nb), fill="#001f3f", width = .9) + theme_bw() ``` ![](https://i.imgur.com/wbu8dLw.png) --- ### Bar chart pour représenter l'évolution du nombre de signalement par année Etape1 : créer le dataframe adéquat ``` ANNEE <- DMR %>% group_by(ANNEE = ANNEE.DECLARATION) %>% summarise(nb= n()) %>% mutate(ANNEE= as.charact er(ANNEE)) ``` Etape 2 : Faire le graphique ``` ggplot(ANNEE) + geom_col(aes(x=ANNEE, y=nb, fill = "orange")) + theme_bw() ``` ![](https://i.imgur.com/vHdLM5o.png) Pour forcer l'affichage de toutes les années en abcisse ``` + scale_x_continuous(breaks=seq(2012, 2019, 1)) + ``` --- ### Bar chart (empilé) pour représenter le nombre de signalement par arrondississement et la proportion de chaque type Il est possible de faire des graphiques empilés (*stacked*) pour représenter des proportions dans des regroupements. Etape1 : créer le dataframe adéquat ``` ARDTYPE <- DMR %>% group_by(CODE_POSTAL, TYPE) %>% summarise( nb = n()) %>% mutate(CODE = as.character(CODE_POSTAL)) ``` Etape 2 : Faire le graphique ``` ggplot(ARDTYPE) + geom_col(aes(x=CODE, y=nb, fill=TYPE)) + scale_fill_brewer(palette="Paired") ``` ![](https://i.imgur.com/mbk1cQ6.png) **Produire ce graphique ** ![](https://i.imgur.com/XOay9uq.png) --- ### Bar chart pour représenter l'évolution du nombre de signalement par année et par arrondissement Il est aussi possible de **représenter au sein d'un même Bar chart des groupes** *dodge* Etape1 : créer le dataframe adéquat ``` ARDTANNEE <- DMR %>% group_by(CODE_POSTAL, ANNEE = ANNEE.DECLARATION) %>% summarise(nb= n()) %>% mutate(CODE = as.character(CODE_POSTAL), ANNEE= as.character(ANNEE)) ``` Etape 2 : Faire le graphique ``` ggplot(ARDTANNEE) + geom_col(aes(x = CODE, y = nb, fill = ANNEE), position = "dodge") + scale_fill_viridis_d(direction=-1) + theme_bw() ``` ![](https://i.imgur.com/IsCzQE3.png) ### Graphiques empilés dans les règles de l'art... ``` ANNEETYPE <- DMR %>% group_by(ANNEE.DECLARATION, TYPE) %>% summarise( nb = n()) ``` ``` ggplot(ANNEETYPE, aes(x=ANNEE, y=nb, fill = TYPE)) + geom_bar( stat = "identity", position = "stack") + scale_fill_brewer(palette="Paired") + theme_bw() ``` ![](https://i.imgur.com/22hZJoU.png) ``` ggplot(ANNEETYPE, aes(x=ANNEE, y=nb, fill = TYPE)) + geom_bar( stat = "identity", position = "fill") + scale_y_continuous(labels = comma) + scale_fill_brewer(palette="Paired") + theme_bw() ``` ![](https://i.imgur.com/6pfe0ze.png) ``` ggplot(ANNEETYPE, aes(x=ANNEE, y=nb, fill = TYPE)) + geom_bar( stat = "identity", position = "dodge") + scale_y_continuous(labels = comma) + scale_fill_brewer(palette="Paired") + theme_bw() ``` ![](https://i.imgur.com/TSFTOG8.png) --- ## JOUER AVEC LES *SMALL MULTIPLE* Un ***small multiple*** est une série de graphiques similaires utilisant la même échelle et les mêmes axes, ce qui permet de les comparer facilement. On peut "décomposer" un graphique (en small multiples) avec les fonctions : ``` facet_wrap(~variable) ``` >Décompose le graphique en autant de modalité que contenues dans la variable mobilisée. Le paramétrage de l'agencement se fait avec les arguments `nrow` (nombre de lignes) et/ou `ncol` (nombre de colonnes). ``` facet_grid(variable1~variable2) ``` >Décompose le graphique en un croisement des modalités contenues dans variable1 et variable2 --- ### Représenter le nombre de signalements par mois pour chaque année Etape1 : créer le dataframe adéquat ``` SignalementAnneeEtMois <- DMR %>% group_by(Annee = ANNEE.DECLARATION, Mois=mois) %>% summarise(nb=n()) ``` Etape 2 : Faire le graphique ``` ggplot(signalementAnneeEtMois) + geom_col(aes(x=Mois, y=nb, fill = "orange")) + theme_bw() + facet_wrap(~Annee, nrow = 4) ``` ![](https://i.imgur.com/m6iSXPq.png) --- ### Représenter le nombre de signalements par journée pour chaque arrondissement Etape1 : créer le dataframe adéquat ``` SignalJourArdt <- DMR %>% group_by(Journee = journee, Ardt=CODE_POSTAL) %>% summarise(nb=n()) %>% mutate(Ardt= as.character(Ardt)) ``` Etape 2 : Faire le graphique ``` ggplot(SignalJourArdt, aes(x=Journee, y=nb, fill = Ardt)) + geom_col() + theme_bw() + facet_wrap(~Ardt, ncol = 4) ``` ![](https://i.imgur.com/qQ0OW57.png) --- ### Représenter le nombre de signalements par mois selon le type Etape1 : créer le dataframe adéquat ``` Moisettype <- DMR %>% filter( ! TYPE %in% c('Activités commerciales et professionnelles', 'Du vert près de chez moi', 'Problème sur un chantier', 'Eau')) %>% group_by(Type=TYPE, Mois = mois) %>% summarise(nb = n()) ``` Etape 2 : Faire le graphique ``` ggplot(Moisettype, aes(x=Mois, y=nb, fill = Type)) + geom_col() + theme_bw() + facet_wrap(~Type, ncol = 4) ``` ![](https://i.imgur.com/9gfgNVT.png) --- ### Représenter le nombre de signalements par mois pour chaque année et chaque arrondissement Etape1 : créer le dataframe adéquat ``` signalementAnneeEtMoisArdt <- DMR %>% group_by(Annee = ANNEE.DECLARATION, Mois=mois, Ardt=CODE_POSTAL) %>% summarise(nb=n()) %>% mutate(Ardt= as.character(Ardt)) ``` Etape 2 : Faire le graphique ``` ggplot(signalementAnneeEtMoisArdt, aes(x=Mois, y=nb, fill = Ardt)) + geom_col() + theme_bw() + facet_grid(Ardt~Annee) ``` ![](https://i.imgur.com/hn3fy6N.png) --- ### Représenter "spatialement" les signalements par type pour une année Etape1 : créer le dataframe adéquat ``` DMRGEO <- DMR %>% separate(col= geo_point_2d, into = c("Latitude", "Longitude"), sep= ",") %>% filter(ANNEE.DECLARATION==2015) %>% mutate(Latitude = as.numeric(Latitude), Longitude = as.numeric(Longitude)) ``` Etape 2 : Faire le graphique ``` ggplot(DMRGEO) + geom_point(aes(x= Longitude, y= Latitude, color=TYPE)) + facet_wrap(~TYPE, ncol = 2) ``` ![](https://i.imgur.com/oPsWSok.png) --- ## DOUGHNUT CHART Un **Doughnut chart** est simplement un diagramme circulaire avec un trou au milieu ;) Très utile pour représenter les proportions des modalités d'une variable. https://www.r-graph-gallery.com/128-ring-or-donut-plot.html --- ### Doughnut chart des types de signalement **Etape1 : créer le dataframe adéquat** ``` type <- DMR %>% group_by(TYPE) %>% summarise(nb = n()) ``` Il faut ici procéder ici à plusieurs sous-étapes supplémentaires : * Calculer les pourcentages des types de signalement ``` type$fraction = type$nb /sum(type$nb) ``` * Calculer les pourcentages cumulatifs des types de signalement ``` type$ymax = cumsum(type$fraction) ``` * Calculer le minimum de chaque types de signalement ``` type$ymin = c(0, head(type$ymax, n=-1)) ``` Une fois ces trois sous-étapes réalisées la detaframe ressemble à ça ![](https://i.imgur.com/MF7klC6.png) **Etape 2 : Faire le graphique** ``` ggplot(type, aes(ymax=ymax, ymin=ymin, xmax=4, xmin=3, fill=TYPE)) + geom_rect() + coord_polar(theta="y") + xlim(c(2, 4)) ``` ![](https://i.imgur.com/SroYG0G.png) Changer les couleurs en utilisant le package `Rcolobrewer` https://rdrr.io/cran/RColorBrewer/man/ColorBrewer.html ![](https://i.imgur.com/JK0S2E6.png) ``` ggplot(type, aes(ymax=ymax, ymin=ymin, xmax=4, xmin=3, fill=TYPE)) + geom_rect() + scale_fill_brewer(palette = "Paired") + coord_polar(theta="y") + xlim(c(2, 4)) ``` ![](https://i.imgur.com/7jirMkv.png) --- ## TREEMAP Une carte proportionnelle (***treemap***) est une représentation de données hiérarchiques dans un espace limité. Elle permet de représenter la proportion de modalités d'une ou de plusieurs variables qualitatives. https://www.rdocumentation.org/packages/treemap/versions/2.4-2/topics/treemap https://www.r-graph-gallery.com/treemap.html ``` install.packages("treemap") library(treemap) ``` --- ### Treemap pour représenter la proportion de chacun des types de signalement Etape1 : créer le dataframe adéquat ``` Agregtype <- DMR %>% group_by(TYPE) %>% summarise(nb=n()) ``` Etape 2 : Faire le graphique ``` treemap(Agregtype, index=c("TYPE"),vSize="nb", type="index", fontsize.labels=c(15,12), fontcolor.labels=c("white","orange"), fontface.labels=c(2,1), bg.labels=c("transparent"), align.labels=list( c("center", "center"), c("right", "bottom")), overlap.labels=0.5, inflate.labels=F,) ``` ![](https://i.imgur.com/YFl6Y1r.png) --- ### Treemap pour représenter la proportion de chacun des sous-types de signalement Etape1 : créer le dataframe adéquat ``` AgregStype <- DMR %>% group_by(TYPE, SOUSTYPE) %>% summarise(nb=n()) ``` Etape 2 : Faire le graphique ``` treemap(Agregtype, index=c("TYPE"),vSize="nb", type="index", fontsize.labels=c(15,12), fontcolor.labels=c("white","orange"), fontface.labels=c(2,1), bg.labels=c("transparent"), align.labels=list( c("center", "center"), c("right", "bottom")), overlap.labels=0.5, inflate.labels=F,) ``` ![](https://i.imgur.com/la4oM5D.png) --- ### Treemap pour représenter la proportion de chacun des sous-types de signalement en les regroupant par type Etape1 : créer le dataframe adéquat ``` AgregStype <- DMR %>% group_by(TYPE, SOUSTYPE) %>% summarise(nb=n()) ``` Etape 2 : Faire le graphique ``` treemap(AgregStype, index=c("TYPE", "SOUSTYPE"),vSize="nb", type="index", fontsize.labels=c(13,10), fontcolor.labels=c("white","white"), fontface.labels=c(2,1), bg.labels=c("transparent"), align.labels=list( c("center", "center"), c("right", "bottom")), overlap.labels=0.5, inflate.labels=F,) ``` ![](https://i.imgur.com/0lY8Gzq.png) --- ### Treemap pour représenter la proportion de chacun des types de signalement par arrondissement Etape1 : créer le dataframe adéquat ``` AgregArdtType <- DMR %>% group_by(ARRONDISSEMENT, TYPE) %>% summarise(nb=n()) ``` Etape 2 : Faire le graphique ``` treemap(AgregArdtType , index=c("ARRONDISSEMENT", "TYPE"),vSize="nb", type="index", fontsize.labels=c(21,8), fontcolor.labels=c("black","white"), fontface.labels=c(2,3), bg.labels=c("transparent"), align.labels=list( c("left", "top"), c("center", "center")), overlap.labels=0.1, inflate.labels=F,) ``` ![](https://i.imgur.com/09U0M3q.png) --- ## HEATMAP Une **heatmap** est une représentation graphique de données statistiques qui fait correspondre à l'intensité d'une grandeur variable une gamme de tons ou un nuancier de couleurs sur une matrice à deux dimensions. Très pertinentes pour représenter des variables quantitatives dans une approche temporelle. http://www.columbia.edu/~sg3637/blog/Time_Series_Heatmaps.html --- ### Heatmap du nombre de signalements par jour depuis 2016 **Etape1 : créer le dataframe adéquat** Il faut pour cela correctement préparer le jeu de données en travaillant sur les données temporelles. Pour cela il faut mobiliser le package `lubridate` ``` install.packages("lubridate") library(lubridate) ``` 1. Ajouter le nom de la journée de la semaine, le nom du mois, le numéro de la semaine du mois pour chaque signalement ``` DMR <- DMR %>% mutate(jour = (day(DMR$DATEDECL)),journee = (wday(DMR$DATEDECL,label = TRUE, abbr = FALSE)), mois = month(DMR$DATEDECL, label = TRUE, abbr = FALSE ), wotm= ceiling(day(DMR$DATEDECL) / 7)) ``` ![](https://i.imgur.com/G9JvHwf.png) 2. Faire l'agrégation adéquate ``` heatmap <- DMR %>% group_by(date = DATEDECL, journee, jour, mois, annee = ANNEE.DECLARATION, wotm) %>% summarise(nb= n()) ``` ![](https://i.imgur.com/0oCf4J2.png) **Etape2 : Faire le graphique** Au préalable charger le package de couleur `wesanderson` ``` install.packages("wesanderson") library(wesanderson) ``` Puis charger la palette Zissou1 ``` pal <- wes_palette("Zissou1", 100, type = "continuous") ``` Commande pour réaliser la heatmap ``` ggplot(heatmap2, aes(wotm, journee, fill = nb)) + geom_tile(colour = "white") + facet_grid(annee~mois) + labs(fill = "Nombre de signalement") + scale_fill_gradientn(colours = pal) + xlab("Semaine du mois") + ylab("Journée") + ggtitle("Nombre de signalements par jour du service Dans Ma Rue") + labs(caption="Source : Ville de Paris") + theme_bw() ``` ![](https://i.imgur.com/HDVlnTM.png) --- ## DIAGRAMME SANKEY Un **diagramme de Sankey** ou diagramme Sankey est un type de diagramme de flux dans lequel la largeur des liens est proportionnelle aux flux représentés. Très utile pour représenter et quantifier des liens entre des variables qualitatives. ``` library(networkD3) ``` https://www.rdocumentation.org/packages/networkD3/versions/0.4/topics/sankeyNetwork ### Diagramme Sankey pour représenter le lien entre type de signalement et arrondissements Au préalable il faut créer le dataframe adéquat, ici je résume le nombre de signalements en fonction du type et de l'arrondissement. Pour faciliter la lecture du graphique je ne conserve que : * les Types de signalementles les plus présents * uniquement les arrondisssement du 10eme au 20eme ``` sankey <- DMR %>% filter(! TYPE %in% c('Activités commerciales et professionnelles', 'Du vert près de chez moi', 'Problème sur un chantier', 'Eau', 'Éclairage / Électricité', 'Mobiliers urbains', 'Arbres, végétaux et animaux' ), ! VILLE %in% c("Paris 1","Paris 2", "Paris 3", "Paris 4","Paris 5", "Paris 6", "Paris 7","Paris 8", "Paris 9")) %>% group_by(Type = TYPE, Ardt = VILLE) %>% summarise( nb= n()) ``` Il faut ensuite créer un dataframe des **noeuds** (*nodes*) qui regroupe toutes les modalités de source et de cible ``` nodes <- data.frame( name=c(as.character(sankey$Type), as.character(sankey$Ardt)) %>% unique()) ``` ![](https://i.imgur.com/U8URIRr.png) Il faut ensuite créer les **liens** (*links*) entre tous les noeuds ``` sankey$IDsource <- match(sankey$Type, nodes$name)-1 sankey$IDtarget <- match(sankey$Ardt, nodes$name)-1 ``` ![](https://i.imgur.com/jznpyPO.png) Enfin on peux produire le diagramme de Sankey ``` sankeyNetwork(Links = sankey, Nodes = nodes, Source = "IDsource", Target = "IDtarget", Value = "nb", NodeID = "name", fontSize = 19, nodeWidth = 55) ``` ![](https://i.imgur.com/TDXHl6B.png) On peux maintenant personnaliser les couleurs en ajoutant une petite fonction ;) ``` nodes$group <- gsub(" ", "-", nodes$name) ``` ``` sankeyNetwork(Links = sankey, Nodes = nodes, Source = "IDsource", Target = "IDtarget", Value = "nb", NodeID = "name", fontSize = 19, nodeWidth = 55, NodeGroup = "group") ``` ![](https://i.imgur.com/KdjMPE0.png) --- ## STREAMGRAPH Un **streamgraph** est un type de graphique en aires empilées qui est déplacé structurée autour d'un axe central, ce qui donne une forme organique fluide. Très pertinent pour représenter l'évolution dans le temps d'une variable quantitive realtives à plusieurs modalités qualitatiaes. https://hrbrmstr.github.io/streamgraph/ ``` install.packages("devtools") library(devtools) ``` Il faut ici installer le package `streamgraph` ``` devtools::install_github("hrbrmstr/streamgraph") ``` --- ### Streamgraph pour représenter l'évolution dans le temps du nombre de types de signalement Etape1. Créer le dataframe adéquat ``` streamgraph <- DMR %>% group_by(Anne = ANNEE.DECLARATION ,Type = TYPE) %>% summarise(nb = n()) ``` Etape2. Produire le streamgraph ``` streamgraph(streamgraph, key="Type", value="nb", date="Anne", height="700px", width="1300px") %>% sg_axis_x("Date") %>% sg_legend(show=TRUE, label="I- Type: ") ``` ![](https://i.imgur.com/wI5HxO9.png) --- ### Streamgraph pour représenter l'évolution dans le temps du nombre de sous-types de signalement Etape1. Créer le dataframe adéquat ``` streamgraph <- DMR %>% filter(! ANNEE.DECLARATION %in% c(2012,2013,2014)) %>% group_by(Anne = ANNEE.DECLARATION ,SousType = SOUSTYPE) %>% summarise(nb = n()) ``` Etape2. Produire le streamgraph ``` streamgraph(streamgraph, key="SousType", value="nb", date="Anne", height="700px", width="1300px") %>% sg_axis_x("Date") %>% sg_legend(show=TRUE, label="I- Type: ") ``` ![](https://i.imgur.com/pE3pUBP.png)