Chapter 09 · Inference
Categorical Data & Chi-Square
When the data are categories — oil, gas or dry; North Sea or Barents; this decade or that — means and t-tests do not apply. Chi-square procedures compare observed counts with the counts a hypothesis predicts, and one family of formulas answers three different questions.
In 60 seconds
- Every chi-square statistic is χ² = Σ (O − E)² / E: observed minus expected counts, squared, scaled, summed.
- Goodness of fit: does one categorical variable follow a stated distribution?
- Independence: are two categorical variables on one population associated?
- Homogeneity: does one categorical variable have the same distribution across several populations?
- A significant χ² says that something differs; residuals say where; Cramér's V says how much.
The chi-square distribution
χ²-curves are right-skewed, start at 0, are indexed by degrees of freedom, have mean = df, and look more and more normal as df grows. The test statistic is compared with the right tail only — large values mean "observed counts are far from expected".
χ² density curves
df = 2, 6 and 18
Goodness of fit: is exploration seasonal?
North Sea exploration uses semi-submersibles that drill year-round, but Arctic and winter weather could still push spud dates into summer. H₀: wildcats are spudded uniformly across the 12 months (each month gets 1/12). Data: 694 Norwegian wildcats, 2000–2024.
Wildcats spudded per calendar month, 2000–2024
Bars = observed · dots = expected under H₀ (uniform)
Chi-square goodness-of-fit
Conditions: every expected count ≥ 1 and at most 20 % of them below 5.
χ² = 6.01 with 11 df, p = 0.87. The spread across months is entirely consistent with chance — no seasonality. Even the Barents Sea alone (n = 99; χ² = 9.97, p = 0.53) shows none: modern rigs and ice-free southern Barents waters keep drilling year-round. "Fail to reject" is a finding too.
Contingency tables and association
Cross-tabulate two categorical variables on the same wells — the sea area and what was found — and look at the conditional distribution of fluid within each area (each row as percentages). If the variables were unrelated, every row would look like the marginal distribution (the "all areas" row).
What wildcats find, by sea area (conditional distributions)
Share of wells in each area · Norwegian wildcats, all years
The rows clearly differ in what is found — the North Sea is the oil province, the Norwegian and Barents Seas lean to gas — while the "none" share is similar everywhere (chapter 03 saw that success does not depend on area).
Independence test
Chi-square independence test
H₀: the two variables are independent. Same expected-count conditions as above.
χ² = 66.7 with 6 df, p < 0.0001 (about 2 × 10⁻¹²). Sea area and fluid type are associated. Where does the association come from? The standardised residual (O − E)/√E for each cell flags the cells that contribute most:
Cell colour: blue = fewer wells than independence predicts, red = more. Each cell shows observed (expected) and the residual. |residual| > 2 is a useful flag.
The North Sea has far more oil-only and far fewer gas finds than expected; the Norwegian Sea has far more gas and less oil; the Barents Sea has more gas and much less oil. Geology — source rocks and burial depth — in one table.
Effect size: Cramér's V
0 = no association, 1 = perfect. Here V = 0.155: a real but modest association. With n = 1,380, even modest effects give tiny p-values.
Homogeneity test: has the mix changed over the decades?
The homogeneity test uses exactly the same arithmetic as the independence test, but the question is different: here each decade is treated as a separate population, and we ask whether the distribution of fluid found is the same in all of them.
What wildcats find, by decade of spud
Share of wells · Norwegian wildcats
χ² = 43.5 with 18 df, p = 0.0007: the distributions are not homogeneous. The share of dry wells fell from the pioneering 1960s–70s, and the 2020s show more oil and oil+gas finds as exploration concentrates near existing infrastructure.
| Procedure | Question | Data | df |
|---|---|---|---|
| Goodness of fit | Does one variable follow a given distribution? | one variable, one population | k − 1 |
| Independence | Are two variables associated? | two variables, one population | (r − 1)(c − 1) |
| Homogeneity | Is the distribution the same across populations? | one variable, several populations | (r − 1)(c − 1) |
All three also work for a discrete quantitative variable with a few possible values (e.g. discoveries per campaign 0–5), treated as categories.
Do it in code
w = pd.read_sql("select * from v_wildcats", con)
obs = w.query("2000 <= entry_year <= 2024").entry_month.value_counts().sort_index()
stats.chisquare(obs) # goodness of fit vs uniform: p = 0.87
ct = pd.crosstab(w.main_area, w.fluid)
chi2, p, dof, expected = stats.chi2_contingency(ct) # 66.7, 2e-12, 6
resid = (ct - expected) / np.sqrt(expected) # standardised residuals
v = np.sqrt(chi2 / (ct.values.sum() * (min(ct.shape) - 1))) # Cramér's V = 0.155
with o as (select main_area, fluid, count(*)::numeric as obs
from v_wildcats group by 1, 2),
t as (select o.*,
sum(obs) over (partition by main_area) as row_total,
sum(obs) over (partition by fluid) as col_total,
sum(obs) over () as n
from o)
select main_area, fluid, obs,
round(row_total * col_total / n, 1) as expected,
round((obs - row_total * col_total / n) / sqrt(row_total * col_total / n), 2) as std_resid,
sum((obs - row_total * col_total / n)^2 / (row_total * col_total / n)) over () as chi2
from t order by 1, 2;
Key takeaways
- χ² = Σ (O − E)²/E for every chi-square procedure; only the expected counts and df change.
- Goodness of fit (one variable), independence (two variables), homogeneity (several populations).
- Check expected counts (all ≥ 1, ≤ 20 % below 5) before trusting the p-value.
- Report residuals and Cramér's V, not just p.
Self-check
1 · Expected count
The Barents Sea row has 157 wells; the "gas" column has 229 of 1,380 wells. Expected gas finds in the Barents Sea under independence?
157 × 229 / 1,380 = 26.1. Observed: 38 — residual (38 − 26.1)/√26.1 = +2.3.
2 · Which test?
You want to know whether the share of North Sea, Norwegian Sea and Barents Sea wells is the same for three different operators. Which chi-square test?
Homogeneity — three populations (operators), one categorical variable (area).
3 · Degrees of freedom
A 5-category goodness-of-fit test and a 4 × 3 independence test: df?
4 and (4 − 1)(3 − 1) = 6.