TD5 : Graphiques avec ggplot2

On s’intéresse dans ce TD à certaines représentations graphiques pouvant être réalisées en R.

La plupart de ces graphiques peuvent être réalisés avec les packages de base, mais le package ggplot2, disponible avec la collection tidyverse permet d’élaborer des graphiques visuellement beaucoup plus satisfaisants.

On le charge dans R avec

library(ggplot2)

ou bien

library(tidyverse)
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.1.4     ✔ readr     2.1.5
✔ forcats   1.0.0     ✔ stringr   1.5.1
✔ ggplot2   3.5.1     ✔ tibble    3.2.1
✔ lubridate 1.9.3     ✔ tidyr     1.3.1
✔ purrr     1.0.2     
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors

1 Graphiques univariés

1.1 Pour une variable qualitative

  • On peut représenter une variable qualitative (i.e. un facteur) par un diagramme en barres.

Par exemple, avec le jeu de données mtcars, on peut représenter le nombre de voitures correspondant à chaque cylindrée.

data("mtcars")

ggplot(mtcars)+
  aes(x = factor(cyl))+
  geom_bar()+
  labs(x="Nb cylindres",
       y="Nb voitures",
       title = "Diagramme en barres")

Remarque : Un graphique ggplot commence toujours par la même fonction du même nom, dans laquelle on indique le data-frame contennt les données (obligatoirement un data-frame !) et aes (Aesthetic) qui décrit les données considérées par le graphique. On ajoute ensuite les fonctions correspondnat au graphique souhaité.

  • Ici, geom_bar() est la fonction permettant d’effectuer le diagramme en barres.
  • La fonction labs() permet d’ajouter des titres aux axes et au graphique.

On peut aussi paramétrer le fond du graphique :

ggplot(mtcars)+
  aes(x=factor(cyl))+
  geom_bar()+
  labs(title="Diagramme en barres",
    x="Nb cylindres",
    y="Nb voitures")+
  theme_minimal() # Fond du graphique

  • On peut aussi représenter une variable qualitative par un diagramme circulaire (pie chart).
# On crée un data-frame ou l'on compte le nombre de voitures pour chaque cylindrée
# puis on calcule le pourcentage de ces véhicules
cyl_count <- as.data.frame(table(mtcars$cyl)) %>% 
  rename(Nb_cyl=Var1) %>%
  mutate(Prop=round(Freq/sum(Freq),digits=2)) # arrondi à 0.01%

# On crée le diagramme circulaire
ggplot(cyl_count)+
  aes(x=Prop,y="",fill=Nb_cyl)+ # fill spécifie la couleur de remplissage
  geom_bar(stat="identity")+ 
  coord_polar("x")+ # On spécifie que le diagramme sera circulaire
  geom_text(aes(label=paste(Prop,"%")), # On rajoute les proportions
            position = position_stack(vjust = 0.5))+ # On précise l'emplacement du texte
  labs(title="Proportions du parc automobile en fonction de la cylindrée")+
  theme_void()

On peut aussi modifier le titre de la légende, ou changer les couleurs afin qu’elles soient adaptées aux personnes daltoniennes.

ggplot(cyl_count)+
  aes(x=Prop,y="",fill=Nb_cyl)+ 
  geom_bar(stat="identity")+
  scale_fill_viridis_d()+ # changement de la palette de couleurs
  coord_polar("x")+ 
  geom_text(aes(label=paste(Prop,"%")),colour="grey40",
            position=position_stack(vjust = 0.5))+
  labs(title="Proportions du parc automobile en fonction de la cylindrée")+
  theme_void()+
  guides(fill=guide_legend(title = "Nombre de cylindres")) # modification de la légende

Remarques :

  • De nombreuses possibilités existent pour le choix des couleurs, et le web regorge de ressources pour s’y retrouver.

  • Certains packages comme RColorBrewer ou wesanderson permettent un plus grand choix de couleurs (et plus joli) que celles incluses de base dans ggplot2.

1.2 Pour une variable quantitative

  • On peut représenter les valeurs prises par une variable quantitative sous forme d’histogramme.
ggplot(mtcars)+
  aes(x=wt)+
  geom_histogram(bins = 10, # Nombre de classes
                 alpha=0.8, # Pour gérer l'opacité
                 fill="skyblue", # couleur de remplissage
                 colour="black")+ # couleur du bord
  theme_bw()+
  labs(title = "Distribution du poids des voitures",
       x="Poids (1000 lbs)",
       y="Effectifs")

  • On peut représenter la même variable point par point.
ggplot(mtcars)+
  aes(x="",y=wt)+
  geom_jitter(width = 0.1)+
  theme_minimal()+
  labs(y="Poids (1000 lbs)",
       x="",
       title = "Poids des voitures")

  • On peut enfin représenter cette variable avec un boxplot.
ggplot(mtcars)+
  aes(x="",y=wt)+
  geom_boxplot(width=0.1)+
  theme_minimal()+
  labs(title = "Distribution du poids des voitures",
       x="",
       y="Poids (1000 lbs)")

Remarques : - Les valeurs en-dehors des moustaches sont considéreés comme anormales, ou outliers. Elles sont ii représentées par les points. - On peut superposer les valeurs prises par la variable au boxplot.

ggplot(mtcars)+
  aes(x="",y=wt)+
  geom_boxplot(width=0.1,outliers = FALSE)+ # On n'affiche pas les outliers
  geom_jitter(width = 0.02,color="tomato")+
  theme_minimal()+
  labs(title = "Distribution du poids des voitures",
       x="",
       y="Poids (1000 lbs)")

2 Graphiques bivariés

2.1 Variable quantitative en fonction d’une variable qualitative

Pour les boxplots

On peut représenter la distribution du poids des voitures en fonction de la cylindrée.

ggplot(mtcars)+
  aes(x=as.factor(cyl),y=wt)+
  geom_boxplot(width=0.5)+
  geom_jitter(width=0.1)+
  labs(title = "Disribution du poids des voitures",
       x="Nombre de cylindres",
       y="Poids (1000 lbs)")+
  theme_minimal()

Pour les histogrammes

ggplot(mtcars)+
  aes(x = wt,fill=as.factor(cyl))+
  geom_histogram(bins=10,position = "identity",alpha=0.7,color="black")+
  scale_fill_brewer(palette = "Set2")+
  labs(title = "Distribution du poids des voitures",
       x="Poids",
       y="Effectifs")+
  guides(fill=guide_legend(title = "Nombre de cylindres"))+
  theme_minimal()

Remarque : Il est aussi possible de faire un histogramme pour chaque cylindrée avec la fonction facet_grid().

ggplot(mtcars)+
  aes(x = wt,fill=as.factor(cyl))+
  geom_histogram(bins=10,position = "identity",alpha=0.7,color="black")+
  facet_grid(~cyl)+
  scale_fill_brewer(palette = "Set2")+
  labs(title = "Distribution du poids des voitures",
       x="Poids",
       y="Effectifs")+
  guides(fill=guide_legend(title = "Nombre de cylindres"))+
  theme_minimal()

2.2 Variable quantitative en fonction d’une autre variable quantitative

Il s’agit essentiellement de faire un nuage de points. Par exemple, on peut représenter la consomation des véhicules en fonction de leur poids.

ggplot(mtcars)+
  aes(x=wt,y=mpg)+
  geom_point(color="tomato")+
  theme_bw()+
  labs(title="Consommation des véhicules en fonction de leur poids",
       x="Poids (1000 lbs)",
       y="Consommation (mpg)")

2.3 Variable qualitative en fonction d’une autre variable qualitative

  • On peut à nouveau réaliser un diagramme en barres. Dans l’exemple suivant, on représente le nombre de voitures par cylindrée et par type de transmission.
ggplot(mtcars)+
  aes(x=as.factor(cyl),fill=factor(am,levels = c(0,1),labels=c("Automatique","Manuelle")))+
  geom_bar()+
  scale_fill_brewer(palette = "Set2")+
  labs(title = "Nombre de voitures par clindrée et transmission",
       x="Nombre de cylindres",
       y="Effectifs")+
  theme_minimal()+
  guides(fill=guide_legend(title = "Transmission"))

A partir d’une table de contingence adaptée, on peut rajouter les effectifs sur le diagramme.

mtcars_table <- mtcars %>%
  group_by(cyl,am) %>%
  summarise(Counts=n()) %>%
  ungroup()# On compte le nombre de lignes pour chaque groupement
`summarise()` has grouped output by 'cyl'. You can override using the `.groups`
argument.
ggplot(mtcars_table)+
  aes(x=as.factor(cyl),
      y=Counts,
      fill=factor(am,levels = c(0,1),labels=c("Automatique","Manuelle")))+
  geom_bar(stat = "identity")+
  scale_fill_brewer(palette="Set2")+
  labs(title = "Nombre de voitures par clindrée et transmission",
       x="Nombre de cylindres",
       y="Effectifs")+
  theme_minimal()+
  guides(fill=guide_legend(title = "Transmission"))+
  geom_text(aes(label=Counts),position = position_stack(vjust=0.5))

  • Une autre possibilité est de représentée les barres “côte à côte”.
ggplot(mtcars_table)+
  aes(x=as.factor(cyl),
      y=Counts,
      fill=factor(am,levels = c(0,1),labels=c("Automatique","Manuelle")))+
  geom_bar(stat = "identity",position = position_dodge())+
  scale_fill_brewer(palette="Set2")+
  labs(title = "Nombre de voitures par clindrée et transmission",
       x="Nombre de cylindres",
       y="Effectifs")+
  theme_minimal()+
  guides(fill=guide_legend(title = "Transmission"))+
  geom_text(aes(label=Counts),nudge_y = -1,nudge_x = c(-0.2,0.2))

  • On peut enfin effectuer le même type de diagramme en barres avec les proportions
mtcars_table <- mtcars_table %>%
  group_by(cyl) %>%
  mutate(Freq = round(100*Counts/sum(Counts),digits=2)) %>%
  ungroup()

ggplot(mtcars_table)+
  aes(x=as.factor(cyl),
      y=Freq,
      fill=factor(am,levels=c(0,1),labels=c("Automatique","Manuelle")))+
  geom_bar(stat = "identity")+
  scale_fill_viridis_d()+
  geom_text(aes(label = paste(Freq,"%")),
            position = position_stack(vjust = 0.5),
            colour="grey45")+
  labs(title = "Proportion de véhicules par cylindrée et transmission",
       x="Nombre de cylindres",
       y="Pourcentage")+
  guides(fill=guide_legend(title = "Transmission"))+
  theme_minimal()

3 Exercices

3.1 Exercice 1

  1. Importer le jeu de données temperatures.xlsx disponible sur Connect, et nettoyer le jeu de données du mieux possible.

Ce jeu de données contient des données de températures moyennes pour plusieurs grandes villes européennes.

Remarque : on pourra utiliser la fonction mutate_at() pour modifier des colonnes spécifiques.

Voir la correction
library(readxl)
temp <- read_xlsx("temperatures.xlsx")
temp <- temp %>%
  mutate(Région=as.factor(Région)) %>%
  mutate_at(.vars=2:17,.funs = as.numeric)
  1. Reproduire le graphique suivant :
Voir la correction
ggplot(temp)+
  aes(x = Longitude,y=Latitude)+
  geom_point(pch=19,size=2,color="tomato")+
  geom_text(aes(label = Ville),position = position_nudge(x=0,y=0.7))+
  theme_minimal()+
  labs(title = "Position géographique des villes")

  1. Représenter les températures mensuelles moyennes pour les trois villes : Londres, Rome et Minsk.

Aide :

  • On pourra utliser la fonction geom_line().
  • On modifiera le data-frame initial pour ne conserver que les températures souhaitées, en rajoutant une colonne Nb_mois indiquant le numéro du mois.
  • On pourra utiliser la fonction scale_x_discrete() pour ré-arranger la grille.
  • On pourra s’aider de l’aide trouvée sur le web… comme souvent pour les graphiques sur R !
Voir la correction
evol_temp <- temp %>%
  select(-c(Moyenne,Amplitude,Latitude,Longitude,Région)) %>%
  pivot_longer(cols = -Ville,names_to = "Mois",values_to = "Temperature") %>%
  filter(Ville %in% c("Londres","Rome","Minsk")) %>%
  group_by(Ville) %>%
  mutate(Nb_Mois=1:12) %>%
  ungroup()

ggplot(evol_temp)+
  aes(x=Nb_Mois,y=Temperature,colour = Ville)+
  geom_line(linewidth = 1)+
  geom_point()+
  theme_bw()+
  labs(title="Températures annuelles",
       x="",
       y="Température (°C)")+
  scale_x_discrete(limits=as.factor(1:12),
                   labels=colnames(temp)[2:13])+
  theme(axis.text.x = element_text(angle=45,vjust = 0.6))

3.2 Exercice 2

On travaille ici à nouveau avec le jeu de données bordeaux.csv, disponible sur Connect.

On cherche à étudier l’éventuelle relation entre la qualité du vin et certaines variables météorologiques. Les variables sont les suivantes :

  • Temperature : somme des températures moyennes journalières;
  • Insolation : durée d’insolation;
  • Chaleur : nombre de jours de grande chaleur;
  • Pluie : hauteur des pluies;
  • Y: qualité du vin, 1 (Bon), 2 (Moyen), 3 (Médiocre).
  1. Réaliser les boxplots des variables Temperature et Pluie en fonction de la qualité du vin.

Remarqe : La ligne horizontale représente la moyenne globale de la variable (on utilisera la fonction geom_abline()).

Voir la correction pour la température
bordeaux <- read.csv("bordeaux.csv",row.names = 1)%>%
  select(-Y1) %>%
  mutate(Y = factor(Y,
                    levels = c(1,2,3),
                    labels=c("Bon","Moyen","Médiocre")))

ggplot(bordeaux)+
  aes(x=Y,y=Temperature)+
  geom_boxplot()+
  labs(x="Qualité du vin",
       title = "Distribution des températures en fonction de la qualité du vin",
       y="Température")+
  theme_bw()+
  geom_abline(intercept = mean(bordeaux$Temperature),
              slope=0,
              colour="tomato",linetype = "dashed",linewidth=0.8)

Voir la correction pour la pluviométrie
ggplot(bordeaux)+
  aes(x=Y,y=Pluie)+
  geom_boxplot()+
  labs(x="Qualité du vin",
       title = "Distribution de la pluviométrie en fonction de la qualité du vin",
       y="Pluie")+
  theme_bw()+
  geom_abline(intercept = mean(bordeaux$Pluie),
              slope=0,
              colour="tomato",linetype = "dashed",linewidth=0.8)

Autre possibilité
# Pour mettre sur le même graphique
bordeaux2 <- bordeaux %>%
  select(Temperature,Pluie,Y) %>%
  pivot_longer(cols=c(Temperature,Pluie),
               names_to = "Type",
               values_to = "Value")
ggplot(bordeaux2)+
  aes(y=Value,x=Y)+
  geom_boxplot()+
  labs(x="Qualité du vin",
       title = "Distribution de la température et de la pluviométrie en fonction de la qualité du vin")+
  theme_bw()+
  facet_wrap(~Type)
  1. Faire les 3 histogrammes de la variable Chaleur en fonction de la qualité du vin dans une même fenêtre graphique.
Voir la correction
ggplot(bordeaux)+
  aes(x=Chaleur)+
  geom_histogram(alpha=0.5,bins = 5,colour="grey25",fill="skyblue")+
  facet_wrap(~Y)+
  labs(title = "Nombre de jours de grande chaleur en fonction de la qualité du vin",
       y="Effectifs",
       x="Nombre de jours de grande chaleur")+
  theme_bw()

  1. Créer une variable Pluie_quali correspondant à la variable Pluie et dont les modalités sont :
  • Faibles lorsque la variable Pluie sera inférieure à sa médiane
  • Fortes lorsque la variable Pluie sera supérieure ou égale à sa médiane.

Indice : on pourra utiliser la fonction cut().

Voir la correction
bordeaux <- bordeaux %>%
  mutate(Pluie_quali = cut(Pluie,breaks=c(min(Pluie),
                                          median(Pluie),
                                          max(Pluie)),
                           include.lowest=TRUE,
                           labels=c("Faibles","Fortes")))
  1. Représenter la répartition de la qualité des vins en fonction des deux modalités Faibles et Fortes. On veillera ici à ce que les couleurs soient visibles par les personnes daltoniennes.
Voir la correction
bordeaux4 <- bordeaux %>%
  group_by(Pluie_quali,Y) %>%
  summarise(Counts=n()) %>%
  group_by(Y) %>%
  mutate(Prop = round(100*Counts/sum(Counts),digits = 2)) %>%
  ungroup()
`summarise()` has grouped output by 'Pluie_quali'. You can override using the
`.groups` argument.
Voir la correction
ggplot(bordeaux4)+
  aes(x=Y,y=Prop,fill=Pluie_quali)+
  geom_bar(stat = "identity")+
  scale_fill_viridis_d(option = "C")+
  geom_text(aes(label = paste(Prop,"%")),
            position = position_stack(vjust = 0.5),
            colour="grey40")+
  labs(title = "Répartition de la qualité des vins",
       x="Qualité des vins",
       y="Pourcentage")+
  guides(fill=guide_legend(title = "Pluie"))+
  theme_bw()

  1. Reproduire enfin le graphique suivant.
Voir la correction
bordeaux5 <- bordeaux %>%
  mutate(Year=rownames(bordeaux))

ggplot(bordeaux5)+
  aes(x=Temperature,y=Pluie,colour = Y)+
  geom_point(size=-1)+
  geom_text(aes(label = Year),show.legend =FALSE)+
  theme_bw()+
  labs(title = "Qualité des vins en fonction de la pluviométrie et de la température",
       x="Température",
       y="Pluviométrie")+
  guides(colour=guide_legend(title = "Qualité du vin",override.aes = list(size=5)))

3.3 Exercice 3

On récupère ici les données créees au TD précédent dans le fichier Borneo.Rdata.

load("Borneo.Rdata")
  1. A partir de donnees_richesses, représenter graphiquement densite_arbre en fonction de richesse_specifique. On coloriera selon le type de sol.
Voir la correction
ggplot(donnees_richesse) + aes(x=richesse_specifique,y=densite_arbres,color=Sol)+
  geom_point()+
  theme_bw()+
  labs(x="Richesse spécifique",
       y="Densité arbres",
       title = "Densité d'arbres en fonction de la richesse spécifique")
  1. Dans le jeu de données sol_propre, faire un boxplot de la distribution du total de cations echangeables Exc.Cations en fonction des différents types de sol.
Voir la correction
ggplot(sol_propre)+
  aes(x=Sol,y=Exc.Ca)+
  geom_boxplot()+
  theme_bw()+
  labs(title = "Distribution du total de cations échangeables")
  1. Sur ce même graphique, dans la commande aes(), rajouter la commande fill = Profondeur pour inclure l’information de la profondeur dans la distribution.
Voir la correction
library(RColorBrewer)
ggplot(sol_propre)+
  aes(x=Sol,y=Exc.Ca,fill=Profondeur)+
  geom_boxplot()+
  theme_bw()+
  labs(title = "Distribution du total de cations échangeables")+
  scale_fill_manual(values=brewer.pal(n=3,name="Set2"))
  1. Sur trois graphiques différents (grâce à facet_grid()ou facet_wrap()), représenter les histogrammes de la distribution du phosphore (colonne NO3) selon le type de sol, et colorier selon la profondeur.
Voir la correction
ggplot(sol_propre)+aes(x=NO3)+
  geom_histogram(bins=15,color="white")+
  theme_bw()+
  facet_wrap(~Sol)+
  labs(y="Effectifs",title="Distribution du phosphore selon le type de sol")
  1. Modifier le graphique précédent afin de réaliser des histogrammes différents selon la profondeur.
Voir la correction
ggplot(sol_propre)+aes(x=NO3,fill=Profondeur)+
  geom_histogram(alpha=0.8,position = "identity",bins=15,color="grey20")+
  theme_bw()+
  facet_wrap(~Sol)+
  labs(y="Effectifs",title="Distribution du phosphore selon le type de sol")+
  scale_fill_manual(values=brewer.pal(n=3,name="Set2"))
  1. Heat map Avec les données abondance_genre_metasite on va faire une représentation de la matrice d’abondance. On aura en abscisse les espèces, en ordonnée les sites, et dans les cases une couleur donnant le nombre d’arbres pour chaque espèce. Séquentiellement:
  • Représenter abondance_genre_metasite avec ggplot(abondance_genre_metasite)

  • Ajouter aes(x = Genre, y = MetaSite, fill = NbArbres)

  • Ajouter la commande geom_raster().

Voir la correction
ggplot(abondance_genre_metasite)+aes(x=Genre,y=MetaSite,fill=NbArbres)+
  geom_raster()+
  scale_fill_distiller(palette = "RdYlBu")
  1. Sur le graphique précédent, ajouter (comme dans le section 9.1):
theme(legend.position = "none", # pas de légende
      # Pour le texte en abscisses
      axis.text.x = element_text(angle = 90, # Mise du texte en vertical
                                 size = 6), # Taille police
      # Pour le texte en ordonnées
      axis.text.y = element_text(size = 7)) # Taille police
Voir la correction
ggplot(abondance_genre_metasite)+aes(x=Genre,y=MetaSite,fill=NbArbres)+
  geom_raster()+
  scale_fill_distiller(palette = "RdYlBu")+
  theme(legend.position = "none", # pas de légende
      # Pour le texte en abscisses
      axis.text.x = element_text(angle = 90, # Mise du texte en vertical
                                 size = 6), # Taille police
      # Pour le texte en ordonnées
      axis.text.y = element_text(size = 7)) # Taille police