Statistical annotation overlays pairwise p-value brackets and significance asterisks onto distribution plots. The visual is the same regardless of test; the underlying statistics are not. Choosing the appropriate test (parametric vs non-parametric, paired vs unpaired, nested vs independent) and adjusting for multiple comparisons are mandatory. ggpubr (R) and statannotations (Python) are the modern interfaces.
install.packages(c('ggpubr', 'ggsignif', 'rstatix'))
pip install statannotations seaborn scipy
Tell your AI agent what you want to do: - "Add Wilcoxon p-values between control and treatment to my boxplot" - "Pairwise tests for 4 groups with Holm adjustment" - "Switch from t-test to Mann-Whitney because the data are non-normal" - "Show asterisks instead of numeric p" - "Use paired test because before-after measurements in same subjects"
"Wilcoxon p-value bracket between Control and Treatment on the boxplot. Show numeric p, not asterisks."
"Pairwise Wilcoxon tests across 4 conditions with Holm adjustment. Display as a bracket pyramid above the boxes with asterisks for significance."
"Pre/post measurements paired by subject_id. Use paired Wilcoxon. Indicate paired-test in the figure legend."
"I have ~200 cells per patient × 8 patients. Don't pseudo-replicate; aggregate to per-patient mean or use a linear mixed model."
"Kruskal-Wallis overall test in the top corner; per-pair Dunn post-hoc as brackets between groups."
Default ggpubr test is t-test - not always right. Specify method = 'wilcox.test' for non-normal or small N.
Use Holm (p.adjust.method = 'holm') as the default adjustment. More powerful than Bonferroni, controls FWER.
Paired tests require matched subjects. paired = TRUE (R) or t-test_paired (statannotations). Verify pairing before invoking.
Nested data must NOT be pairwise-tested. Pseudoreplication inflates p by orders of magnitude. Use LMM (lme4::lmer) or aggregate to per-cluster median.
Always report effect size alongside p. Cohen's d for normal; Cliff's delta for non-parametric; median difference with bootstrap CI.
Don't hide n.s. comparisons. Pre-specify the comparisons tested in the caption; show all results including n.s.
For >5 groups, omnibus first. Kruskal-Wallis or ANOVA; pairwise post-hoc only if overall significant.
Asterisks vs numeric: label = 'p.signif' for asterisks (terse); label = 'p.format' for numeric (precise). Provide both via figure + supplementary table.
map_signif_level = TRUE in ggsignif converts p to asterisks following the * < 0.05, ** < 0.01, *** < 0.001 convention.
p < 1e-50 from large N often means small effect with massive N. Effect size is the biological story; p is the statistical question.