Performs one or multiple mean comparisons.
Usage
compare_means(
formula,
data,
method = "wilcox.test",
paired = FALSE,
id = NULL,
group.by = NULL,
ref.group = NULL,
symnum.args = list(),
p.adjust.method = "holm",
p.format.style = "default",
p.digits = NULL,
p.leading.zero = NULL,
p.min.threshold = NULL,
p.decimal.mark = NULL,
signif.cutoffs = NULL,
signif.symbols = NULL,
ns.symbol = "ns",
use.four.stars = FALSE,
...
)Arguments
- formula
a formula of the form
x ~ groupwherexis a numeric variable giving the data values andgroupis a factor with one or multiple levels giving the corresponding groups. For example,formula = TP53 ~ cancer_group.It's also possible to perform the test for multiple response variables at the same time. For example,
formula = c(TP53, PTEN) ~ cancer_group.- data
a data.frame containing the variables in the formula.
- method
the type of test. Default is wilcox.test. Allowed values include:
t.test(parametric) andwilcox.test(non-parametric). Perform comparison between two groups of samples. If the grouping variable contains more than two levels, then a pairwise comparison is performed.anova(parametric) andkruskal.test(non-parametric). Perform one-way ANOVA test comparing multiple groups.
- paired
a logical indicating whether you want a paired test. Used only in
t.testand in wilcox.test.- id
optional character string naming a column that identifies matched subjects for a paired comparison (
method = "t.test"or"wilcox.test"). By default (id = NULL) a paired test pairs observations by row order, so a p-value can be wrong if the data are not sorted so that the compared groups align. Providingidpairs the observations by subject id instead (row-order independent), using only the complete pairs (per-comparison pairwise deletion, via rstatix). It works for a two-group, a pairwise (more than two groups) and aref.groupcomparison; it is an error to combineidwithanova/kruskal.testor withref.group = ".all.".- group.by
a character vector containing the name of grouping variables.
- ref.group
a character string specifying the reference group. If specified, for a given grouping variable, each of the group levels will be compared to the reference group (i.e. control group).
ref.groupcan be also".all.". In this case, each of the grouping variable levels is compared to all (i.e. basemean).- symnum.args
a list of arguments to pass to the function
symnumfor symbolic number coding of p-values. For example,symnum.args <- list(cutpoints = c(0, 0.0001, 0.001, 0.01, 0.05, Inf), symbols = c("****", "***", "**", "*", "ns")).In other words, we use the following convention for symbols indicating statistical significance:
ns: p > 0.05*: p <= 0.05**: p <= 0.01***: p <= 0.001****: p <= 0.0001
Note: If
signif.cutoffsis provided, it takes precedence oversymnum.args.- p.adjust.method
method for adjusting p values (see
p.adjust). Has impact only in a situation, where multiple pairwise tests are performed; or when there are multiple grouping variables. Allowed values include "holm", "hochberg", "hommel", "bonferroni", "BH", "BY", "fdr", "none". If you don't want to adjust the p value (not recommended), use p.adjust.method = "none".Note that, when the
formulacontains multiple variables, the p-value adjustment is done independently for each variable.- p.format.style
character string specifying the p-value formatting style. One of:
"default"(backward compatible, uses scientific notation),"apa"(APA style, no leading zero),"nejm"(NEJM style),"lancet"(Lancet style),"ama"(AMA style),"graphpad"(GraphPad style), or"scientific"(scientific notation for GWAS). Seelist_p_format_stylesfor details.- p.digits
integer specifying the number of decimal places for p-values. If provided, overrides the style default.
- p.leading.zero
logical indicating whether to include leading zero before decimal point (e.g., "0.05" vs ".05"). If provided, overrides the style default.
- p.min.threshold
numeric specifying the minimum p-value to display exactly. Values below this threshold are shown as "< threshold". If NULL, the selected style's default threshold is used; styles without a threshold show exact values. If provided, overrides the style default.
- p.decimal.mark
character string to use as the decimal mark. If NULL, uses
getOption("OutDec").- signif.cutoffs
numeric vector of p-value cutoffs in descending order for assigning significance symbols. For example,
c(0.10, 0.05, 0.01)means p < 0.10 gets "*", p < 0.05 gets "**", p < 0.01 gets "***". Ifuse.four.stars = TRUE, can include a fourth level (e.g.,c(0.10, 0.05, 0.01, 0.001)for "****" at p < 0.001). Default is NULL, which uses the package defaults (backward compatible).- signif.symbols
character vector of symbols corresponding to
signif.cutoffs. If NULL, auto-generated as "*", "**", "***" (and "****" ifuse.four.stars = TRUE). Must have the same length assignif.cutoffs.- ns.symbol
character string for non-significant results. Default is "ns". Use "" (empty string) to show nothing for non-significant results.
- use.four.stars
logical. If TRUE and
signif.symbolsis NULL, allows four stars (****) for the most significant level whensignif.cutoffshas 4 levels. Default is FALSE.- ...
Other arguments to be passed to the test function.
Value
a data frame with the following columns:
.y.: the y variable used in the test.group1,group2: the compared groups in the pairwise tests. Available only whenmethod = "t.test"ormethod = "wilcox.test".p: the p-value.p.adj: the adjusted p-value. Default forp.adjust.method = "holm".p.format: the formatted p-value.p.format.signif: the formatted p-value with significance symbols.p.signif: the significance level.method: the statistical test used to compare groups.
Significance letters (compact letter display)
To label groups with letters instead of p-values or stars - groups that share a
letter are not significantly different - compute the pairwise comparisons and
derive the letters with rstatix::add_cld() (available without any extra
package, as rstatix is already a dependency), then place them with
geom_text():
library(ggpubr)
library(rstatix)
library(dplyr)
df <- ToothGrowth
df$dose <- factor(df$dose)
# all-pairwise comparisons, then compact letters (columns: group, cld)
cld <- df %>% tukey_hsd(len ~ dose) %>% add_cld()
# one letter per group, placed above each box
ypos <- df %>% group_by(dose) %>%
summarise(y.position = max(len) + 2, .groups = "drop")
cld$y.position <- ypos$y.position[match(cld$group, as.character(ypos$dose))]
ggboxplot(df, "dose", "len") +
geom_text(data = cld, aes(x = group, y = y.position, label = cld))add_cld() expects an all-pairwise result (tukey_hsd(),
dunn_test(), games_howell_test(), pairwise
wilcox_test()/t_test(), or compare_means()); it is not
defined for a single ref.group comparison.
To instead flag each treatment by which of several controls it differs from
(e.g. "a" for a negative control and "b" for a positive control),
run one comparison per control and assemble the letters:
trts <- c("trtA", "trtB", "trtC")
cn <- compare_means(value ~ group, df, ref.group = "neg.ctrl")
cp <- compare_means(value ~ group, df, ref.group = "pos.ctrl")
pv <- function(cc, g) dplyr::filter(cc, group1 == g | group2 == g)$p[1]
lab <- sapply(trts, function(g)
paste0(if (pv(cn, g) < .05) "a" else "",
if (pv(cp, g) < .05) "b" else ""))
# then place `lab` above each treatment with geom_text()Examples
# Load data
# :::::::::::::::::::::::::::::::::::::::
data("ToothGrowth")
df <- ToothGrowth
# One-sample test
# :::::::::::::::::::::::::::::::::::::::::
compare_means(len ~ 1, df, mu = 0)
#> # A tibble: 1 × 9
#> .y. group1 group2 p p.adj p.format p.signif method p.format.signif
#> <chr> <dbl> <chr> <dbl> <dbl> <chr> <chr> <chr> <chr>
#> 1 len 1 null m… 1.66e-11 1.7e-11 1.7e-11 **** Wilco… p = 1.7e-11 **…
# Two-samples unpaired test
# :::::::::::::::::::::::::::::::::::::::::
compare_means(len ~ supp, df)
#> # A tibble: 1 × 9
#> .y. group1 group2 p p.adj p.format p.signif method p.format.signif
#> <chr> <chr> <chr> <dbl> <dbl> <chr> <chr> <chr> <chr>
#> 1 len OJ VC 0.0645 0.064 0.064 ns Wilcoxon p = 0.064 ns
# Two-samples paired test
# :::::::::::::::::::::::::::::::::::::::::
compare_means(len ~ supp, df, paired = TRUE)
#> # A tibble: 1 × 9
#> .y. group1 group2 p p.adj p.format p.signif method p.format.signif
#> <chr> <chr> <chr> <dbl> <dbl> <chr> <chr> <chr> <chr>
#> 1 len OJ VC 0.00431 0.0043 0.0043 ** Wilcoxon p = 0.0043 **
# Paired test pairing by a subject id column (row-order independent)
# :::::::::::::::::::::::::::::::::::::::::
df$id <- rep(1:30, 2) # pairs the two supp levels by subject
compare_means(len ~ supp, df, paired = TRUE, id = "id")
#> # A tibble: 1 × 9
#> .y. group1 group2 p p.adj p.format p.signif method p.format.signif
#> <chr> <chr> <chr> <dbl> <dbl> <chr> <chr> <chr> <chr>
#> 1 len OJ VC 0.00431 0.0043 0.0043 ** Wilcoxon p = 0.0043 **
# Compare supp levels after grouping the data by "dose"
# ::::::::::::::::::::::::::::::::::::::::
compare_means(len ~ supp, df, group.by = "dose")
#> # A tibble: 3 × 10
#> dose .y. group1 group2 p p.adj p.format p.signif method
#> <dbl> <chr> <chr> <chr> <dbl> <dbl> <chr> <chr> <chr>
#> 1 0.5 len OJ VC 0.0232 0.023 0.023 * Wilcoxon
#> 2 1 len OJ VC 0.00403 0.004 0.004 ** Wilcoxon
#> 3 2 len OJ VC 1 1 1 ns Wilcoxon
#> # ℹ 1 more variable: p.format.signif <chr>
# pairwise comparisons
# ::::::::::::::::::::::::::::::::::::::::
# As dose contains more thant two levels ==>
# pairwise test is automatically performed.
compare_means(len ~ dose, df)
#> # A tibble: 3 × 9
#> .y. group1 group2 p p.adj p.format p.signif method p.format.signif
#> <chr> <chr> <chr> <dbl> <dbl> <chr> <chr> <chr> <chr>
#> 1 len 0.5 1 7.02e-6 1.4e-5 7.0e-06 **** Wilco… p = 7.0e-06 **…
#> 2 len 0.5 2 8.41e-8 2.5e-7 8.4e-08 **** Wilco… p = 8.4e-08 **…
#> 3 len 1 2 1.77e-4 1.8e-4 0.00018 *** Wilco… p = 0.00018 ***
# Comparison against reference group
# ::::::::::::::::::::::::::::::::::::::::
compare_means(len ~ dose, df, ref.group = "0.5")
#> # A tibble: 2 × 9
#> .y. group1 group2 p p.adj p.format p.signif method p.format.signif
#> <chr> <chr> <chr> <dbl> <dbl> <chr> <chr> <chr> <chr>
#> 1 len 0.5 1 7.02e-6 7 e-6 7.0e-06 **** Wilco… p = 7.0e-06 **…
#> 2 len 0.5 2 8.41e-8 1.7e-7 8.4e-08 **** Wilco… p = 8.4e-08 **…
# Comparison against all
# ::::::::::::::::::::::::::::::::::::::::
compare_means(len ~ dose, df, ref.group = ".all.")
#> # A tibble: 3 × 9
#> .y. group1 group2 p p.adj p.format p.signif method p.format.signif
#> <chr> <chr> <chr> <dbl> <dbl> <chr> <chr> <chr> <chr>
#> 1 len .all. 0.5 0.0000508 0.00015 5.1e-05 **** Wilco… p = 5.1e-05 **…
#> 2 len .all. 1 0.764 0.76 0.76404 ns Wilco… p = 0.76404 ns
#> 3 len .all. 2 0.000179 0.00036 0.00018 *** Wilco… p = 0.00018 ***
# Anova and kruskal.test
# ::::::::::::::::::::::::::::::::::::::::
compare_means(len ~ dose, df, method = "anova")
#> # A tibble: 1 × 7
#> .y. p p.adj p.format p.signif method p.format.signif
#> <chr> <dbl> <dbl> <chr> <chr> <chr> <chr>
#> 1 len 9.53e-16 9.5e-16 9.5e-16 **** Anova p = 9.5e-16 ****
compare_means(len ~ dose, df, method = "kruskal.test")
#> # A tibble: 1 × 7
#> .y. p p.adj p.format p.signif method p.format.signif
#> <chr> <dbl> <dbl> <chr> <chr> <chr> <chr>
#> 1 len 0.00000000148 0.0000000015 1.5e-09 **** Kruskal-Wa… p = 1.5e-09 **…