Barres empilées avec barres d’erreur & p-values en R
Construisez un diagramme en barres groupées des moyennes, puis annotez les p-values ajustées — en direct dans votre navigateur
Apprenez à créer un diagramme en barres empilées/groupées des moyennes de groupes avec des barres d’erreur mean_se en R avec ggpubr, puis à ajouter des p-values comparant les groupes. Calculez les tests par groupe avec rstatix (t_test + adjust_pvalue + add_significance) et placez les p-values ajustées avec stat_pvalue_manual() ou geom_text(). Modifiez et exécutez chaque exemple en direct.
Date de publication
22 juin 2026
Modifié
7 juillet 2026
AstuceÀ retenir
La recette tient en deux étapes : construisez un diagramme en barres groupées des moyennes, puis annotez les p-values que vous avez calculées séparément avec rstatix.
Graphique :ggbarplot(..., add = "mean_se", color = "supp", position = position_dodge()) dessine les moyennes de groupes avec des barres d’erreur, une barre par supplément au sein de chaque dose.
Statistiques : regroupez les données, exécutez un test par groupe, puis ajustez — group_by(dose) |> t_test(len ~ supp) |> adjust_pvalue() |> add_significance().
Annoter :stat_pvalue_manual() (le verbe ggpubr) place les p-values ajustées ; un simple geom_text() fait la même chose avec un contrôle manuel total.
Utilisez p.adj pour le nombre, p.adj.signif pour le symbole de signification (*, **, ns).
Chaque graphique ci-dessous est rendu pour vous — puis ajustez n’importe quel exemple en direct dans le bac à sable.
Vous avez un diagramme en barres des moyennes de groupes avec barres d’erreur — disons la longueur des dents pour deux suppléments à chaque dose — et la question évidente : au sein de chaque dose, les deux suppléments diffèrent-ils significativement ? C’est une recette précise, et elle nécessite deux ingrédients qui vivent à des endroits différents :
Le graphique — un diagramme en barres groupées (juxtaposées) des moyennes + barres d’erreur, dessiné avec ggbarplot() de ggpubr.
Les statistiques — un test par groupe (un test OJ-vs-VC pour chaque dose), calculé avec rstatix et ajusté pour les comparaisons multiples.
Parce que les p-values sont calculées séparément puis dessinées par-dessus, vous faites appel à stat_pvalue_manual() plutôt qu’à stat_compare_means(). Cette séparation est exactement ce que vous voulez lorsque vous avez besoin de p-values ajustées, d’une comparaison par groupe, ou d’un contrôle précis de l’emplacement de chaque étiquette.
Cette leçon parcourt toute la recette sur les données intégrées ToothGrowth : préparer les données, construire les barres de moyennes avec barres d’erreur, exécuter les tests par groupe avec rstatix, puis annoter le résultat de deux façons — le verbe ggpubr (stat_pvalue_manual()) et le verbe ggplot2 (geom_text()).
ggpubr applique automatiquement son thème de publication (theme_pubr()) à l’intérieur de ggbarplot(), si bien que le graphique est prêt à publier dès le départ — vous n’ajoutez pas de thème.
Voici l’image que la plupart des gens recherchent : la longueur moyenne des dents en barres juxtaposées par dose, colorées par supplément, avec des barres d’erreur mean_se, et la p-value ajustée pour OJ vs VC affichée au-dessus de chaque dose. Nous calculons les tests d’abord, puis nous les dessinons. Tout le reste dans la leçon est un fragment de cette unique recette.
library(ggpubr)library(rstatix)df <- ToothGrowthdf$dose <-factor(df$dose)# one OJ-vs-VC test per dose, then adjust across the three dosesres.stats <- df |>group_by(dose) |>t_test(len ~ supp) |>adjust_pvalue() |>add_significance()ggbarplot(df, x ="dose", y ="len", add ="mean_se",color ="supp", palette ="jco",position =position_dodge(0.8)) +stat_pvalue_manual(res.stats, x ="dose",y.position =c(17, 27, 31),label ="p.adj")
Nous allons le construire morceau par morceau, en commençant par le diagramme en barres.
Étape 1 — Préparer les données
ToothGrowth comporte trois colonnes : len (la longueur des dents), supp (le supplément, OJ ou VC) et dose (0,5, 1, 2 mg/jour). La variable de regroupement sur l’axe des x doit être un facteur, donc le premier geste est toujours factor(dose).
C’est toute la préparation dont la recette a besoin : un data frame au format long avec la valeur (len), le groupe x (dose) et le groupe de remplissage (supp).
Étape 2 — Le diagramme en barres des moyennes avec barres d’erreur
Construisez le diagramme en barres des moyennes avec ggbarplot(). L’argument add = "mean_se" est ce qui transforme les valeurs brutes en une barre de la moyenne assortie d’une barre d’erreur (erreur standard) — vous ne pré-résumez pas. Colorez par supp et juxtaposez pour que les deux suppléments soient côte à côte au sein de chaque dose.
library(ggpubr)df <- ToothGrowthdf$dose <-factor(df$dose)ggbarplot(df, x ="dose", y ="len", add ="mean_se",color ="supp", palette ="jco",position =position_dodge(0.8))
Remplacez add = "mean_se" par "mean_sd" pour afficher l’écart-type à la place, ou ajoutez des points en gigue avec add = c("mean_se", "jitter"). Utilisez fill = "supp" au lieu de color = "supp" pour des barres pleines.
Étape 3 — Les statistiques : un test par groupe, ajusté
Place aux chiffres maintenant. Nous voulons une comparaison OJ-vs-VC pour chaque dose, donc nous group_by(dose) et exécutons un t_test(len ~ supp) rstatix — cela renvoie une ligne soignée par dose. Parce que nous avons exécuté trois tests, nous adjust_pvalue() pour la multiplicité, puis add_significance() pour associer chaque p-value ajustée à un symbole d’étoile.
group1 / group2 — les deux suppléments comparés (OJ, VC).
p — la p-value brute · p.adj — ajustée pour les trois tests (par défaut "holm").
p.adj.signif — le symbole de signification (ns, *, **, …) dérivé de p.adj.
Passez method = "bonferroni" à adjust_pvalue() pour une correction différente, ou utilisez wilcox_test() à la place de t_test() pour la comparaison non paramétrique.
Étape 4a — Annoter avec stat_pvalue_manual() (le verbe ggpubr)
stat_pvalue_manual() dessine des p-values que vous avez déjà calculées. Pointez-le vers res.stats, indiquez-lui la variable x (x = "dose"), donnez-lui une y.position pour chaque étiquette, et choisissez la colonne à afficher avec label. Ici, la p-value ajustée au-dessus de chaque dose.
library(ggpubr)library(rstatix)df <- ToothGrowthdf$dose <-factor(df$dose)res.stats <- df |>group_by(dose) |>t_test(len ~ supp) |>adjust_pvalue() |>add_significance()p <-ggbarplot(df, x ="dose", y ="len", add ="mean_se",color ="supp", palette ="jco",position =position_dodge(0.8))p +stat_pvalue_manual(res.stats, x ="dose",y.position =c(17, 27, 31),label ="p.adj")
Vous préférez des étoiles de signification au nombre brut ? Basculez la colonne label vers p.adj.signif :
library(ggpubr)library(rstatix)df <- ToothGrowthdf$dose <-factor(df$dose)res.stats <- df |>group_by(dose) |>t_test(len ~ supp) |>adjust_pvalue() |>add_significance()p <-ggbarplot(df, x ="dose", y ="len", add ="mean_se",color ="supp", palette ="jco",position =position_dodge(0.8))p +stat_pvalue_manual(res.stats, x ="dose",y.position =c(17, 27, 31),label ="p.adj.signif")
Étape 4b — Annoter avec geom_text() (le verbe ggplot2)
Si vous voulez un contrôle manuel total — votre propre police, couleur, ou un y exact pour chaque étiquette — annotez avec un simple geom_text() à partir du même data frame res.stats. Mappez x = dose, fixez vous-même les hauteurs y, et lisez l’étiquette dans la colonne.
Les deux verbes atteignent le même point : stat_pvalue_manual() est le raccourci ggpubr pratique (il comprend les crochets, hide.ns, tip.length) ; geom_text() est l’échappatoire quand vous voulez dessiner l’étiquette n’importe où à la main.
Essayez en direct
Les graphiques ci-dessus ont été rendus au moment de la construction. Envie d’expérimenter ? Modifiez le code et appuyez sur Run — il s’exécute dans votre navigateur via webR (pas de serveur, pas d’installation). Essayez de remplacer t_test par wilcox_test, d’étiqueter avec "p.adj.signif", ou d’ajuster les valeurs de y.position.
Vous travaillez en Python ? Un guide basé sur statannotations arrive dans la série Python.
🟢 Avec un agent IA
Demandez à Prova« mes p-values de stat_pvalue_manual() atterrissent au mauvais endroit sur un diagramme en barres groupées — comment fixer y.position par groupe ? » — elle répond avec du code que vous pouvez exécuter sur votre propre résultat rstatix et vérifie le placement par rapport à vos données, pour que la correction soit reproductible. The runtime is the judge.Ask Prova →
Problèmes courants
Les p-values atterrissent au mauvais endroit ou chevauchent les barres.stat_pvalue_manual() ne se positionne pas automatiquement quand vous passez x = "dose" — donnez-lui une y.position explicite (une valeur par ligne de res.stats, en unités de données), et ajoutez de la marge avec + ylim(NA, 65) si une étiquette est rognée.
stat_pvalue_manual() renvoie une erreur de colonne manquante. L’argument label nomme une colonne qui doit exister dans votre data frame de statistiques — n’utilisez "p.adj" ou "p.adj.signif" qu’après que adjust_pvalue() / add_significance() les ont créées.
J’obtiens une seule p-value pour tout le graphique, et non une par dose. Vous avez sauté le regroupement — group_by(dose) avant t_test() est ce qui produit une comparaison par dose ; sans cela, rstatix regroupe toutes les données en un seul test.
Les barres ne sont pas côte à côte. Ajoutez position = position_dodge(0.8) à ggbarplot() ; sans cela les deux suppléments s’empilent sur le même emplacement x.
dose sur l’axe des x est traitée comme continue. Convertissez-la avec factor(dose) d’abord — un x numérique donne un axe continu dénué de sens et casse les étiquettes par groupe.
Questions fréquentes
NoteComment ajouter un diagramme en barres des moyennes de groupes avec barres d’erreur en R ?
Utilisez ggbarplot() de ggpubr avec add = "mean_se" : ggbarplot(ToothGrowth, x = "dose", y = "len", add = "mean_se") dessine la barre à la moyenne de chaque groupe avec une barre d’erreur (erreur standard) — aucun résumé manuel. Utilisez add = "mean_sd" pour l’écart-type, et color = "supp" + position = position_dodge(0.8) pour des barres groupées (juxtaposées).
NoteComment ajouter des p-values à un diagramme en barres groupées en R ?
Calculez les tests séparément avec rstatix (group_by(dose) |> t_test(len ~ supp) |> adjust_pvalue() |> add_significance()), puis dessinez-les avec stat_pvalue_manual(), en passant x = "dose", une y.position par groupe, et label = "p.adj". Parce que les p-values sont pré-calculées et ajustées, vous utilisez stat_pvalue_manual() plutôt que stat_compare_means().
NoteQuelle est la différence entre stat_pvalue_manual() et stat_compare_means() ?
stat_compare_means() calcule le test et le dessine en une seule couche — pratique pour une comparaison unique. stat_pvalue_manual() dessine des p-values que vous avez calculées ailleurs (avec rstatix), ce qu’il faut pour des p-values ajustées, un test par groupe, ou un placement précis des étiquettes. Cette recette en barres empilées a besoin de la seconde.
NoteComment afficher des étoiles de signification au lieu de la p-value numérique ?
Après add_significance(), votre data frame de statistiques comporte une colonne p.adj.signif (ns, *, **, …). Passez label = "p.adj.signif" à stat_pvalue_manual() (ou à l’aes(label = ...) de geom_text()) pour afficher le symbole au lieu du nombre. Ajoutez hide.ns = TRUE pour supprimer entièrement les étiquettes non significatives.
NotePourquoi mes p-values sont-elles ajustées, et quelle correction est utilisée ?
Exécuter un test par dose signifie des comparaisons multiples, donc adjust_pvalue() corrige le p brut en p.adj pour contrôler le taux de faux positifs. La méthode par défaut est Holm ; passez method = "bonferroni" (ou "fdr") à adjust_pvalue() pour une correction différente. Annotez toujours p.adj (et non le p brut) lorsque vous avez exécuté plusieurs tests.
Testez vos connaissances
ImportantExercice : étiqueter les barres avec des étoiles de signification
Complétez le code pour que les comparaisons par dose soient annotées avec les symboles de signification ajustés (et non la p-value numérique), placés au-dessus de chaque dose.
# add_significance() created a column named "p.adj.signif".# Pass that column name (as a string) to the label argument.
library(ggpubr)library(rstatix)df <- ToothGrowthdf$dose <-factor(df$dose)res.stats <- df |>group_by(dose) |>t_test(len ~ supp) |>adjust_pvalue() |>add_significance()ggbarplot(df, x ="dose", y ="len", add ="mean_se",color ="supp", palette ="jco",position =position_dodge(0.8)) +stat_pvalue_manual(res.stats, x ="dose",y.position =c(17, 27, 31),label ="p.adj.signif")
Vérification rapide : dans cette recette, pourquoi utilisons-nous stat_pvalue_manual() plutôt que stat_compare_means() ?
A. Parce que stat_compare_means() ne peut pas dessiner de diagrammes en barres.
B. Parce que les p-values sont pré-calculées et ajustées avec rstatix, et stat_pvalue_manual() dessine les p-values que vous fournissez. ✅
C. Parce que stat_pvalue_manual() est plus rapide.
Le but de cette recette est la séparation : calculer (et ajuster) les tests avec rstatix, puis dessiner le résultat — c’est exactement à cela que sert stat_pvalue_manual().
Conclusion
Vous avez construit la recette complète : un diagramme en barres groupées des moyennes avec barres d’erreur (ggbarplot(..., add = "mean_se")), un test par groupe calculé et ajusté avec rstatix (group_by() |> t_test() |> adjust_pvalue() |> add_significance()), puis les p-values ajustées dessinées par-dessus avec stat_pvalue_manual() — ou à la main avec geom_text(). L’idée clé est que les statistiques sont calculées séparément du graphique, ce qui vous permet d’afficher des p-values ajustées, une comparaison par groupe.
Pour l’annotation en un seul appel (calculer et dessiner en une seule couche), voir Add p-values to ggplots, qui oppose stat_compare_means() au flux de travail stat_pvalue_manual() à p-values pré-calculées utilisé ici.
Leçons connexes
Means & error bars in R — le graphique barres-de-moyennes + barres d’erreur sur lequel repose cette recette. · Add p-values to ggplots — l’alternative stat_compare_means() en un appel + le contraste avec stat_pvalue_manual() de rstatix. · Bar plots in R — variations de diagrammes en barres (regroupement, juxtaposition, couleurs). · Box & violin plots in R — la même annotation sur une vue de la distribution.
Cette leçon est reproductible : les figures sont exécutées au moment de la construction (si elles s’affichent, le code fonctionne), et le bac à sable et le quiz se réexécutent en direct dans votre navigateur. The runtime is the judge.