Skip to main content
v2026.11,858 entries · CC-BY 4.0

Bonferroni Correction in R: p.adjust() and pairwise.t.test()

How to run a Bonferroni correction in R with p.adjust() and the pairwise test functions, read the adjusted p-values, and when to switch to Holm.

Written and maintained by CASRAI Editorial Board

Last updated

In R, the Bonferroni correction is one argument away: p.adjust(p, method = "bonferroni") multiplies each p-value by the number of comparisons, and the pairwise test functions (pairwise.t.test(), pairwise.wilcox.test()) pass the same p.adjust.method choice straight through. The part most tutorials skip is that R’s own documentation says there is no reason to use the unmodified Bonferroni correction, because Holm’s method is also valid under arbitrary assumptions and dominates it. This guide shows the commands, how to read the output, and when the one-word switch to "holm" is the better call.

What it is used for in research

You run many tests on the same data (all pairs of group means after an ANOVA, many outcomes, many subgroups) and want the chance of at least one false positive across the whole set held at your chosen alpha. That is family-wise error rate (FWER) control. R’s p.adjust documentation groups “bonferroni”, “holm”, “hochberg” and “hommel” as the four methods designed to give strong control of the family-wise error rate; “BH” (alias “fdr”) and “BY” control the false discovery rate instead.

For the concept, the alpha-divided-by-m threshold and a worked comparison with the false discovery rate, see Bonferroni correction and multiple comparisons. For choosing between Bonferroni and Tukey after an ANOVA, see Bonferroni vs Tukey post hoc correction. This page is only the R implementation.

Install or access (official source only)

Everything below uses base R. The stats package that holds p.adjust, pairwise.t.test and pairwise.wilcox.test is attached by default, so no extra package needs installing. Download R from CRAN, the Comprehensive R Archive Network, at cran.r-project.org. R is distributed as free software under the GNU General Public License (R FAQ, cran.r-project.org/doc/FAQ/R-FAQ.html, checked 2026-10-29).

Step-by-step: apply Bonferroni in R

1. Adjust a vector of p-values with p.adjust()

Use this when you already have the raw p-values from separate tests. The documented usage is p.adjust(p, method = p.adjust.methods, n = length(p)).

  1. Put the raw p-values in a vector, in any order.
  2. Call p.adjust with method = "bonferroni".
  3. Compare each adjusted p-value with your alpha (for example 0.05).
# illustrative values, not real data
p <- c(a_vs_b = 0.01, a_vs_c = 0.02, b_vs_c = 0.04)
p.adjust(p, method = "bonferroni")

With three comparisons, each p-value is multiplied by 3, so 0.01 becomes 0.03 and 0.02 becomes 0.06. The returned vector has the same length as p and keeps its names, so the labels you gave the comparisons carry over to the result.

2. Pairwise t-tests after a one-way layout with pairwise.t.test()

The documented usage is pairwise.t.test(x, g, p.adjust.method = p.adjust.methods, pool.sd = !paired, paired = FALSE, alternative = c("two.sided", "less", "greater"), ...), where x is the numeric response and g is the grouping factor. The adjustment method can be abbreviated: the official help-page example writes "bonf".

pairwise.t.test(response, group, p.adjust.method = "bonferroni")

The official examples use the built-in airquality data: attach(airquality); Month <- factor(Month, labels = month.abb[5:9]); pairwise.t.test(Ozone, Month), then the same call with p.adjust.method = "bonf", then pool.sd = FALSE. Run them yourself to see output on a real dataset. If you also need the omnibus ANOVA first, see running ANOVA in R.

Pooled or separate standard deviations

By default (paired = FALSE) pool.sd is TRUE: one common SD is calculated for all groups and used for every comparison, which the documentation notes can be useful when some groups are small. Set pool.sd = FALSE to run ordinary two-sample t-tests per pair. Pooling does not generalize to paired tests, so pool.sd and paired cannot both be TRUE. For repeated measures on the same subjects, use paired = TRUE.

3. Non-parametric pairs with pairwise.wilcox.test()

The documented usage is pairwise.wilcox.test(x, g, p.adjust.method = p.adjust.methods, ...), with the adjustment options documented under p.adjust.

pairwise.wilcox.test(response, group, p.adjust.method = "bonferroni")

For the underlying test see Wilcoxon test in R; for the omnibus test that usually precedes it, see Kruskal-Wallis test in R.

4. Choosing the method string

Valid values come from p.adjust.methods: “holm”, “hochberg”, “hommel”, “bonferroni”, “BH”, “BY”, “fdr”, “none”.

method What R’s documentation says
“bonferroni” P-values are multiplied by the number of comparisons. Strong FWER control.
“holm” Strong FWER control; valid under arbitrary assumptions. The documentation states there seems no reason to use the unmodified Bonferroni correction because Holm’s method dominates it.
“hochberg”, “hommel” Strong FWER control; valid when tests are independent or non-negatively associated. Hommel is more powerful than Hochberg, though the difference is usually small.
“BH” (alias “fdr”), “BY” Control the false discovery rate rather than the family-wise error rate. BH is typically more powerful than the FWER methods; BY is for the most general dependence and is more conservative than BH.
“none” Pass-through, no adjustment.

Practical consequence: if a journal, protocol or reviewer specifically asks for Bonferroni, use "bonferroni"; if you only need FWER control, "holm" is the documented-as-better drop-in and costs you one word.

Output and how to read it

p.adjust returns a numeric vector of corrected p-values, the same length as p, with names copied from p. Compare each adjusted p-value with the usual alpha, for example 0.05. Do not also divide alpha by the number of tests: the adjustment is already inside the number.

pairwise.t.test and pairwise.wilcox.test return an object of class “pairwise.htest” that prints as a triangular matrix. Each cell is the adjusted p-value for the pair named by its row and column. Only the lower triangle of the possible comparisons is calculated, so every pair appears once. A cell below your alpha means that pair differs at the family-wise level you chose.

Troubleshooting

My family is larger than the vector I passed

If some tests were run elsewhere, set n to the full number of comparisons. The documentation requires n to be at least length(p) and warns to set it to a non-default value only when you know what you are doing. When n is larger than length(p), the unobserved p-values are assumed to be greater than all the observed ones for “bonferroni” and “holm”, and equal to 1 for the other methods.

Double correction

If you pass p.adjust.method = "bonferroni" to pairwise.t.test, the matrix is already adjusted. Calling p.adjust on those values again, or comparing them with alpha divided by the number of tests, corrects twice.

Missing values

p may contain NA values; the examples on the p.adjust help page show how they are handled. Check how many comparisons you intend the correction to count before relying on the default n.

Warnings about ties in pairwise.wilcox.test

The official example comment for pairwise.wilcox.test notes that its calls “give warnings because of ties”. Ties are a property of the data, not of the adjustment.

Non-default alternative gives odd results

Because only the lower triangle of comparisons is computed, setting alternative to anything other than “two.sided” requires the levels of the grouping factor to be ordered sensibly.

Free alternatives

Option How it applies the same correction
R p.adjust with “holm” Same function and same call as above, with method = “holm”; strong FWER control and, per the R documentation, dominates Bonferroni.
R p.adjust with “BH” or “fdr” Controls the false discovery rate instead, for screening many outcomes where some false positives are tolerable.
Python statsmodels multipletests(pvals, alpha=0.05, method='hs', ...) The documentation lists “bonferroni” (one-step correction) and “holm” (step-down using Bonferroni adjustments) among its methods (statsmodels.org, checked 2026-10-29). Note the default method there is ‘hs’ (holm-sidak), not Bonferroni.

FAQ

How do I do a Bonferroni correction in R?

Call p.adjust(p, method = "bonferroni") on your vector of raw p-values, or pass p.adjust.method = "bonferroni" to pairwise.t.test() or pairwise.wilcox.test(). Then compare the adjusted p-values with your alpha.

Does p.adjust change alpha or the p-values?

The p-values. Bonferroni multiplies them by the number of comparisons, which gives the same decision as dividing alpha by that number.

Should I use Bonferroni or Holm in R?

R’s documentation says there seems no reason to use the unmodified Bonferroni correction because Holm’s method dominates it and is also valid under arbitrary assumptions. Use Bonferroni when a protocol or reviewer requires it by name.

Can I abbreviate the method name?

Yes. The documentation says the method can be abbreviated, and the official pairwise.t.test example uses “bonf”.

Does the Bonferroni method assume independent tests?

The documentation lists Bonferroni among the methods designed for strong family-wise error rate control and notes that Holm is valid under arbitrary assumptions; independence is stated as a condition only for Hochberg and Hommel.

Sources (official docs)

Follow CASRAI

Research-administration guidance, standards updates and independent tool reviews.

Ask CASRAI · free to try

Ask about Bonferroni Correction in R: p.adjust() and pairwise.t.test()

Ask your first 2 questions free below. Subscribers get 150 a day for $29 a month.

An AI assistant specialized in research administration. It cites the sources behind every answer, labels web answers and says when it can't answer.

Answers draw on CASRAI's guides and dictionary plus the federal and funder documents we index: Federal Register, Grants.gov, Regulations.gov and UKRI.

Works on this site and inside Claude, Cursor and the AI tools you already use.

Everything CASRAI publishes — this page, the dictionary, the guides and the news — stays free to read, with no account and no card.

Referenced across the research world

University of Cambridge logoColumbia University logoCrossref logoUniversity of Edinburgh logoHarvard University logoUniversity of Oxford logoPrinceton University logoStanford School of Medicine logoUniversity College London logoORCID logoUniversity of Cambridge logoColumbia University logoCrossref logoUniversity of Edinburgh logoHarvard University logoUniversity of Oxford logoPrinceton University logoStanford School of Medicine logoUniversity College London logoORCID logo
  • University of Cambridge logo
  • Columbia University logo
  • Crossref logo
  • University of Edinburgh logo
  • Harvard University logo
  • University of Oxford logo
  • Princeton University logo
  • Stanford School of Medicine logo
  • University College London logo
  • ORCID logo

View CASRAI adoption →

Ask CASRAI · Regulatory Radar

Research-admin question? Get an answer that links its sources.

An AI assistant specialized in research administration. Every answer links its sources to check before you act. 2 questions free, no account. $29/month after.

  • Answers draw on CASRAI's guides and dictionary plus the federal and funder documents we index: Federal Register, Grants.gov, Regulations.gov and UKRI.
  • Every answer numbers its sources and links each one, so you can check the source yourself.