Comment faire de beaux graphiques avec R et ggplot2

Mes premiers graphiques en R étaient horribles…

Je pensais que si j’avais la data quelque part dans le graphe, ça suffisait.

Sauf que..

Quand je visitais les internets et que je voyais des graphes magnifiques, je comprenais pas trop.

Comment ils font pour faire des graphiques aussi beau ??

FiveThirtyEight Graph
Source : FiveThirtyEight.com

En fait, ils trichent.

Ils ne font pas leurs graphiques avec R.

D’ailleurs, R n’a pas été pensé à l’origine pour faire des graphes jolis prêts à être publiés.

Mais ça, c’était avant.

Aujourd’hui, on peut faire de superbes graphiques avec R !

Et en plus.. c’est pas si dur.

En fait, la BBC utilise R pour créer ses propres graphiques directement publiables dans leurs articles !

C’est pourquoi dans cet article, je vais vous montrer :

  • De quoi R est capable
  • Comment la BBC a créé son propre package pour faire des graphiques en R
  • Un exemple avec ggplot2 pour que vous puissiez en faire autant

À vous les infographies en R !!!

De quoi R est capable ?

Pour commencer, on va se prendre un jeu de données.

J’utilise le jeu de données PokemonGO, créé par Alberto Barradas et partagé sur Kaggle : https://www.kaggle.com/abcsds/pokemongo

library(data.table)
pokemon <- fread("pokemonGO.csv")
head(pokemon)
#    Pokemon No.       Name Type 1 Type 2 Max CP Max HP                                                                              Image URL
# 1:           1  Bulbasaur  Grass Poison   1079     83   http://cdn.bulbagarden.net/upload/thumb/2/21/001Bulbasaur.png/250px-001Bulbasaur.png
# 2:           2    Ivysaur  Grass Poison   1643    107       http://cdn.bulbagarden.net/upload/thumb/7/73/002Ivysaur.png/250px-002Ivysaur.png
# 3:           3   Venusaur  Grass Poison   2598    138     http://cdn.bulbagarden.net/upload/thumb/a/ae/003Venusaur.png/250px-003Venusaur.png
# 4:           4 Charmander   Fire           962     73 http://cdn.bulbagarden.net/upload/thumb/7/73/004Charmander.png/250px-004Charmander.png
# 5:           5 Charmeleon   Fire          1568    103 http://cdn.bulbagarden.net/upload/thumb/4/4a/005Charmeleon.png/250px-005Charmeleon.png
# 6:           6  Charizard   Fire Flying   2620    135   http://cdn.bulbagarden.net/upload/thumb/7/7e/006Charizard.png/250px-006Charizard.png

Et on va tracer la relation entre Max CP et Max HP.

Max CP, c’est le maximum de dégâts que peut infliger un pokemon. Et Max HP, c’est le maximum de dégâts qu’un pokemon peut recevoir.

Traçons un premier graphe avec le R natif :

png(filename = "base_r.png", width = 640, height = 450)
plot(`Max CP` ~ `Max HP`, data = pokemon)
dev.off()

Base R

Euh..

Pas terrible, hein.

Alors, certes, on remarque la relation entre nos deux variables.

Mais de là à dire que c’est publiable ? Peut-être pas.

C’est pour ça que Hadley Wickham et ses collaborateurs ont créé ggplot2.

D’après le site de ggplot2, c’est un package pour créer d’élégantes visualisations de données à l’aide d’une grammaire de graphiques.

Essayons.

library(ggplot2)
ggplot(pokemon, aes(x = `Max HP`, y = `Max CP`)) +
    geom_point()
ggsave(filename = "base_ggplot.png", width = 640/72, height = 450/72, dpi = 72)

Base ggplot2

Ok, pas mal !

Déjà, au lieu d’avoir des cercles vides pour représenter des points, on a des points pour de vrai.

Et le graphe est beaucoup plus clean !

Par exemple, on a pas besoin de tourner notre tête pour lire les libellés sur l’axe des ordonnées.

Et puis les polices sont plus jolies.

Mais bon.

C’est pas non plus fou fou. On peut sans doute faire mieux !

Le package bbplot

Ce qui est chouette avec ggplot2, c’est qu’on a le contrôle sur TOUT.

Et c’est facile à customiser.

On peut ajouter nos propres couleurs, polices, logo, etc.

Et au lieu de tout réécrire à chaque fois, on peut l’intégrer à un package.

Exactement comme la BBC l’a fait (source).

Plutôt que de créer des graphes un peu moches, ils ont créé leur propre package bbplot pour faire des beaux graphiques avec LEUR style.

Regardez plutôt :

graphiques BBC
Source: bbc.github.io/rcookbook

Par mal, hein ?

Essayons le package

Il suffit d’ajouter la fonction bbc_style() :

library(bbplot)
p <- ggplot(pokemon, aes(x = `Max HP`, y = `Max CP`)) +
    geom_point() +
    bbc_style()
finalise_plot(p, source_name = "Source: Kaggle", 
              save_filepath = "base_bbplot.png",
              width_pixels = 640, height_pixels = 450,
              logo_image_path = "pokemongo_logo.png")

Base bbplot

Hum..

C’est un peu mieux.

Notamment :

  • Les tailles de polices sont vraiment bonnes par rapport aux graphiques précédents.
  • Le design est vraiment clean. On a notamment un très bon rapport data/encre (plus d’infos ici)
  • C’est très facile d’ajouter un logo (j’ai mis celui de Pokemon GO)

Mais c’est pas non plus révolutionnaire.

  • Les libellés sur les axes ont disparu. On ne sait même plus ce qu’on trace !
  • Pourquoi ce n’est pas aussi joli que les graphiques qu’ils montrent ?
  • La fonction est un peu cassée. On peut choisir la résolution, j’ai mis 640x450, mais le graphique de sortie était en 2666x1875. Bizarre.

Bon.

C’est mieux, mais on a quand même encore du travail !

On va devoir :

C’est un peu normal, en fait. Chaque graphique est unique et doit être personnalisé autour de ce qu’on souhaite communiquer.

Mais..

J’en attendais un peu plus quand même, quand j’ai vu les exemples fournis par la BBC !

Du coup…

Que peut-on apprendre de leur package ?

Ça fait toujours un peu peur d’aller voir le code derrière un package.

Mais en fait, le package bbplot est super simple !

Il n’y a que deux fonctions.

La fonction bbc_style qui crée un thème ggplot2 en spécifiant plein de détails, comme les polices, le style de la légende, la grille, etc.

Et la fonction finalise_plot qui ajoute le footer, le logo, et enregistre le graphe.

La 2e fonction n’a besoin d’aucun changement.

Et la 1e est celle qu’on peut changer pour spécifier tous les petits détails pour avoir des graphiques jolis qui représentent le style de votre entreprise.

Et cette 1e fonction est très courte : https://github.com/bbc/bbplot/blob/master/R/bbc_style.R

En fait, on peut voir ce package comme un très bon point de départ.

Quand on regarde ce qu’ils ont mis dans cette fonction, on voit que :

  1. Ils se focalisent sur la création d’un style cohérent avec la police, les tailles de police, et les couleurs basiques.
  2. Ils retirent BEAUCOUP d’élements.

Le 2e point est particulièrement important. Rappelez-vous qu’on se plaignait de ne plus avoir les libellés sur les axes.

Et ce qui est chouette, c’est qu’en utilisant ce package, ça ne coûte en gros qu’une seule ligne de code !

Comment faire un beau graphique avec ggplot2 et bbplot ?

Maintenant je vais vous montrer comment passer d’un graphique déjà pas mal à un graphique vraiment cool, prêt à être publié !

Pour ça, on va avancer étape par étape, et à chaque fois on améliorera notre code de manière itérative.

Itération 0 - Le point de départ

On commence avec le code sur lequel on s’était arrêtés :

library(bbplot)
p <- ggplot(pokemon, aes(x = `Max HP`, y = `Max CP`)) +
    geom_point() +
    bbc_style()
finalise_plot(p, source_name = "Source: Kaggle", 
              save_filepath = "base_bbplot.png",
              width_pixels = 640, height_pixels = 450,
              logo_image_path = "pokemongo_logo.png")

Base bbplot

Je ne vais pas répéter à chaque fois le chargement de la librairie et l’appel à finalise_plot, donc rappelez-vous qu’il faut le faire.

On va plutôt se focaliser sur la grammaire de ggplot.

Itération 1 - Ajouter un titre

La première chose qu’on va faire, c’est ajouter un titre.

Pour ça, on utilise la fonction labs.

Et je vais lui donner la couleur du logo de Pokemon GO :

p <- ggplot(pokemon, aes(x = `Max HP`, y = `Max CP`)) +
    geom_point() +
    # Title
    labs(title = "Relationship between Max CP and Max HP") +
    # Style
    bbc_style() +
    theme(plot.title = element_text(color = "#063376"))

bbplot iteration 1

Itération 2 - Améliorer les libellés des axes

Le package bbplot a fait disparaître les titres axes, pour une certaine raison.

En fait, c’est plus clair d’ajouter l’information directement sur l’échelle des axes que de mettre un titre.

Voyez par vous-même :

p <- ggplot(pokemon, aes(x = `Max HP`, y = `Max CP`)) +
    geom_point() +
    # Title
    labs(title = "Relationship between Max CP and Max HP") +
    # Axis
    scale_x_continuous(labels = function(x) paste0(x, " HP")) +
    scale_y_continuous(labels = function(y) paste0(y, " CP")) +
    # Style
    bbc_style() +
    theme(plot.title = element_text(color = "#063376"))

bbplot iteration 2

À présent, avec le titre ET les unités, c’est clair. On reste minimaliste et on évite la redondance.

Itération 3 - Ajouter une ligne de régression

Notre objectif initial était de voir la relation entre Max CP et Max HP.

Et on voit bien que cette relation existe.

Ça pourrait être chouette de tracer une ligne au milieu de ce nuage de point !

Une ligne de régression.

Pour ça, on peut utiliser geom_smooth avec l’argument method = "lm", et se = FALSE pour retirer les bandes de confiance.

Ah oui, et vous aurez peut-être remarqué qu’il y a un pokemon très très à droite sur le graphe. C’est un point un peu aberrant qui va avoir une grosse influence sur la droite. Du coup, uniquement pour le calcul de la droite, je le retire.

p <- ggplot(pokemon, aes(x = `Max HP`, y = `Max CP`)) +
    geom_smooth(data = pokemon[Name != "Chansey"], method = "lm", 
                se = FALSE, col = "#ee1515") +
    geom_point() +
    # Title
    labs(title = "Relationship between Max CP and Max HP") +
    # Axis
    scale_x_continuous(labels = function(x) paste0(x, " HP")) +
    scale_y_continuous(labels = function(y) paste0(y, " CP")) +
    # Style
    bbc_style() +
    theme(plot.title = element_text(color = "#063376"))

bbplot iteration 3

Itération 4 - Ajouter des couleurs pour chaque groupe

Et si on veut rajouter une dimension pour voir les différents types de pokemon ?

Est-ce que les pokemons Feu sont plus puissants que les pokemons Dragon ?

C’est facile à faire en ajoutant l’argument col dans les aesthetics :

p <- ggplot(pokemon, aes(x = `Max HP`, y = `Max CP`)) +
    geom_smooth(data = pokemon[Name != "Chansey"], method = "lm", 
                se = FALSE, col = "#ee1515") +
    geom_point(aes(col = `Type 1`)) +
    # Title
    labs(title = "Relationship between Max CP and Max HP") +
    # Axis
    scale_x_continuous(labels = function(x) paste0(x, " HP")) +
    scale_y_continuous(labels = function(y) paste0(y, " CP")) +
    # Style
    bbc_style() +
    theme(plot.title = element_text(color = "#063376"))

bbplot iteration 4

Iteration 5 - Améliorer la légende de couleur

Mouais..

Les couleurs sont peu moches. Et difficile à discerner les unes des autres.

Les couleurs de ggplot2 par défaut ne sont pas incroyables.

Personnellement, j’aime bien utiliser celle de Tableau.

Vous pouvez les retrouver ici : Palettes de couleur de Tableau Software.

Dans notre cas, comme on a 15 types, je prends la palette Tableau 20.

Et comme la légende va prendre beaucoup de place, je vais réduire sa taille :

colors <- c("#1F77B4", "#AEC7E8", "#FF7F0E", "#FFBB78", "#2CA02C",
            "#98DF8A", "#D62728", "#FF9896", "#9467BD", "#C5B0D5",
            "#8C564B", "#E377C2", "#7F7F7F", "#BCBD22", "#17BECF")
p <- ggplot(pokemon, aes(x = `Max HP`, y = `Max CP`)) +
    geom_smooth(data = pokemon[Name != "Chansey"], method = "lm", 
                se = FALSE, col = "#ee1515") +
    geom_point(aes(col = `Type 1`)) +
    # Title
    labs(title = "Relationship between Max CP and Max HP") +
    # Axis
    scale_x_continuous(labels = function(x) paste0(x, " HP")) +
    scale_y_continuous(labels = function(y) paste0(y, " CP")) +
    # Legend
    scale_color_manual(values = colors) +
    # Style
    bbc_style() +
    theme(plot.title = element_text(color = "#063376")) +
    theme(legend.text = element_text(size = 14))

bbplot iteration 5

Iteration 6 - Faire les choix difficiles

Les couleurs, c’est mieux.

Mais 15 couleurs, ce sera toujours difficile à discerner.

En fait, c’est super dur de voir 15 catégories à la fois sur un graphe.

Certes on pourrait faire un diagramme en barres, mais là l’objectif principal ça reste de voir la relation entre Max CP et Max HP.

Du coup… compromis.

  1. Soit on ne montre pas les types de pokemon
  2. Soit on réduit le nombre de catégories.

Je vais faire le 2e choix.

Si on fait un tableau de fréquences, on peut voir que pas mal de types sont assez rares :

sort(table(pokemon$`Type 1`))
#    Fairy      Ice   Dragon    Ghost Fighting   Ground  Psychic Electric     Rock      Bug     Fire    Grass   Poison   Normal    Water 
#        2        2        3        3        7        8        8        9        9       12       12       12       14       22       28 

Si je garde seulement les types avec au moins 10 pokemons et que je regroupe le reste dans “Other”, je m’en sors avec seulement 7 catégories

table_pokemon <- table(pokemon$`Type 1`)
pokemon[, type_1 := ifelse(table_pokemon[`Type 1`] >= 10,
                           `Type 1`, "Other")]
pokemon[, type_1 := factor(type_1, c("Bug", "Fire", "Grass", "Normal",
                                     "Poison", "Water", "Other"))]
sort(table(pokemon$type_1))
#    Bug   Fire  Grass Poison Normal  Water  Other 
#     12     12     12     14     22     28     51 

J’ai aussi ré-ordonné les facteurs pour que “Other” se retrouve bien à la fin.

Voyons ce que ça donne :

p <- ggplot(pokemon, aes(x = `Max HP`, y = `Max CP`)) +
    geom_smooth(data = pokemon[Name != "Chansey"], method = "lm", 
                se = FALSE, col = "#ee1515") +
    geom_point(aes(col = type_1)) +
    # Title
    labs(title = "Relationship between Max CP and Max HP") +
    # Axis
    scale_x_continuous(labels = function(x) paste0(x, " HP")) +
    scale_y_continuous(labels = function(y) paste0(y, " CP")) +
    # Legend
    scale_color_manual(values = colors, 
                       guide = guide_legend(nrow = 1)) +
    # Style
    bbc_style() +
    theme(plot.title = element_text(color = "#063376"))

bbplot iteration 6

Pas mal !

On distingue bien les couleurs.

Et je n’ai plus besoin de réduire la taille de la légende.

Alors, quoi d’autre ?

Iteration 7 - Ajouter des annotations

Où est Pikachu ?

Et qui est ce pokemon tout à droite qui semble faible mais avec beaucoup d’HPs ?

Qui sont les pokemons les plus forts ?

On peut ajouter des annotations pour faire transparaître ces informations sur le graphique :

p <- ggplot(pokemon, aes(x = `Max HP`, y = `Max CP`)) +
    geom_smooth(data = pokemon[Name != "Chansey"], method = "lm", 
                se = FALSE, col = "#ee1515") +
    geom_point(aes(col = type_1)) +
    # Arrow for pokemon Chansey
    geom_curve(aes(x = 375, y = 1500, xend = 404, yend = 860),
               colour = "#555555", curvature = -.2, size = .5,
               arrow = arrow(length = unit(0.03, "npc"))) +
    geom_label(aes(x = 330, y = 1400, label = "Chansey"),
               hjust = 0, vjust = 0, colour = "#555555",
               fill = "white", label.size = NA, size = 6) +
    # Arrow for pokemon Pikachu
    geom_curve(aes(x = 50, y = 1400, xend = 65, yend = 880),
               colour = "#555555", curvature = .2, size = .5,
               arrow = arrow(length = unit(0.03, "npc"))) +
    geom_label(aes(x = 50, y = 1460, label = "Pikachu"),
               hjust = .75, vjust = 0, colour = "#555555",
               fill = "white", label.size = NA, size = 6) +
    # Arrow for pokemon Snorlax
    geom_curve(aes(x = 290, y = 3335, xend = 270, yend = 3135),
               colour = "#555555", curvature = -.2, size = .5,
               arrow = arrow(length = unit(0.03, "npc"))) +
    geom_label(aes(x = 290, y = 3255, label = "Snorlax"),
               hjust = .3, vjust = 0, colour = "#555555",
               fill = "white", label.size = NA, size = 6) +
    # Arrow for pokemon Mewtwo
    geom_curve(aes(x = 155, y = 4100, xend = 175, yend = 4150),
               colour = "#555555", curvature = .2, size = .5,
               arrow = arrow(length = unit(0.03, "npc"))) +
    geom_label(aes(x = 155, y = 4100, label = "Mewtwo"),
               hjust = 1, vjust = .3, colour = "#555555",
               fill = "white", label.size = NA, size = 6) +
    # Title
    labs(title = "Relationship between Max CP and Max HP") +
    # Axis
    scale_x_continuous(labels = function(x) paste0(x, " HP")) +
    scale_y_continuous(labels = function(y) paste0(y, " CP")) +
    # Legend
    scale_color_manual(values = colors, 
                       guide = guide_legend(nrow = 1)) +
    # Style
    bbc_style() +
    theme(plot.title = element_text(color = "#063376"))

Et voici une version plus grande du graphique final (cliquez pour zoomer) :

bbplot iteration 7

Pas mal !

Finalement ce n’était pas si dur..

Et remarquez la différence entre le premier graphique et le dernier.

ggplot2 est une librairie de visualisation très puissante pour faire de beaux graphiques.

On peut prendre bbplot comme point de départ, puis itérer jusqu’à obtenir le résultat voulu.

Mis à jour :

Commentaires

Laisser un commentaire

Les champs obligatoires sont marqués *

Chargement...

Les commentaires sont validés manuellement. La page va se rafraîchir après envoi.