# How to Calculate a One-Way ANOVA: Worked Example With Welch F and Tukey HSD

A one-way ANOVA tests whether three or more group means differ when the groups are defined by a single categorical factor. The method partitions total variation into variation between group means and variation within groups, then compares those two quantities through an F statistic [1]. It replaces a series of pairwise t-tests, which inflate the familywise Type I error rate as the number of comparisons grows [2].

This article walks through the full calculation on a small dataset: sums of squares, the ANOVA table, the classical F test, Welch's alternative for unequal variances, and Tukey HSD for pairwise follow-up. Every number is reproducible by hand or with a few lines of Python.

## Quick Answer

- Partition variance: \(SS_{T} = SS_{B} + SS_{W}\), where \(SS_B\) reflects differences among group means and \(SS_W\) reflects scatter inside groups [1].
- Classical F statistic: \(F = MS_B / MS_W = (SS_B/(k-1)) / (SS_W/(N-k))\), compared against an F distribution with \(k-1\) and \(N-k\) degrees of freedom [1].
- Welch's F replaces the pooled within-group variance with a variance-weighted term and adjusts the denominator degrees of freedom; it is the safer default when group variances or sizes differ [3][4].
- If the omnibus test rejects, Tukey HSD controls the familywise error rate across all pairwise mean differences; use the Tukey-Kramer form when group sizes are unequal [6][7].
- Check assumptions first: independent observations, approximately normal residuals, and comparable variances. Levene's test and a residual normality check are standard screens.

## The Model and the Hypotheses

For \(k\) groups with \(n_i\) observations each and a total of \(N\) observations, the one-way model writes each observation as

$$
y_{ij} = \mu + \tau_i + \varepsilon_{ij}
$$

where \(\mu\) is the grand mean, \(\tau_i\) is the effect of group \(i\), and \(\varepsilon_{ij}\) is the residual error [1]. The null hypothesis is \(H_0: \mu_1 = \mu_2 = \dots = \mu_k\), meaning all group means are equal. The alternative is that at least one mean differs from the others. Note that rejection does not tell you which group is different, only that the set of means is not homogeneous.

The test rests on three assumptions: observations are independent, residuals are approximately normally distributed, and the groups share a common variance \(\sigma^2\) [2]. The third assumption is the one that causes the most trouble in practice, and it is the reason Welch's version exists.

## Step-by-Step Calculation

**Step 1. Compute group means and the grand mean.** For each group, \(\bar{y}_i = \sum_j y_{ij} / n_i\). The grand mean is \(\bar{y} = \sum_i \sum_j y_{ij} / N\).

**Step 2. Compute the sums of squares.** The between-group sum of squares weights each group mean by its sample size:

$$
SS_B = \sum_{i=1}^{k} n_i (\bar{y}_i - \bar{y})^2
$$

The within-group sum of squares adds up the squared deviations inside each group:

$$
SS_W = \sum_{i=1}^{k} \sum_{j=1}^{n_i} (y_{ij} - \bar{y}_i)^2
$$

The total sum of squares is \(SS_T = SS_B + SS_W\) [1].

**Step 3. Fill in the ANOVA table.** Divide each sum of squares by its degrees of freedom to get mean squares, then form the F ratio.

| Source | df | SS | MS | F |
|---|---|---|---|---|
| Between groups | \(k-1\) | \(SS_B\) | \(MS_B = SS_B/(k-1)\) | \(MS_B/MS_W\) |
| Within groups | \(N-k\) | \(SS_W\) | \(MS_W = SS_W/(N-k)\) | |
| Total | \(N-1\) | \(SS_T\) | | |

**Step 4. Get the p-value.** Compare the observed F to an F distribution with \(k-1\) and \(N-k\) degrees of freedom. Reject \(H_0\) when F exceeds the critical value or when p falls below your chosen alpha.

**Step 5. Run Welch's F if variances look unequal.** Welch's statistic uses a different denominator and fractional degrees of freedom [3]. Delacre and colleagues argue that the classical F test is biased under unequal variances and recommend the Welch W-test as the default [4]. SciPy exposes both through `f_oneway`, with `equal_var=False` selecting the Welch variant [5].

**Step 6. Follow up with Tukey HSD.** When the omnibus test rejects, Tukey's method compares every pair of means while holding the familywise error rate at alpha [6]. For unequal group sizes, the Tukey-Kramer adjustment applies [7]. SciPy's `tukey_hsd` implements this directly [8].

## Worked Example

Suppose you grow three batches of cells under media formulations A, B, and C and measure a fluorescence readout in five replicate wells per batch. The data are illustrative and constructed for this walkthrough.

| Group | Observations | Mean | Variance |
|---|---|---|---|
| A | 12, 14, 11, 13, 15 | 13 | 2.5 |
| B | 16, 18, 15, 17, 19 | 17 | 2.5 |
| C | 13, 15, 14, 12, 16 | 14 | 2.5 |

Here \(k = 3\), each \(n_i = 5\), and \(N = 15\). The grand mean is 14.667.

**Sums of squares.** Each group contributes \(n_i(\bar{y}_i - \bar{y})^2\):

- A: \(5 \times (13 - 14.667)^2 = 13.89\)
- B: \(5 \times (17 - 14.667)^2 = 27.22\)
- C: \(5 \times (14 - 14.667)^2 = 2.22\)

So \(SS_B = 43.33\). Each group variance is 2.5, and with \(n_i - 1 = 4\) degrees of freedom per group, each group contributes \(4 \times 2.5 = 10\) to \(SS_W\). Three groups give \(SS_W = 30\). Then \(SS_T = 43.33 + 30 = 73.33\).

**ANOVA table.**

| Source | df | SS | MS | F |
|---|---|---|---|---|
| Between | 2 | 43.33 | 21.67 | 8.667 |
| Within | 12 | 30.00 | 2.50 | |
| Total | 14 | 73.33 | | |

The classical F statistic is \(F(2, 12) = 8.667\) with \(p = 0.00469\). The critical value at alpha = 0.05 is \(F_{crit}(0.05; 2, 12) = 3.885\), so the result is significant. The effect size, \(\eta^2 = SS_B / SS_T = 43.33 / 73.33 = 0.591\), indicates that about 59 percent of the total variation is attributable to group membership.

**Welch's F.** With `equal_var=False`, SciPy returns \(F(2, 8.0) = 8.00\), \(p = 0.0123\) [5]. The conclusion is unchanged here, but the Welch p-value is larger because the denominator degrees of freedom drop from 12 to 8. That is the price of not assuming equal variances.

**Assumption checks.** Levene's test gives \(p = 1.0\), and Shapiro-Wilk on the residuals gives \(p = 0.103\). Neither flags a problem, which is expected given the constructed data.

**Tukey HSD.** The studentized range critical value is \(q(0.05; 3, 12) = 3.773\). With \(n = 5\) per group and \(MS_W = 2.50\), the honestly significant difference is

$$
HSD = q \sqrt{MS_W / n} = 3.773 \times \sqrt{2.50/5} = 2.668
$$

| Comparison | Mean difference | 95% CI | p |
|---|---|---|---|
| B vs A | 4.0 | 1.332 to 6.668 | 0.0046 |
| B vs C | 3.0 | 0.332 to 5.668 | 0.0277 |
| C vs A | 1.0 | -1.668 to 3.668 | 0.591 |

Group B is higher than both A and C. Groups A and C are not distinguishable at this sample size. You can reproduce these numbers with the site's [ANOVA Calculator](/tools/anova-calculator) or in Python with `scipy.stats.f_oneway` and `scipy.stats.tukey_hsd` [5][8].

## Choosing Between Classical F and Welch F

The classical F test assumes a single underlying variance across groups. When that assumption fails, the test's actual Type I error rate can drift away from the nominal alpha, sometimes substantially [4]. Welch's statistic was designed for exactly this situation [3], and simulation work supports using it as the default, not as a fallback [4].

In practice, inspect the group variances and sizes. If they differ, report Welch's F; Delacre and colleagues recommend it even when they look similar [4]. When variances and sizes are similar, the two tests agree closely, and either is defensible. Reporting both is cheap and transparent.

## Common Mistakes

- **Running multiple t-tests instead of ANOVA.** Each additional comparison raises the chance of a false positive. ANOVA plus a controlled post-hoc procedure keeps the familywise error rate at alpha [2].
- **Treating a significant F as proof that all groups differ.** The omnibus test only says the means are not all equal. Tukey HSD or another post-hoc method identifies which pairs differ [6].
- **Ignoring unequal variances.** The classical F can be biased when variances differ [4]. Check Levene's test and switch to Welch's F when needed [3].
- **Using the equal-n Tukey formula with unequal group sizes.** The Tukey-Kramer adjustment is required when group sizes differ [7].
- **Reporting p without an effect size.** A small p with a tiny \(\eta^2\) means the difference is detectable but may not be meaningful. Report both.
- **Skipping the residual diagnostics.** Normality and independence are model assumptions, not optional checks. Plot residuals and run Shapiro-Wilk.

## Limitations

The one-way ANOVA handles a single factor. If your design has two crossed factors, repeated measurements on the same subject, or blocking, you need a different model. Non-normal residuals with small samples can distort the F distribution; a rank-based alternative such as Kruskal-Wallis may be more appropriate. Tukey HSD controls error across all pairwise comparisons, which can be conservative when you only care about a few planned contrasts. Welch's F addresses variance heterogeneity but does not fix non-normality or dependence among observations. And no test can rescue a study with too few replicates per group: with \(n = 5\), the confidence intervals above span several units, and small effects will remain undetectable.

## Frequently Asked Questions

### What is the difference between the classical F and Welch's F?

The classical F pools the within-group variance into a single \(MS_W\) and uses \(N - k\) denominator degrees of freedom [1]. Welch's F weights each group's variance separately and computes fractional denominator degrees of freedom, which makes it robust to unequal variances [3]. Under equal variances and balanced designs, the two give nearly identical results.

### When should I use Tukey HSD instead of a Bonferroni correction?

Tukey HSD is designed specifically for all pairwise comparisons after ANOVA and is generally more powerful than Bonferroni for that purpose [6]. Bonferroni is simpler and works for any set of contrasts, including planned comparisons that are not all pairs. If you have a small number of pre-specified contrasts, Bonferroni or a similar method is fine. For exhaustive pairwise testing, Tukey is the standard choice.

### Can I run ANOVA on two groups?

Yes, but a two-sample t-test gives the same p-value and is simpler. ANOVA becomes useful when \(k \geq 3\), because that is when the multiple-comparison problem appears [2].

### What sample size do I need per group?

There is no universal number. Power depends on the effect size you want to detect, the within-group variance, alpha, and \(k\). With small samples, the F test is sensitive to non-normality, and confidence intervals will be wide. Pilot data or published variance estimates let you run a proper power calculation before collecting data.

### Does a significant ANOVA mean the groups are biologically different?

No. Statistical significance means the observed mean differences are unlikely under the null model given the assumptions. Whether a difference matters depends on the effect size, the measurement's precision, and the biological context. Report \(\eta^2\), confidence intervals, and the raw group means so readers can judge.

## References

1. [NIST/SEMATECH e-Handbook of Statistical Methods: Are the means equal? (one-way ANOVA)](https://www.itl.nist.gov/div898/handbook/prc/section4/prc43.htm)
2. [Kim HY. Analysis of variance (ANOVA) comparing means of more than two groups. Restorative Dentistry and Endodontics, 2014](https://doi.org/10.5395/rde.2014.39.1.74)
3. [Welch BL. On the comparison of several mean values: an alternative approach. Biometrika, 1951](https://doi.org/10.1093/biomet/38.3-4.330)
4. [Delacre M, Leys C, Mora YL, Lakens D. Taking parametric assumptions seriously: arguments for the use of Welch's F-test instead of the classical F-test in one-way ANOVA. International Review of Social Psychology, 2019](https://doi.org/10.5334/irsp.198)
5. [SciPy documentation: scipy.stats.f_oneway](https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.f_oneway.html)
6. [NIST/SEMATECH e-Handbook of Statistical Methods: Tukey's method](https://www.itl.nist.gov/div898/handbook/prc/section4/prc471.htm)
7. [Kramer CY. Extension of multiple range tests to group means with unequal numbers of replications. Biometrics, 1956](https://doi.org/10.2307/3001469)
8. [SciPy documentation: scipy.stats.tukey_hsd](https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.tukey_hsd.html)

## Related Articles

- [T-Test vs ANOVA: A Decision Framework for Comparing Group Means](/blog/guides/t-test-vs-anova-a-decision-framework-for-comparing-group-means)
- [Post-Hoc Comparisons After ANOVA](/knowledge/bioinformatics/post-hoc-comparisons-after-anova-tukey-bonferroni-and-false-discovery-rate-which-one-for-your-biolog)
- [Kruskal-Wallis Test vs. One-Way ANOVA](/knowledge/bioinformatics/kruskal-wallis-test-vs-one-way-anova-how-to-choose-and-what-to-do-after-rejection)
- [Repeated Measures ANOVA vs Mixed-Effects Models](/knowledge/bioinformatics/repeated-measures-anova-vs-mixed-effects-models-which-should-you-use-for-your-biological-data)
- [Degrees of Freedom in Statistics: What df Means](/blog/research-skills/degrees-of-freedom-in-statistics-what-df-means)
- [How to Report Statistical Results in Lab Report Text](/blog/research-skills/how-to-report-statistical-results-in-lab-report-text-means-sd-p-values-and-more)