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.
Obtenez le livre — R Graphics Essentials (PDF)

Introduction

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 :

  1. Le graphique — un diagramme en barres groupées (juxtaposées) des moyennes + barres d’erreur, dessiné avec ggbarplot() de ggpubr.
  2. 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.

Le graphique canonique : barres groupées + barres d’erreur + p-values ajustées

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 <- ToothGrowth
df$dose <- factor(df$dose)

# one OJ-vs-VC test per dose, then adjust across the three doses
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")

A ggpubr grouped bar plot of mean tooth length by dose, coloured by supplement (OJ vs VC), with mean_se error bars and an adjusted p-value printed above each of the three doses.

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).

library(ggpubr)
df <- ToothGrowth
df$dose <- factor(df$dose)
str(df)
'data.frame':   60 obs. of  3 variables:
 $ len : num  4.2 11.5 7.3 5.8 6.4 10 11.2 11.2 5.2 7 ...
 $ supp: Factor w/ 2 levels "OJ","VC": 2 2 2 2 2 2 2 2 2 2 ...
 $ dose: Factor w/ 3 levels "0.5","1","2": 1 1 1 1 1 1 1 1 1 1 ...

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 <- ToothGrowth
df$dose <- factor(df$dose)

ggbarplot(df, x = "dose", y = "len", add = "mean_se",
          color = "supp", palette = "jco",
          position = position_dodge(0.8))

A ggpubr grouped bar plot of mean tooth length by dose, coloured by supplement OJ versus VC, with mean_se error bars and no p-values yet.

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.

library(ggpubr)
library(rstatix)
df <- ToothGrowth
df$dose <- factor(df$dose)

res.stats <- df |>
  group_by(dose) |>
  t_test(len ~ supp) |>
  adjust_pvalue() |>
  add_significance()
res.stats
# A tibble: 3 × 11
  dose  .y.   group1 group2    n1    n2 statistic    df       p   p.adj
  <fct> <chr> <chr>  <chr>  <int> <int>     <dbl> <dbl>   <dbl>   <dbl>
1 0.5   len   OJ     VC        10    10    3.17    15.0 0.00636 0.0127 
2 1     len   OJ     VC        10    10    4.03    15.4 0.00104 0.00312
3 2     len   OJ     VC        10    10   -0.0461  14.0 0.964   0.964  
# ℹ 1 more variable: p.adj.signif <chr>

Les colonnes que lit l’annotation :

  • 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 <- ToothGrowth
df$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")

A ggpubr grouped bar plot of mean tooth length by dose with mean_se error bars and the adjusted p-value for OJ versus VC printed above each dose.

Vous préférez des étoiles de signification au nombre brut ? Basculez la colonne label vers p.adj.signif :

library(ggpubr)
library(rstatix)
df <- ToothGrowth
df$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")

The same grouped bar plot annotated with significance symbols (stars) for OJ versus VC above each dose instead of the numeric p-value.

É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.

library(ggpubr)
library(rstatix)
df <- ToothGrowth
df$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)) +
  geom_text(aes(x = dose, y = c(17, 27, 31), label = p.adj.signif),
            data = res.stats)

The grouped bar plot of mean tooth length by dose with the adjusted significance symbols placed above each dose using a manual geom_text layer.

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

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).

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().

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.

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.

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

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 <- ToothGrowth df$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")
library(ggpubr)
library(rstatix)
df <- ToothGrowth
df$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

Vous préférez un livre ? R Graphics Essentials est disponible en PDF téléchargeable — toutes les leçons de cette série, hors ligne et à vous.

Obtenir le livre (PDF) →Les leçons restent gratuites en ligne.
Cette page vous a-t-elle été utile ?

Prouvez que vous savez le faire. Maîtrisez toute la série ggpubr — suivez votre parcours, construisez des projets et obtenez un certificat.

Commencer gratuitement →

Passez à Pro — Prova illimité sur vos propres données et un certificat vérifiable qui atteste la compétence.

dès 15 $/mois facturé annuellement

Passer à Pro →

✓ Vous êtes Pro — continuez. The runtime is the judge.

Recevez les nouvelles leçons R & Python par e-mail

Pratique, reproductible, sans spam. Désinscription à tout moment.

Double opt-in. Nous ne partageons jamais votre e-mail.

Partager cette pageXLinkedInRedditHN
Note

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.

Réutilisation

Citation

BibTeX
@online{2026,
  author = {},
  title = {Barres empilées avec barres d’erreur \& p-values en R},
  date = {2026-06-22},
  url = {https://www.datanovia.com/learn/data-visualization/ggpubr/stacked-bar-pvalues},
  langid = {fr}
}
Veuillez citer ce travail comme suit :
“Barres empilées avec barres d’erreur & p-values en R.” 2026. June 22. https://www.datanovia.com/learn/data-visualization/ggpubr/stacked-bar-pvalues.