The BoxPlot, ViolinPlot, and yPlot modules include a Stats tab that adds pairwise statistical test results as bracket annotations directly on the plotly figure. The same helpers are exported so you can add bracket annotations to any custom plotly figure.
Using the Stats tab
Enable testing from the Stats tab in any supported module app:
The Stats tab exposes these controls:
| Control | Default | Description |
|---|---|---|
| Enable Stats | OFF | Master toggle |
| Test | Wilcoxon |
wilcox.test, t.test,
kruskal.test, or anova
|
| P-value Adjustment | holm | Any p.adjust method |
| Display | Adjusted P-value |
p.adj, p.value, or symbol
(*/**/***/****) |
| Significance Threshold | 0.05 | Boundary for * vs ns
|
| Hide Non-Significant | OFF | Suppress ns brackets |
| Paired Test | OFF | Paired Wilcoxon or paired t-test |
| Comparisons | (all pairs) | Restrict to specific pairs |
| Bracket Style | Capped |
capped (ticked) or flat
|
| Bracket Spacing / Text Offset / Bracket Inset | — | Fine layout control (fractions of y-range) |
| Per Facet Panel | ON | Test independently per facet, or across the full dataset |
Supported tests
- Pairwise: Wilcoxon rank-sum, t-test (paired or unpaired) — draw a bracket per pair.
- Omnibus: Kruskal-Wallis, ANOVA — draw a single draggable text annotation with the overall p-value.
Using the stat helpers in a custom module
The pipeline is fully exported:
-
compute_pairwise_stats()— run the tests, return a data frame. -
create_stat_annotations()— convert results to plotly shapes and annotations. -
apply_stat_annotations()— append them to a plotly figure.
library(VizModules)
library(ggplot2)
library(plotly)
stats_df <- compute_pairwise_stats(
df = example_iris,
x = "Species",
y = "Sepal.Length",
test = "wilcox.test",
p.adjust.method = "holm"
)
# Build the figure with ggplot2 + ggplotly(), matching how the plot modules
# construct their figures. This matters for bracket placement: ggplotly()
# categorical axes are 1-based (the first factor level sits at x = 1), which
# is the convention create_stat_annotations() expects.
p <- ggplot(example_iris, aes(x = Species, y = Sepal.Length)) +
geom_boxplot()
fig <- ggplotly(p)
stat_result <- create_stat_annotations(
stats_df = stats_df,
fig = fig,
df = example_iris,
x = "Species",
y = "Sepal.Length",
display = "symbol"
)
apply_stat_annotations(fig, stat_result)Axis coordinates.
create_stat_annotations()positions brackets using 1-based categorical x-coordinates, matching figures built withggplotly()(as every VizModules plot module does). If you build the figure with a rawplot_ly(type = "box")call instead, plotly uses 0-based category positions and the brackets will appear shifted one category to the right — preferggplotly()for aggplotobject to stay consistent with the modules.
Restricting comparisons
By default all pairwise combinations are tested. Pass a list of
length-2 character vectors to pairs, or convert to/from the
UI’s "A vs B" strings with
generate_pair_strings() /
parse_pair_strings():
choices <- generate_pair_strings(example_iris, x = "Species")
pairs <- parse_pair_strings(choices[1:2])
stats_df <- compute_pairwise_stats(
df = example_iris,
x = "Species",
y = "Sepal.Length",
test = "wilcox.test",
pairs = pairs
)Faceted plots and nested grouping
Pass facet.by with per.facet = TRUE to test
independently within each panel, or group.by to compare
group levels within each x category:
compute_pairwise_stats(
df = example_rnaseq,
x = "condition",
y = "expression",
test = "wilcox.test",
facet.by = "gene",
per.facet = TRUE
)Pass the same facet.by/group.by values to
create_stat_annotations() so brackets land on the correct
subplot axes.