Dans ce nouveau post, nous allons traiter d’une forme de visualisation hybride entre le tableau et le graphe: la heatmap (ou en français carte thermique). Celle-ci permet de présenter en un unique visuel une grande quantité de données et autorise de fait la réalisation de comparaisons multiples. Son point de départ est un tableau à deux dimensions établis pour stocker les valeurs à illustrer. Il s’agit alors de communiquer une information quasi complète sur la répartition de ces valeurs sur les deux dimensions. Cela serait bien sur possible juste en fournissant le tableau. Cependant, la lecture en serait laborieuse eu égard à la taille de ce dernier. Pour simplifier les choses, la heatmap propose d’attribuer des couleurs aux différentes cases du tableau et de faire varier les nuances en fonction des valeurs de la variable représentée. Les contrastes établis permettent ainsi de mettre en évidence des tendances.

Il existe deux grands types de heatmap: la heatmap la classique, où les dimensions du tableau sont n’importe quelles variables discrètes, et la calendar heatmap, où une des variables exprime une notion temporelles permettant la formation d’un calendrier. C’est ce second type qui sera mobilisé pour notre illustration.

Nous allons une nouvelle fois utiliser les données du Hadley Center concernant les relevés de températures au centre de l’Angleterre entre 1772 et 2022. Il s’agit de présenter l’évolution des moyennes mensuelles sur toute la période en utilisant une calendar heatmap.

Commençons par charger les packages qui nous serons utiles par la suite.

library(tidyverse)
library(lubridate)

Puis, chargeons les données.

dat<-read.table("https://hadleyserver.metoffice.gov.uk/hadcet/cetdl1772on.dat")

Mettons les données en forme pour de manière à obtenir notre tableau de base avec une variable année, une variable mois et une variable température moyenne à l’intersection de ces dernières.

options(dplyr.summarise.inform = FALSE)
colnames(dat)<-c('year','day',paste0('month_',1:12))
dat_<-dat %>% pivot_longer(cols=starts_with('month_'),
                           names_to = 'month',
                           names_prefix = 'month_',
                           values_to = 'd_t') %>% 
              mutate(date=as.Date(paste0(day,'/',month,'/',year),
                                  format='%d/%m/%Y')) %>% 
              select(date,d_t) %>% 
              mutate(d_t=ifelse(d_t==-999,NA,d_t),
                     d_t=d_t/10) %>% 
              remove_missing()
dat_1<-dat_ %>% mutate(ms=month(date,label=TRUE),an=year(date)) %>% 
                group_by(an,ms) %>% 
                summarise(temp=mean(d_t)) 

Nous commençons par redéfinir les noms des variables. Puis, revoyons le format de la base pour qu’elle soit établie sur la longueur. Construisons la variable date et assurons nous qu’elle soit bien de type temporelle. Limitons le résultat à un base comprenant les températures moyennes relevées sur chaque jour entre 1772 et 2022. A partir de là, nous définissons les variables mois (ms) et année (an), ainsi que la moyenne des températures journalières à leur intersection (les moyennes mensuelles). On a alors une tableau dont les six premières lignes sont les suivantes:

head(dat_1)
## # A tibble: 6 × 3
## # Groups:   an [1]
##      an ms     temp
##   <dbl> <ord> <dbl>
## 1  1772 janv   1.22
## 2  1772 févr   1.88
## 3  1772 mars   4.39
## 4  1772 avr    6.41
## 5  1772 mai   10.1 
## 6  1772 juin  16.1

C’est ce tableau, dans son ensemble que nous allons transformer en calendar heatmap. Commençons par un graphe de base. Les esthétiques mobilisées sont en abscisses les années, en ordonnées les mois et la couleur de remplissage des cases définie en fonction des températures moyennes. Le geom utilisé est le geom_tile().

ggplot(data=dat_1,
       aes(x=an,y=ms,fill=temp))+
  geom_tile()

Le résultat en l’état n’est pas très convainquant. Améliorons le rendu avec quelques manipulations simples.

ggplot(data=dat_1,
       aes(x=an,y=ms,fill=temp))+
  geom_tile()+
  labs(title="Températures moyennes au centre de l'Angleterre",
       caption="Source: Hadley Center - Parker et al, 1992")+
  theme_minimal()+
  theme(
    plot.title = element_text(face='bold',hjust=0.5),
    plot.caption=element_text(face='italic',hjust=1),
    axis.title = element_blank(),
    panel.grid = element_blank())

Modifions les couleurs permettant de présenter les gradations de températures. Pour cela, utilisons le complément scale_fill_gradient2(). Indiquons que les valeurs les plus bases seront bleues, les valeurs moyennes jaunes et les valeurs les plus hautes rouges. Initialisons cette gradation à partir des minimum, médiane et maximum des températures. Indiquons “°C” comme étiquette pour la légende associée.

ggplot(data=dat_1,
       aes(x=an,y=ms,fill=temp))+
  geom_tile()+
  labs(title="Températures moyennes au centre de l'Angleterre",
       caption="Source: Hadley Center - Parker et al, 1992")+
  scale_fill_gradient2(low="blue3", high="red3", mid="yellow",
                       midpoint=9,limit=c(-4,19.8), space="Lab", 
                       name="°C")+
  theme_minimal()+
  theme(
    plot.title = element_text(face='bold',hjust=0.5),
    plot.caption=element_text(face='italic',hjust=1),
    axis.title = element_blank(),
    panel.grid = element_blank())

Les tuiles de notre heatmap sont un peu petite. Voyons comment les rendre plus distinctes en utilisant coord_fixed() qui instaure un lien proportionnel en entre les longueurs des incréments en abscisses et en ordonnées. Le ratio marquant le lien a une valeur par défaut de 1.

ggplot(data=dat_1,
       aes(x=an,y=ms,fill=temp))+
  geom_tile()+
  labs(title="Températures moyennes au centre de l'Angleterre",
       caption="Source: Hadley Center - Parker et al, 1992")+
  scale_fill_gradient2(low="blue3", high="red3", mid="yellow",
                       midpoint=9,limit=c(-4,19.8), space="Lab", 
                       name="°C")+
  coord_fixed()+
  theme_minimal()+
  theme(
    plot.title = element_text(face='bold',hjust=0.5),
    plot.caption=element_text(face='italic',hjust=1),
    axis.title = element_blank(),
    panel.grid = element_blank())

Le résultat est illisible. Voyons ce que l’on peut obtenir en modifiant le ratio et en déplaçant la légende.

ggplot(data=dat_1,
       aes(x=an,y=ms,fill=temp))+
  geom_tile()+
  labs(title="Températures moyennes au centre de l'Angleterre",
       caption="Source: Hadley Center - Parker et al, 1992")+
  scale_fill_gradient2(low="blue3", high="red3", mid="yellow",
                       midpoint=9,limit=c(-4,19.8), space="Lab", 
                       name="°C")+
  coord_fixed(ratio=2)+
  theme_minimal()+
  theme(
    plot.title = element_text(face='bold',hjust=0.5),
    plot.caption=element_text(face='italic',hjust=1),
    axis.title = element_blank(),
    panel.grid = element_blank(),
    legend.position = 'bottom')

Redimensionnons le graphe de manière à avoir une fenêtre d’expression plus adaptée.

ggplot(data=dat_1,
       aes(x=an,y=ms,fill=temp))+
  geom_tile()+
  labs(title="Températures moyennes au centre de l'Angleterre",
       caption="Source: Hadley Center - Parker et al, 1992")+
  scale_fill_gradient2(low="blue3", high="red3", mid="yellow",
                       midpoint=9,limit=c(-4,19.8), space="Lab", 
                       name="°C")+
  coord_fixed(ratio=2)+
  theme_minimal()+
  theme(
    plot.title = element_text(face='bold',hjust=0.5),
    plot.caption=element_text(face='italic',hjust=1),
    axis.title = element_blank(),
    panel.grid = element_blank(),
    legend.position = 'bottom')

Intégrons des limites entre les tuiles à l’aide de lignes noires. Modifions la taille de la légende et des éléments textuelles (titres et captions). Encadrons notre graphe.

ggplot(data=dat_1,
       aes(x=an,y=ms,fill=temp))+
  geom_tile(colour="black",lwd = 0.01,linetype = 1)+
  labs(title="Températures moyennes au centre de l'Angleterre",
       caption="Source: Hadley Center - Parker et al, 1992")+
  scale_fill_gradient2(low="blue3", high="red3", mid="yellow",
                       midpoint=9,limit=c(-4,19.8), space="Lab", 
                       name="°C")+
  coord_fixed(ratio=2)+
  theme_minimal()+
  theme(
    plot.title = element_text(face='bold',hjust=0.5),
    plot.caption=element_text(face='italic',hjust=1),
    plot.background = element_rect(color='black'),
    axis.title = element_blank(),
    panel.grid = element_blank(),
    legend.position = 'bottom',
    legend.title = element_text(vjust=0.85,size=6),
    legend.text = element_text(size=6),
    legend.key.size = unit(0.5, 'cm'), 
    legend.key.height = unit(0.25, 'cm'),
    legend.key.width = unit(1.5, 'cm'))

Les choses seraient plus parlante avec une numérotation des abscisses plus précises. Optons pour une progressions par blocs de 4 ans. Changeons l’angle d’écriture pour que l’ensemble reste lisible.

ggplot(data=dat_1,
       aes(x=an,y=ms,fill=temp))+
  geom_tile(colour="black",lwd = 0.01,linetype = 1)+
  labs(title="Températures moyennes au centre de l'Angleterre",
       caption="Source: Hadley Center - Parker et al, 1992")+
  scale_x_continuous(breaks=seq(1772,2022,4))+
  scale_fill_gradient2(low="blue3", high="red3", mid="yellow",
                       midpoint=9,limit=c(-4,19.8), space="Lab", 
                       name="°C")+
  coord_fixed(ratio=2)+
  theme_minimal()+
  theme(
    plot.title = element_text(face='bold',hjust=0.5),
    plot.caption=element_text(face='italic',hjust=1),
    plot.background = element_rect(color='black'),
    axis.title = element_blank(),
    axis.text.x = element_text(angle=90),
    panel.grid = element_blank(),
    legend.position = 'bottom',
    legend.title = element_text(vjust=0.85,size=6),
    legend.text = element_text(size=6),
    legend.key.size = unit(0.5, 'cm'), 
    legend.key.height = unit(0.25, 'cm'),
    legend.key.width = unit(1.5, 'cm'))

Pour finir, passons l’axes des abscisses au dessus du repaire, renversons l’ordre des ordonnées pour que janvier arrive en premier, ajustons le graphe pour que les étiquettes des ordonnées collent au repaire. N’oublions pas de donner un nom au graphe pour pouvoir le mobiliser par la suite.

ggplot(data=dat_1,
       aes(x=an,y=fct_rev(factor(ms)),fill=temp))+
  geom_tile(colour="black",lwd = 0.01,linetype = 1)+
  labs(title="Températures moyennes au centre de l'Angleterre",
       caption="Source: Hadley Center - Parker et al, 1992")+
  scale_x_continuous(breaks=seq(1772,2022,4),position="top")+
  scale_fill_gradient2(low="blue3", high="red3", mid="yellow",
                       midpoint=9,limit=c(-4,19.8), space="Lab", 
                       name="°C")+
  coord_fixed(ratio=2,xlim=c(1767,2022),expand = FALSE)+
  theme_minimal()+
  theme(
    plot.title = element_text(face='bold',hjust=0.5),
    plot.caption=element_text(face='italic',hjust=1),
    plot.background = element_rect(color='black'),
    axis.text.x= element_text(angle=90,size=6,vjust=0.5),
    axis.text.y= element_text(hjust=1,size=6,margin = margin(0,-0.5,0,0,'cm')),
    axis.title = element_blank(),
    panel.grid = element_blank(),
    legend.position = 'bottom',
    legend.title = element_text(vjust=0.85,size=6),
    legend.text = element_text(size=6),
    legend.key.size = unit(0.5,'cm'), 
    legend.key.height = unit(0.25,'cm'),
    legend.key.width = unit(1.5,'cm'),
    plot.margin = margin(0.01,0.5,0.01,0.5,'cm'))->g
g

Le graphe que nous venons de réaliser présente les années sur l’axe horizontal et les mois sur l’axe vertical. C’est un choix. Nous aurions pu faire l’inverse et obtenir un autre graphe comme celui ci-contre. L’important c’est que la présentation choisie réponde à une logique.

ggplot(data=dat_1,
       aes(x=factor(ms),y=an,fill=temp))+
  geom_tile(colour="black",lwd = 0.01,linetype = 1)+
  labs(title="Températures moyennes au centre de l'Angleterre",
       caption="Source: Hadley Center - Parker et al, 1992")+
  scale_x_discrete(position="top")+
  scale_y_continuous(breaks=seq(1772,2022,4))+
  scale_fill_gradient2(low="blue3", high="red3", mid="yellow",
                       midpoint=9,limit=c(-4,19.8), space="Lab", 
                       name="°C")+
  coord_fixed(ratio=0.15,ylim=c(1767,2022),expand = FALSE)+
  theme_minimal()+
  theme(
    axis.title = element_blank(),
    axis.text.y = element_text(size=6), 
    legend.position = 'top',
    legend.title = element_text(vjust=0.85,size=4),
    legend.text = element_text(size=4),
    legend.key.size = unit(0.5, 'cm'), 
    legend.key.height = unit(0.25, 'cm'),
    legend.key.width = unit(1.5, 'cm'),
    plot.title = element_text(face='bold',hjust=0.5,size=9),
    plot.caption=element_text(face='italic',hjust=1),
    plot.background = element_rect(color='black'),
    plot.margin = margin(0.1, 1, 0.1, 1, "cm"),
    panel.grid = element_blank())->g1
g1

Ce type de graphe, si il est diffusé par internet, gagne à inclure des éléments interactif qui peuvent améliorer la communication du message porté par les données. Cela peut être réalisé à partir du package plotly. Pour illustrer ce propos, utilisons-le sur le premier graphe (g) légèrement transformé afin de simplifier les choses (je suis loin de maîtriser toutes les subtilités de ce package très riche…).

library(plotly)
g_<-g+aes(text=paste0(ms,' ',an,'
',round(temp,digits=2),'°'))+
  theme(plot.title = element_text(color='red'),
        legend.position = 'none' ) 
ggplotly(g_,tooltip = c('text')) %>% 
    layout(margin = list(l = -20, r = -2, b =-2, t = 30,pad=-100),
           dragmode=FALSE) %>% 
  config(displayModeBar=FALSE)

Notez que l’on a ajouté une esthétique (text) reprenant le texte associé à l’étiquette devant s’afficher lorsque la curseur passe sur une tuile. Notons également que beaucoup de notre mise en forme a été perdue. Bref, il y a peut-être plus intéressant pour tirer partie de nos données.

Essayons plutôt de dresser une heatmap des écarts de températures à la moyenne générale de la série ou à la moyenne de chaque mois. Reprenons la base pour générer les variables correspondant.

dat_2<-dat_1 %>% 
  mutate(tm=mean(temp)) %>% 
  group_by(ms) %>% 
  mutate(moy_m=median(temp),dif=temp-moy_m,dif_=temp-tm)

Commençons par les écarts à la moyenne générale.

ggplot(data=dat_2,
       aes(x=an,y=fct_rev(factor(ms)),fill=dif_))+
  geom_tile(colour="black",lwd = 0.01,linetype = 1)+
  labs(title="Températures moyennes au centre de l'Angleterre",
       caption="Source: Hadley Center - Parker et al, 1992")+
  scale_x_continuous(breaks=seq(1772,2022,4),position="top")+
  scale_fill_gradient2(low="blue", high="red", mid="yellow",midpoint=0,
                       limit=c(-11.7131,9.5782),space="Lab",name="Ecart")+
  coord_fixed(ratio=2,xlim=c(1767,2022),expand = FALSE)+
  theme_minimal()+
  theme(
    plot.title = element_text(face='bold',hjust=0.5),
    plot.caption=element_text(face='italic',hjust=1),
    plot.background = element_rect(color='black'),
    axis.text.x= element_text(angle=90,size=6,vjust=0.5),
    axis.text.y= element_text(hjust=1,size=6,margin = margin(0,-0.5,0,0,'cm')),
    axis.title = element_blank(),
    panel.grid = element_blank(),
    legend.position = 'bottom',
    legend.title = element_text(vjust=0.85,size=6),
    legend.text = element_text(size=6),
    legend.key.size = unit(0.5,'cm'), 
    legend.key.height = unit(0.25,'cm'),
    legend.key.width = unit(1.5,'cm'),
    plot.margin = margin(0.01,0.5,0.01,0.5,'cm'))

Sans grande surprise, on a quelque chose de quasi identique au graphe de base. On a la marque forte de température plus élevées les mois d’été et plus faibles les mois d’hiver. On note aussi des hivers moins froid sur la fin de la série mais les choses sont peu claires. Les effets de saisons masquent les tendances ce ne sera pas le cas avec les écarts à la moyenne établi sur les mois.

ggplot(data=dat_2,
       aes(x=an,y=fct_rev(factor(ms)),fill=dif))+
  geom_tile(colour="black",lwd = 0.01,linetype = 1)+
  labs(title="Températures moyennes au centre de l'Angleterre",
       caption="Source: Hadley Center - Parker et al, 1992")+
  scale_x_continuous(breaks=seq(1772,2022,4),position="top")+
  scale_fill_gradient2(low="blue", high="red", mid="yellow",midpoint=0.03028,
                       limit=c(-6.52803,5.411265),space="Lab",name="Ecart")+
  coord_fixed(ratio=2,xlim=c(1767,2022),expand = FALSE)+
  theme_minimal()+
  theme(
    plot.title = element_text(face='bold',hjust=0.5),
    plot.caption=element_text(face='italic',hjust=1),
    plot.background = element_rect(color='black'),
    axis.text.x= element_text(angle=90,size=6,vjust=0.5),
    axis.text.y= element_text(hjust=1,size=6,margin = margin(0,-0.5,0,0,'cm')),
    axis.title = element_blank(),
    panel.grid = element_blank(),
    legend.position = 'bottom',
    legend.title = element_text(vjust=0.85,size=6),
    legend.text = element_text(size=6),
    legend.key.size = unit(0.5,'cm'), 
    legend.key.height = unit(0.25,'cm'),
    legend.key.width = unit(1.5,'cm'),
    plot.margin = margin(0.01,0.5,0.01,0.5,'cm'))

On observe plus nettement le réchauffement sur la fin de la série. Il y a moins de tuiles bleutées et plus d’orangées à partir des années 90.