ANOVA (Analysis of Variance) › Worked Example: Comparing Fertilizers
Machine Learning from ScratchAll chapters ANOVA (Analysis of Variance) › Worked Example: Comparing FertilizersOne section · Sections in this chapter5 / 13 ↓
Reading tools Definitions & inline links
Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.
Article links
Make inline references clickable
A agricultural researcher tests three fertilizers to see if they produce different plant growth. She randomly assigns 10 plants to each fertilizer and measures growth (in cm) after 6 weeks.
import numpy as np
from scipy import stats
# Plant growth data (cm) for three fertilizers
fertilizer_a = np.array([23, 25, 21, 24, 22, 26, 23, 24, 25, 22])
fertilizer_b = np.array([28, 31, 29, 30, 27, 32, 28, 29, 31, 30])
fertilizer_c = np.array([25, 27, 24, 26, 28, 25, 26, 27, 24, 26])
# Combine into groups
groups = [fertilizer_a, fertilizer_b, fertilizer_c]
group_names = ["Fertilizer A", "Fertilizer B", "Fertilizer C"]
# Step 1: Calculate basic statistics
k = len(groups)
ns = [len(g) for g in groups]
N = sum(ns)
means = [np.mean(g) for g in groups]
grand_mean = np.mean(np.concatenate(groups))
# Step 2: Calculate Sum of Squares Between (SSB)
ss_between = sum(n * (m - grand_mean) ** 2 for n, m in zip(ns, means))
# Step 3: Calculate Sum of Squares Within (SSW)
ss_within = sum(np.sum((g - m) ** 2) for g, m in zip(groups, means))
# Step 4: Calculate Total Sum of Squares (SST)
all_data = np.concatenate(groups)
ss_total = np.sum((all_data - grand_mean) ** 2)
# Verify decomposition: SST = SSB + SSW
assert np.isclose(ss_total, ss_between + ss_within)
# Step 5: Calculate degrees of freedom
df_between = k - 1
df_within = N - k
df_total = N - 1
# Step 6: Calculate Mean Squares
ms_between = ss_between / df_between
ms_within = ss_within / df_within
# Step 7: Calculate F-statistic
f_stat = ms_between / ms_within
# Step 8: Calculate p-value
p_value = stats.f.sf(f_stat, df_between, df_within)
# Step 9: Calculate effect size (eta-squared)
eta_squared = ss_between / ss_totalLet's trace through the calculation step by step:
Step 1: Basic Statistics
Step 2: Sum of Squares Between
Step 3: Sum of Squares Within
Step 4: Mean Squares and F-Statistic
One-Way ANOVA: Effect of Fertilizer on Plant Growth ================================================================= Group Statistics: ----------------------------------------------------------------- Fertilizer A: n = 10, mean = 23.50 cm Fertilizer B: n = 10, mean = 29.50 cm Fertilizer C: n = 10, mean = 25.80 cm Grand mean: 26.27 cm ANOVA Table: ----------------------------------------------------------------- Source SS df MS F p-value ----------------------------------------------------------------- Between 183.27 2 91.63 40.83 6.87e-09 Within 60.60 27 2.24 Total 243.87 29 ----------------------------------------------------------------- Effect size (eta-squared): 0.752 Interpretation: 75.2% of variance in growth is explained by fertilizer type Decision (alpha = 0.05): ----------------------------------------------------------------- F(2, 27) = 40.83, p < 0.001 Reject H_0: At least one fertilizer produces different growth
# Verify our calculation with scipy
f_scipy, p_scipy = stats.f_oneway(fertilizer_a, fertilizer_b, fertilizer_c)
print("Verification with scipy.stats.f_oneway:")
print(f" F = {f_scipy:.2f}, p = {p_scipy:.2e}")Verification with scipy.stats.f_oneway: F = 40.83, p = 6.87e-09


The ANOVA reveals a highly significant difference among fertilizers (, ). The effect size indicates that 72% of the variation in plant growth is explained by fertilizer type, a very large effect.