ANOVA and the Kruskal-Wallis Test, Explained with Real Data

Home › Research Methodology › ANOVA and the Kruskal-Wallis Test, Explained with Real Data

Quick answerANOVA (analysis of variance) tests whether the averages of three or more groups differ. It produces an F statistic and a p-value. A significant result says at least one group differs, but not which one; post hoc tests answer that. When data are skewed, the Kruskal-Wallis test does the same job using ranks.

Below, we explain ANOVA in plain language and then work through a complete analysis, notebook-style, on real research data: does the type of venue a paper is published in affect how often it is cited? Every result on this page is calculated live from the ScholarsColab dataset, and you can run the same code yourself.

ANOVA at a glance

One-way ANOVA Kruskal-Wallis test
Compares Means of 3+ groups Ranks (distributions) of 3+ groups
Best for Roughly normal data with similar spreads Skewed data, outliers, ordinal scales
Result F statistic and p-value H statistic and p-value
Next step if significant Post hoc tests (e.g. Tukey) Post hoc tests (e.g. Dunn)

Why not just run several t-tests? Each test carries a 5% risk of a false alarm; running many of them multiplies that risk. ANOVA tests all groups at once.

Worked example: does the venue affect citations?

The data: 41,792 papers, mostly published between 1998 and 2026, by 967 ScholarsColab mentors (data updated 15 October 2026). Each paper’s venue has been grouped from its journal or conference name. No titles, journal names or researcher identities are included in the dataset you will load.

Step 1: Load the data

We load the anonymised paper-level data straight from ScholarsColab. Each row is one published paper.

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

Citations build up over time, so comparing a 2024 paper with a 2012 paper would be unfair. We keep papers published in the same window of years, and compare five groups of venues.

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
The medians already tell a story: a typical paper in the “IEEE Transactions / Journals” group has a median of 69.5 citations, against 4.0 in the “Generic "International Journal of"” group. But in every group the mean sits well above the median, a first sign that the data are skewed.

Step 3: Look at the data with a box plot

Before running any test, always look at the data. A box plot shows the middle half of each group (the box), the median (the thick line) and the spread (the whiskers).

In [3]:
import matplotlib.pyplot as plt

# Citations are very skewed, so plot them on a log scale (log10 of citations + 1)

window["log_citations"] = np.log10(window["citations"] + 1)
data = [window.loc[window["venue_group"] == g, "log_citations"] for g in groups]

fig, ax = plt.subplots(figsize=(9, 4.5))
ax.boxplot(data, vert=False, showfliers=False)
ax.set_yticks(range(1, len(groups) + 1), groups)
ticks = [0, 10, 100, 1000]
ax.set_xticks(np.log10(np.array(ticks) + 1), ticks)
ax.set_xlabel("Citations (log scale)")
ax.invert_yaxis()
plt.tight_layout()
plt.show()
Out [3]:
Box plot of citations by venue group0101001000Citations (log scale)IEEE Transactions / JournalsOpen-access mega-journalsOther journalsGeneric "International Journal of"Conference
Each box covers the middle half of the papers in a group; the thick line is the median; the whiskers reach the most extreme papers within 1.5 box-lengths. Very highly cited papers beyond the whiskers are not drawn.
How to read it: the boxes for IEEE Transactions / Journals and Open-access mega-journals sit furthest to the right, so their papers tend to be cited most; Conference and Generic "International Journal of" sit furthest to the left. The boxes also differ in width, so the groups vary in spread as well as in typical value.

Step 4: Check the shape of the data

ANOVA assumes the data in each group are roughly normal. Skewness measures how lopsided the data are: 0 is symmetric, and values above about 1 are strongly skewed.

In [4]:
from scipy import stats

print(f"Skewness of citations:     {stats.skew(window['citations']):.1f}")
print(f"Skewness of log citations: {stats.skew(window['log_citations']):.2f}")
Out [4]:
Skewness of citations:     38.5
Skewness of log citations: 0.34
Raw citations have a skewness of 38.5, which is extremely lopsided (a few papers have thousands of citations). After taking the logarithm, the skewness is 0.34, close to symmetric, so we run ANOVA on the log scale.

Step 5: Run the one-way ANOVA

In [5]:
samples = [window.loc[window["venue_group"] == g, "log_citations"] for g in groups]
F, p = stats.f_oneway(*samples)

# Eta squared: the share of variation explained by the groups

grand_mean = window["log_citations"].mean()
ss_between = sum(len(s) * (s.mean() - grand_mean) ** 2 for s in samples)
ss_total = ((window["log_citations"] - grand_mean) ** 2).sum()

print(f"One-way ANOVA: F = {F:.1f}, p {p_text(p)}")
print(f"Eta squared = {ss_between / ss_total:.3f}")
Out [5]:
One-way ANOVA: F = 450.6, p < 0.001
Eta squared = 0.081
How to read it: the very small p-value means the venue groups do not all have the same average log citations. Eta squared is 0.081: the type of venue explains about 8.1% of the variation in citations. For comparison, research discipline explains about 1.8% in the same papers.

Step 6: Confirm with the Kruskal-Wallis test

Because citations are so skewed, it is good practice to confirm the result with a test that uses ranks instead of values.

In [6]:
# The rank-based alternative: suitable for skewed data such as citations

H, p = stats.kruskal(*[window.loc[window["venue_group"] == g, "citations"] for g in groups])
print(f"Kruskal-Wallis: H = {H:.1f}, p {p_text(p)}")
Out [6]:
Kruskal-Wallis: H = 1610.2, p < 0.001

Both tests agree: the type of venue is clearly associated with how often papers are cited. Which venue groups differ from which? That is the job of the post hoc tests.

Association, not cause. Papers in IEEE Transactions may be cited more partly because stronger work is submitted there in the first place. This analysis shows a strong association, not that the venue alone causes the citations.
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 run a one-way ANOVA

  1. Plot the groups with box plots before testing.
  2. Check assumptions: independent observations, roughly normal data in each group, similar spreads (Levene’s test).
  3. If assumptions fail, transform the data (as we did with logs), use Welch’s ANOVA, or use Kruskal-Wallis.
  4. Report F, both degrees of freedom, the p-value and an effect size such as eta squared.
  5. If significant, run post hoc tests to see which groups differ.

Common mistakes reviewers point out

  • Running many t-tests instead of one ANOVA.
  • Testing before looking at the data, and missing heavy skew or outliers.
  • Claiming that all groups differ from a significant ANOVA alone.
  • Reporting p without an effect size.

Frequently asked questions

What does ANOVA tell you?

It tells you whether the means of three or more groups are likely to differ, by comparing the variation between groups with the variation within groups.

What is the F statistic?

F is the ratio of between-group variation to within-group variation. Larger values suggest the group means differ more than chance would explain.

When should I use the Kruskal-Wallis test instead of ANOVA?

Use Kruskal-Wallis when data are clearly skewed, have outliers or are ordinal, because it compares ranks rather than means.

What is a box plot?

A box plot shows the median, the middle 50% of values (the box) and the spread of the rest (the whiskers), making it easy to compare groups at a glance.

Can I run this analysis in Python?

Yes. Use scipy.stats.f_oneway for ANOVA and scipy.stats.kruskal for the Kruskal-Wallis test, as shown in the notebook on this page.

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.