Post Hoc Tests Explained: Tukey, Bonferroni and Dunn, with Real Data

Home › Research Methodology › Post Hoc Tests Explained

Quick answerPost hoc tests are run after a significant ANOVA or Kruskal-Wallis test to find out which specific groups differ. Because they compare many pairs, they adjust p-values (for example with the Bonferroni correction or Tukey’s method) to avoid false alarms from testing many times.

Below, we explain post hoc tests step by step and run them live on real data from ScholarsColab.

Common post hoc tests at a glance

Test Use after Notes
Tukey’s HSD ANOVA with equal spreads The most common choice; compares all pairs
Games-Howell ANOVA with unequal spreads Safer when group spreads differ
Bonferroni Any set of comparisons Simple and strict: multiply each p-value by the number of comparisons
Dunn’s test Kruskal-Wallis Pairwise comparisons of ranks
Dunnett’s test ANOVA, comparing groups with one control Fewer comparisons, more power

Worked example: which venue groups differ in citations?

This continues the example from our ANOVA guide, where we found that citations differ between types of venue. Now we find out which types differ. Every result is calculated live, and you can run the same code yourself.

The data: 41,792 papers, mostly published between 1998 and 2026, by 967 ScholarsColab mentors (data updated 15 October 2026). The file you will load is anonymised: no paper titles, journal names or researcher identities.

Step 1: Load the data

In [1]:
import numpy as np
import pandas as pd
from datetime import date

# The ScholarsColab Research Methods Index (papers level): anonymised, updated daily
DATA = "https://scholarscolab.com/wp-json/scholarscolab/v1/research-methods-data?level=papers"
papers = pd.read_csv(DATA, comment="#")   # lines starting with # hold the citation

def p_text(p):
    return "< 0.001" if p < 0.001 else f"= {p:.3f}"

print(papers.shape)
papers.head()
Out [1]:
(41792, 15)
yearcitationsvenue_typevenue_grouptitle_wordstitle_colonreviewsystematicmachine_learningdeep_learningiotblockchaincovidsustainabilitydiscipline
02023.031journalOther journals12000000000Medicine & health
12024.024journalOther journals13000110000Medicine & health
22024.012journalOther journals16000000000Medicine & health
32024.09journalOther journals20010000000Medicine & health
42024.09journalGeneric "International Journal of"9000000000Medicine & health

Step 2: Choose the papers and the groups

We use the same five venue groups and the same publication window as in the ANOVA example.

In [2]:
THIS_YEAR = date.today().year
# Compare papers that have had a similar time to collect citations

window = papers[papers["year"].between(THIS_YEAR - 16, THIS_YEAR - 4)].copy()

groups = ["IEEE Transactions / Journals", "Open-access mega-journals", "Other journals",
          'Generic "International Journal of"', "Conference"]
window = window[window["venue_group"].isin(groups)]

summary = window.groupby("venue_group")["citations"].agg(papers="count", median="median", mean="mean")
summary.reindex(groups).round(1)
Out [2]:
papersmedianmean
IEEE Transactions / Journals25269.5120.2
Open-access mega-journals81334.060.6
Other journals121739.047.3
Generic "International Journal of"32534.014.8
Conference41015.012.8

Step 3: Compare every pair, and correct for multiple testing

With five groups there are ten possible pairs. Because citations are skewed, we compare each pair with the Mann-Whitney U test. Running ten tests raises the chance of a false alarm, so the Bonferroni correction multiplies each p-value by ten.

In [3]:
from itertools import combinations
from scipy import stats

pairs = list(combinations(groups, 2))
rows = []
for a, b in pairs:
    x = window.loc[window["venue_group"] == a, "citations"]
    y = window.loc[window["venue_group"] == b, "citations"]
    u, p = stats.mannwhitneyu(x, y, alternative="two-sided")
    rows.append({"pair": f"{a} vs {b}", "median A": x.median(), "median B": y.median(),
                 "p": p, "p (Bonferroni)": min(1, p * len(pairs))})

result = pd.DataFrame(rows).set_index("pair")
result["significant"] = result["p (Bonferroni)"] < 0.05
result.round(4)
Out [3]:
median Amedian Bpp (Bonferroni)significant
IEEE Transactions / Journals vs Open-access mega-journals69.534.00.00000.0000True
IEEE Transactions / Journals vs Other journals69.59.00.00000.0000True
IEEE Transactions / Journals vs Generic "International Journal of"69.54.00.00000.0000True
IEEE Transactions / Journals vs Conference69.55.00.00000.0000True
Open-access mega-journals vs Other journals34.09.00.00000.0000True
Open-access mega-journals vs Generic "International Journal of"34.04.00.00000.0000True
Open-access mega-journals vs Conference34.05.00.00000.0000True
Other journals vs Generic "International Journal of"9.04.00.00000.0000True
Other journals vs Conference9.05.00.00000.0000True
Generic "International Journal of" vs Conference4.05.00.23311.0000False
How to read it: 9 of the 10 pairs differ significantly even after the Bonferroni correction. The exceptions: “Generic "International Journal of" vs Conference”. Their medians are close, and after the correction the difference is not significant. A post hoc test is exactly what reveals findings like this, which the overall ANOVA cannot show.
Run this analysis yourself. Download the notebook and open it in Jupyter or Google Colab (File → Upload notebook). It loads the same anonymised ScholarsColab data, so your results will match this page.Download notebook (.ipynb) →Data files: papers.csv · mentors.csv

How to choose and run a post hoc test

  1. Run the overall test first (ANOVA or Kruskal-Wallis).
  2. Choose the post hoc test that matches it and your data (see the table above).
  3. Decide which comparisons you need: all pairs, or only each group against a control.
  4. Report adjusted p-values and the size of each difference.
  5. Present results clearly, for example with letters or a table showing which groups differ.

Common mistakes reviewers point out

  • Running many unadjusted t-tests instead of a proper post hoc test.
  • Using a post hoc test that does not match the overall test or the data.
  • Reporting only “significant” pairs without the differences themselves.
  • Running post hoc tests after a non-significant overall test and reporting chance findings.

Frequently asked questions

What is a post hoc test?

A post hoc test compares groups pairwise after a significant ANOVA or Kruskal-Wallis test, to identify which groups differ, while controlling for the extra risk of testing many pairs.

Why do we need to adjust p-values?

Every test has a small chance of a false positive. When many tests are run, those chances add up, so adjustments keep the overall false-positive rate near 5%.

What is the Bonferroni correction?

It multiplies each p-value by the number of comparisons (or divides the significance level by it). It is simple and strict.

Which post hoc test should I use after ANOVA?

Tukey’s HSD is the usual choice when group spreads are similar; Games-Howell is safer when they differ; Dunnett’s test suits comparisons with a single control group.

Which post hoc test follows Kruskal-Wallis?

Dunn’s test is the most common, or pairwise Mann-Whitney U tests with a correction such as Bonferroni.

Use this data in your own work

Use this data in your own research. Download the aggregated figures behind these charts (counts, medians and shares by type of paper, period, method and discipline) as a CSV file. Please cite it as:ScholarsColab (2026). ScholarsColab Research Methods Index [Data set]. Vidhilekha Soft Solutions Pvt Ltd. Retrieved 15 October 2026, from https://scholarscolab.com/research-methodology/. Licensed under CC BY 4.0.Download the data (CSV) →

More research methodology guides

About the data on this page. The ScholarsColab Research Methods Index is built from the Google Scholar publication lists of PhD researchers who mentor on ScholarsColab, and is updated daily as mentors and publications are added. Methods, designs and paper types are identified from titles, so papers whose titles do not mention them are not counted in those groups. The mentors are not a random sample of all researchers (engineering and computer science are strongly represented), so the figures describe this group of researchers and may not apply to research as a whole.