Un raincloud plot, c’est un graphique qui réunit 3 visualisations : une courbe de densité , un boxplot et les données brutes (sous la forme de dotplot ou de points écartés (jittered points))
Pour 3 raisons (au moins) :
Parce que les densités seules ne fournissent pas de paramètres résumés, comme les quartiles 1 et 3, et la médiane (qui est aussi le quartile 2 )
Parce que les boxplots seuls fournissent peu d’informations sur la distribution (on peut avoir deux boxplots identiques pour des distributions bien différentes – voir ci-dessous ) :
Parce que les boxplots et les densités ne renseignent pas sur le nombre de données
Parce que lorsque les données brutes sont représentées avec un dotplot, cela ressemble à de la pluie qui tombe qui tombe d’un nuage (représenté par la courbe de densité)
La réalisation du raincloud peut être divisé en 4 parties
La courbe de densité est réalisé à l’aide de la fonction stat_halfeye()
du package ggdist
, en fixant certains arguments (voir la description dans le code).
Les valeurs de ces arguments sont celles employées par Cédric Scherer (grand spécialiste de la data visualisation).
Pour voir leur impact, vous pouvez les modifier tour à tour.
library(palmerpenguins)
data(penguins)
library(ggplot2)
library(ggdist)
library(tidyverse)
# partie courbe de densité
g1 <- penguins %>%
ggplot(aes(x=species, y=flipper_length_mm, fill=species))+
stat_halfeye(adjust = 0.5, # permet de régler le lissage
width=0.5, # permet de gérer la hauteur des courves
justification = -0.2, # permet de déplacer les courbes sur la droite
.width = 0, point_colour = NA) # permet de supprimer l'affichage d'un intervalle présentt par défaut
plot(g1) # affichage le plot
La seconde étape consiste à ajouter des boxplots, à l’aide de la fonction geom_boxplot()
du package ggplot2 :
# ajout des boxplots
g2 <- g1 +
geom_boxplot(
width = 0.12, # gère la largeur des boites
outlier.color ="purple", # passe les outliers en orange
alpha = 0.5) # ajoute une transparence dans les boites (elles sont plus claires que l'aire des courbes de densités)
plot(g2) # affichage du plot
La troisième étape consiste à ajouter les données brutes. Pour cela, on peut utiliser une visualisation en dotplot (avec la fonction stat_dots()
du pacakge ggdist
), ou une visualisation avec des points décalés (jittered) (avec la fonction geom_point()
du package ggplot2
en précisant une position de type “jittered”) :
g3 <- g2+
stat_dots(
dotsize=0.5, # diminue la taille des points par 2
side = "left", # permet de placer les points du coté opposé à la courbe de densité
justification = 1.1, # permet d'éloigner les points du boxplot
binwidth = 1 # permet de regrouper les points (ici par pas de 1 mm de flipper length, la taille des points s'adapte)
)
plot(g3)
La même chose avec des points décalés, en employant la fonction geom_point()
du package ggplot2
:
g3bis <- g2+
geom_point(aes(colour=species), # permet d'ajouter une couleur sur le spoints
size = 1.3, # gère la taille des points
alpha = .3, #ajoute de la transparence sur les points
position = position_jitter( # permet d'obtenir des points décalés
seed = 1, # permet de toujours obtenir la même représnetation aléatoire des points
width = .09 # permet de gérer la largeur du décalage
))
plot(g3bis)
Pour finir, nous pouvons embellir cette visualisation, en :
coord_flip()
,ggthemes
)library(ggthemes)
g4 <- g3 +
ggtitle("Distribution de la longueur de la nageoire\n par espèce (Raincloud Plot)")+
coord_flip() + # permet de faire une rotation du plot
theme_solarized()+
scale_colour_solarized('blue')+
theme(legend.position = "none") # permet de supprimer la légende
plot(g4)
Vous la voyez le nuage de pluie 🌧️là , non ?
Vous pouvez changer de thème en remplaçant les lignes
theme_solarized()+
scale_colour_solarized('blue'
par les lignes suivantes :
scale_fill_fivethirtyeight() +
theme_fivethirtyeight() +
# ou
theme_economist() +
scale_colour_economist()+
# ou (il y en a d'autres)
theme_gdocs() +
scale_colour_gdocs()
Et si vous avez besoin de modifier l’ordre des modalités de votre variable catégorielle, 👉 consultez l’article Comment modifier l’ordre d’affichage dans un plot ?
Voici un petit hack pour réaliser un raincloud ultra facilement : utilisez la fonction ggbetweenstats()
du package ggstatsplot
.
Par défaut, la fonction ggbetweenstats()
renvoie de nombreuses informations sur le plot, mais il est très facile de les supprimer.
library(ggstatsplot)
ggbetweenstats(
data = penguins,
x = species,
y = flipper_length_mm
)
Pour supprimer, les informations de comparaisons et de taille d’effet, on utilise les arguments results.subtitle = FALSE
et pairwise.comparisons = FALSE
.
ggbetweenstats(
data = penguins,
x = species,
y = flipper_length_mm,
results.subtitle=FALSE,
pairwise.comparisons = FALSE
)
Ce n’est pas que je sois complètement adepte du moindre effort, mais il faut bien avouer que la solution ggbetweenstats()
est diablement 👿 efficace !
Et vous, vous êtes plutôt perfectionniste ou plutôt les pieds sur le bureau….?
👉 Dites le moi en commentaire !!
Si vous voulez approfondir vos connaissances, sur les raincloud, je vous conseille ces 3 ressources :
C’est possible en faisant un don sur la page Tipeee du blog
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.
20 Responses
Merci beaucoup pour ce post toujours très intéressant, comme d’habitude ! Je me demande du coup quelle est la différence entre le raincloud plot (version rapide en particulier) et le violin plot auquel on ajoute le boxplot + les données ?
Merci pour votre éclairage.
Sophie
Bonjour Sophie,
Ouh la question piège…..je tente quand même une réponse. Pour moi la différence est surtout esthétique : dans la version de ggbetweenstats, les 3 graphes sont superposés (les uns sur les autres) alors que dans la version manuelle, ils sont juxtaposés (les un à côté des autres); ce qui rend les données plus lisibles, et le graphique plus élégant.
Le violin plot c’est une courbe de densité en miroir. Lorsqu’on utilise ggplot, on peut faire différents réglages (Cedric Scherer en parle dans son article : https://www.cedricscherer.com/2021/06/06/visualizing-distributions-with-raincloud-plots-and-how-to-create-them-with-ggplot2/). Je ne sais pas quel est le réglage par défaut de celui réalisé par ggbetweenstats.
La version manuelle, permet probablement des réglages plus fins, qui peuvent surement s’avérer très utiles pour mieux représenter les données, dans certaines situations.
J’espère que cela vous aide. Et vous qu’en pensez-vous ?
cette représentation graphique semble en effet très intéressante… mais en utilisant vos ligne de code je ne fait pas apparaître les graphiques que vous présentez…
Bonjour,
je viens d’essayer, cela semble bien fonctionner.
Pouvez-vous, s’il vous plait, êtes plus précis, et me dire quel graphique ne correspond pas ?
Merci.
Bonjour
La version hack de ggbetweenstats() est moins esthétique que la version “manuelle” et la distribution de densité semble remplacée par une représentation en violon.
Y aurait il un équivalent de esquisse/esquisseR avec quelques fonctions plus avancées permettant ce très esthétique graphe en nuage pluvieux.
Merci de toutes vos informations
Lionel
Bonjour Lionel,
je suis bien d’accord avec vous ggbetweenstats() est moins élégante, mais beaucoup plus rapide. Je suis convaincue qu’elle peut être suffisante dans de nombreuses situations.
Je viens de regarder sous esquisse, à priori, on ne peut pas combiner plusieurs graphiques, j’arrive seulement à ajouter les points jittered aux boxplots….
Bonne continuation
Bonjour
Je vous remercie infiniment Professeur
Merci beaucoup pour cette ressource,
Moi j’ai trouvé des graphique sans couleurs et à la fin au lieu d’avoir des graphique en position horizontale, les graphiques sont restés vertical
Bonjour,
je pense qu’il manque au moins un petit + en fin d’une ligne…
Bonne continuation
Bonjour Claire,
Avant tout merci pour cet article qui m’est très utile et qui m’a permis de découvrir ce genre de représentations graphique.
J’ai une question concernant l’espace que l’on peut appliquer sur l’axe x entre les catégories. En effet dans mon cas j’ai 10 catégories à représenter sur celui ci et le problème est que les points se chevauchent d’une catégorie à l’autre (beaucoup de points avec la même valeur). J’ai bien essayer de chercher une solution en changeant la taille des points, en réduisant l’épaisseur des formes et en essayant de personnaliser l’axe mais rien à faire je bloque !
Auriez-vous une idée pour éviter ce chevauchement ? (autre que la représentation des points proposées en g3bis dans votre exemple)
Je vous remercie d’avance pour votre réponse.
Bonjour Térance,
Est-ce que vous avez essayé de jouer sur les arguments binwidth et dotsize de la fonction stat_dots ?
Bonjour Claire,
Merci pour votre réponse.
Oui c’est ce que j’ai fais dans un premier temps mais rien à faire, les points deviennent trop petits pour être discerné et donc le raincloud plot perds de son intérêt. Ce qu’il faudrait c’est vraiment pouvoir indiquer à R d’agrandir l’espacement sur l’axe x entre les différentes catégories. Pensez-vous que ce soit possible avec ggplot2 ?
Je ne sais pas le faire, regardez du côté de la fonction scale_x_discrete().
Sinon, vous pouvez diminuer la hauteur de la courbe avec l’argument width de la fonction stat_halfeye(). Et aussi diminuer la largeur des boites du boxplot avec l’argument width de la couche geom_bxplot().
Tenez-nous informé 🙏si vous trouvez la solution, je suis certaine que cela aidera d’autres lecteurs et lectrice du blog.
Bonjour Claire,
Merci beaucoup pour cet article vraiment très utile pour la visualisation d’une distribution.
Néanmoins, je suis confronté à un problème que je ne parviens pas à traiter : mes données sont trop nombreuses (42 000 données sur ma variable à 3 modalités). Du coup, la création du graphique 3 prend beaucoup de temps et le résultat est inexploitable. Il y a tellement de points qu’ils chevauchent les courbes de densité et recouvrent même toute la surface de l’image …
Existe-t-il une technique pour extraire un échantillon plus petit des données tout en conservant les caractéristiques de la distribution ?
Merci pour votre éclairage
Fabien
Bonjour Fabien,
Pour extraire un échantillon tout en conservant les caractéristiques de la distribution, vous pouvez employer une méthode d’échantillonnage aléatoire.
Par exemple :
# Fixer la graine aléatoire pour assurer la reproductibilité
set.seed(123)
# Extraire un échantillon aléatoire de taille 100
sample_vec <- sample(population_vec, size = 100, replace = FALSE)
La fonction **`sample()`** est utilisée pour extraire un échantillon aléatoire (de taille 100 dabs l'exemmple) à partir du vecteur "population_vec" (vos 42 000 valeurs). L'argument **`size`** est utilisé pour spécifier la taille de l'échantillon et l'argument **`replace`** est réglé sur **`FALSE`** pour garantir que chaque observation de la population ne sera échantillonnée qu'une seule fois. La fonction **`set.seed()`** est utilisée pour fixer la graine aléatoire et assurer que le même échantillon sera extrait à chaque exécution du code.
Je vous conseille de vérifier que l'échantillon est représentatif de la population et qu'il n'y a pas de biais dans le processus d'échantillonnage.
Pour cela vous pouvez calculer les moyennes et sd de l'échantillon et du vecteur source. Vous pouvez aussi réaliser des courbes de densité (de l'échantillon et de la population) pour vérifier qu'elles ont bien les mêmes formes.
J'espère que cela vous aide.
Bonne continuation
Merci Claire,
J’y dû m’y reprendre à plusieurs fois, mais c’est bon, ça marche
En fait, je misais sur un échantillon trop petit et mes moyennes et sd étaient trop éloignés de la distribution d’origine
Au final, je suis monté à un échantillon de 4300 (pour plus de 42000 dans la distribution d’origine) pour retrouver les bonnes caractéristiques
Je me demande si ce ne sont pas les données manquantes qui jouaient trop d’influence dans les petits échantillons
Un grand merci à vous pour cette fonction que je ne connaissais pas, je l’ai notée dans mon cahier à spirales 😉
Vous êtes vraiment trop forte !
Encore un grand merci
Fabien
Super article ! (as usual).
Une remarque, si comme moi vous utilisez des labels pour les axes x et y, il faut penser à rajouter
‘+ theme(axis.title = element_text())’ lorsque l’on utilise certains thèmes qui par défaut ne les affiche pas.
Exemple :
scale_fill_fivethirtyeight() +
theme_fivethirtyeight()
Bonjour Vincent,
Merci pour la précision !
Une seconde remarque, si on souhaite ajoute la pvalue avec stat_pvalue_manual(), il faut spécifier l’argument aes(fill = ma_variable) pour chaque construction, et non au départ via ggplot().
(explication ici : https://github.com/kassambara/ggpubr/issues/266 )
#good
stat.test % wilcox_test(V2 ~ V1)
dat%>%
ggplot(aes(x= V1, y=V2))+
stat_halfeye(aes(fill = V1)) +
geom_boxplot(aes(fill = V1)) +
stat_dots(aes(fill = V1)) +
stat_pvalue_manual(stat.test)
#not good
stat.test % wilcox_test(V2 ~ V1)
dat%>%
ggplot(aes(x= V1, y=V2, fill = V1))+
stat_halfeye() +
geom_boxplot() +
stat_dots() +
stat_pvalue_manual(stat.test)
👍 Merci Vincent !