Pourquoi et comment utiliser une échelle log ?

Problématique

Parfois, lorsque nous devons représenter, sur un même graphique, plusieurs échantillons, des données qui sont dans des échelles assez différentes, nous pouvons être confrontés à l’écrasement de l’échantillon contenant les plus faibles valeurs, comme ci-dessous :

la représentation du premier échantillon est écrasé

Un problème similaire peut se rencontrer lorsqu’il s’agit de représenter une moyenne avec une incertitude (l’intervalle de confiance par exemple)

L'incertitude est écrasée et n'est pas visible

Dans cet article, nous allons voir les solutions simples que nous pouvons employer avec R et le package ggplot 2.

Table des matières

Les data

Le code pour générer les data utilisées dans cet article sont à la fin de celui ci.

 

Transformation log des données

En général, notre premier réflexe est d’employer une transformation log sur les données, comme dans le graph ci-dessous dans lequel j’utilise y=log(response) :

ggplot(mydataL, aes(x=group, y=log(response), colour=group))+
    geom_jitter( width=0.15)+
    geom_boxplot() +
    scale_color_manual(values=c("red", "dodgerblue")) 
Transformation log des données

Les graphs que l’on obtient sont effectivement plus lisibles, mais on perd beaucoup en interprétabilité, puisqu’il faut faire la transformation inverse (exponentielle) pour revenir à l’échelle originale (exp(3)=20.08).

Utiliser une échelle log

Une meilleure solution consiste à utiliser une échelle log. Il existe deux possibilités (au moins) pour le faire avec ggplot2 via les couches coord_trans() et scale_x_continuous().

Fonction coord_trans(y=“log")

La fonction coord_trans(y="log) permet d’obtenir une échelle log sur l’axe des ordonnées (y) et l’argument breaks dans la fonction scale_y_continuous(), permet se spécifier les valeurs (en y) que l’on souhaite afficher.

Voici des exemples avec les deux types de graphs précédents :

# graph boxplot
ggplot(mydataL, aes(x=group, y=response, colour=group))+
    geom_jitter(seed=123, width=0.15)+
    geom_boxplot() +
    scale_color_manual(values=c("red", "dodgerblue"))+
    
    scale_y_continuous(breaks = c(10,20,50,75, 100, 200, 250))+
    coord_trans(y="log")+
    
    theme(legend.position = "none")+
    ggtitle("avec la fonction coord_trans()")


# graph moyenne + ic
ggplot(mydataL2.s, aes(x=group, y=response, colour=group)) + 
    geom_point(size=2)+
    geom_errorbar(aes(ymin=response-ci, ymax=response+ci), width=.01)+
    scale_color_manual(values=c("red", "dodgerblue"))+
    
    scale_y_continuous(breaks = c(10,20,50, 100, 200, 250))+
    coord_trans(y="log")+
    
    theme(legend.position = "none")+
    ggtitle("avec la fonction coord_trans()") 
échelle log par la fonction coord_trans

Les graphiques sont plus lisibles et on a gardé l’interprétatbilité !

 

Fonction scale_y_continuous(trans="log")

La fonction scale_y_continuous(), avec l’argument trans="log" et en spécifiant les valeurs des marques souhaitées avec l’argument breaks, permet également d’obtenir le même résultat. Mais attention, pas toujours ! Je vous explique cela ensuite.

# graph boxplot
ggplot(mydataL, aes(x=group, y=response, colour=group))+
    geom_jitter(seed=123, width=0.15)+
    geom_boxplot() +
    scale_color_manual(values=c("red", "dodgerblue"))+
    
    scale_y_continuous(trans="log", breaks = c(10,20,50, 75,100, 200, 250))

# graph moyenne + ic
ggplot(mydataL2.s, aes(x=group, y=response, colour=group)) + 
    geom_point(size=2)+
    geom_errorbar(aes(ymin=response-ci, ymax=response+ci), width=.01)+
    scale_color_manual(values=c("red", "dodgerblue"))+
    
    scale_y_continuous(trans="log", breaks = c(10,20,50, 100, 200, 250)) 

Précautions

Comme je le disais plus haut, attention, ces deux fonctions ne sont pas redondantes, et elles conduisent dans certaines situations (lorsqu’on demande la réalisation d’un calcul dans ggplot2 avec la fonction stat_summary()), à des graphiques différents :

Voici ce que j’ai compris :

  • scale_x_continuous(trans=”log”) transforme les données en log, et si on utilise une couche stat_summary() pour calculer la moyenne (par ce qu’on veut la représenter), la moyenne va être calculée sur les données loguées, puis elle va être retransformée par la transformation inverse (exponentielle ici ) avant d’être représentée sur le graph.

  • La fonction coord_trans(), quant à elle, ne modifie que l’échelle. Il n’y a pas de transformation des données. Donc, si on utilise un couche stat_summary() pour calculer une moyenne, le calcul sera fait sur les données brutes. Et les résultats sont un peu différents, comme vu pércédemment.

Voici un exemple concret avec un boxplot sur lequel j’ajoute la moyenne :

g1 <- ggplot(mydataL, aes(x=group, y=response, colour=group))+
    geom_jitter(seed=123, width=0.15)+
    geom_boxplot() +
    scale_color_manual(values=c("red", "dodgerblue"))+
    
    stat_summary(fun=mean, geom="point", shape=20, size=5, color="black", fill="black", label=mean)+
    stat_summary(fun.y=mean, colour="red", geom="text", show_guide = FALSE, 
               vjust=-0.7, aes( label=round(..y.., digits=1)))+
   
    scale_y_continuous(trans="log", breaks = c(10,20,50, 75,100, 200, 250))+
    theme(legend.position = "non")+
    ggtitle("avec scale_y_continuous()")



g2 <- ggplot(mydataL, aes(x=group, y=response, colour=group))+
    geom_jitter(seed=123, width=0.15)+
    geom_boxplot() +
    scale_color_manual(values=c("red", "dodgerblue"))+
    
    stat_summary(fun=mean, geom="point", shape=20, size=5, color="black", fill="black", label=mean)+
    stat_summary(fun.y=mean, colour="red", geom="text", show_guide = FALSE, 
               vjust=-0.7, aes( label=round(..y.., digits=1)))+
    
    scale_y_continuous(breaks = c(10,20,50, 75,100, 200, 250))+
    coord_trans(y="log")+
    
    
    theme(legend.position = "non")+
    ggtitle("avec coord_trans()")

grid.arrange(g1, g2, ncol=2) 

Comme on peut le voir, les deux  moyennes ne sont pas identiques.

Voici le détails des calculs pour le groupe 2

Code pour créer les data :

Data frame pour les boxplots

## Pour les boxplots
# génération de 2 échantillon de données
set.seed(123)
y1 <- rnorm(100,m=20,sd=5 )
y2 <- rnorm(100,m=200,sd=50 )

# assemblage dans un data frame
mydata <- data.frame(y1,y2)
mydata <- round(mydata,2)

# passage en format long pour la représentation graphique
library(tidyverse)
mydataL <- mydata %>% 
    pivot_longer(cols=y1:y2,
                 names_to = "group",
                 values_to = "response") %>% 
    arrange(group) 
### Avec la fonction scale_y_numeric(trans="log")

#response du groupe 2
x <- mydataL$response[mydataL$group=="y2"]
# étape 1 : passage en log
logx <- log(x)


# étape 2 : calcul de la moyenne
moy_logx <- mean(logx)
moy_logx
## [1] 5.240923

# etape 3 : transformation inverse
exp_moy_logx <- exp(moy_logx)
exp_moy_logx
## [1] 188.8443


# comparaison avec la moyenne des observations
# utilisée par la fonction coord_trans
mean(x)
## [1] 194.6227 

Data frame pour le plot des moyennes et ic

set.seed(123)
y1 <- rnorm(100,m=20,sd=10 )
y2 <- rnorm(100,m=200,sd=100 )

# assemblage dans un data frame
mydata2 <- data.frame(y1,y2)
mydata2 <- round(mydata,2)
#head(mydata)

# passage en format long pour la représentation graphique
library(tidyverse)
mydataL2 <- mydata2 %>% 
    pivot_longer(cols=y1:y2,
                 names_to = "group",
                 values_to = "response") %>% 
    arrange(group)

# calcul des moyennes et ic
library(Rmisc)
mydataL2.s <- summarySE(mydataL2,  measurevar="response", groupvars=c("group"))

mydataL2.s
##   group   N response        sd        se        ci
## 1    y1 100  20.4522  4.563671 0.4563671 0.9055314
## 2    y2 100 194.6229 48.349671 4.8349671 9.5936236 

Le mot de la fin

J’espère que cet article vous permettra de mieux visualiser vos données et aussi vous évitera quelques déconvenues avec l’emploi de la fonction scale_x_numeric(trans=“log”).

Si vous avez d’autres solutions, dites-le-moi en commentaire 👇👇👇, cela permettra de tous nous faire progresser 

10 réponses

  1. Bonjour,
    Merci pour cet article encore très instructif, je ne connaissais pas la fonction scale_x_continuous(trans=”log”)!
    J’avoue n’utiliser que la fonction scale_y_log10(), vraiment très pratique aussi 😉
    Bonne journée!

  2. Réponse sans faute et approfondie. Bravo et merci.
    L’échelle logarithme ne permet pas de représenter les valeurs nulles (ni négatives). Pour cela, j’utilise une transformation linéaire-logarithme à travers la fonction asinh(x/cofactor). La variable cofactor permet de choisir le point de transition entre la fonction logarithme pour les grandes valeurs et linéaire pour les valeurs se rapprochant de zéro (https://fr.wikipedia.org/wiki/Sinus_hyperbolique_r%C3%A9ciproque). Cette fonction est disponible dans Excel !
    Pour aller plus loin, on peut utiliser la transformation de Cox Box (par exemple https://pbil.univ-lyon1.fr/R/pdf/tdr29.pdf) et optimiser son paramétrage pour ajuster les valeurs à une distribution gaussienne. Je ne l’utilise pas, préférant la simplicité.

    1. Bonjour Samuel,

      Effectivement, je me suis rendue compte, après coup, que je n’avais pas précisé que la transfo log n’était pas appropriée pour les valeurs nulles et négatives.
      Je ne connaissais pas la fonction asinh(x/cofactor), donc un grand merci pour ce complément et ce partage.

  3. Merci pour ce post! Je pense que ceci va certainement éviter des maux de têtes pour comprendre pourquoi on n’a pas la même moyenne!

  4. Bonjour Claire
    Merci à toi pour tout ce que tu fais pour le monde statistique
    ce post est vraiment très très utiles pour corriger les casses têtes lors de l’analyses des données.
    Merci Beaucoup

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Vous souhaitez soutenir mon travail ?

C’est possible en faisant un don  sur la page Tipeee du blog  👇👇👇

Bonjour !

vous venez souvent ?

Identifiez-vous pour avoir accès à toutes les fontionnalités !

Aide mémoire off'R ;)

Enregistrez vous pour recevoir gratuitement mes fiches “aide mémoire” (ou cheat sheets) qui vous permettront de réaliser facilement les principales analyses biostatistiques avec le logiciel R et pour être informés des mises à jour du site.