Skip to content

12-02: Exercises — Chi-Square Test of Independence

Notes reference: 12-02: Chi-Square Test of Independence


Q1: Expected counts

A 2 × 3 table has row totals 120 and 80, column totals 90, 70, 40, and grand total 200.

Compute all six expected counts and verify they reproduce the margins.

Solution

                 (row total) × (column total)
E_ij  =  ─────────────────────────────────────
                      grand total
Expected Col 1 Col 2 Col 3 Row total
Row 1 120×90/200 = 54.0 120×70/200 = 42.0 120×40/200 = 24.0 120.0
Row 2 80×90/200 = 36.0 80×70/200 = 28.0 80×40/200 = 16.0 80.0
Col total 90.0 70.0 40.0 200.0
The expected table reproduces every margin exactly — that is the defining
property, and the fastest way to check your arithmetic.

df = (2 − 1)(3 − 1) = 2
All E ≥ 5  ✓
' Observed in B2:D3, row totals in E, column totals in row 4
=$E2*B$4/$E$4         ' B7: expected — the mixed $ makes it fill correctly
E <- outer(c(120, 80), c(90, 70, 40)) / 200
addmargins(E)

Q2: Full test of independence

Is smoking status related to exercise habit? A survey of 300 adults:

Never exercises Sometimes Regularly Total
Smoker 32 28 15 75
Non-smoker 48 92 85 225
Total 80 120 100 300

Test at α = 0.05.

Solution

STEP 1  H₀: smoking status and exercise habit are INDEPENDENT
        H₁: they are ASSOCIATED
        α = 0.05

Expected counts

Expected Never Sometimes Regularly
Smoker 75×80/300 = 20 75×120/300 = 30 75×100/300 = 25
Non-smoker 225×80/300 = 60 225×120/300 = 90 225×100/300 = 75

All expected counts ≥ 5 ✓ · df = (2−1)(3−1) = 2

Contributions

Cell O E (O−E)²/E
Smoker / Never 32 20 7.2000
Smoker / Sometimes 28 30 0.1333
Smoker / Regularly 15 25 4.0000
Non-smoker / Never 48 60 2.4000
Non-smoker / Sometimes 92 90 0.0444
Non-smoker / Regularly 85 75 1.3333
χ² = 15.1111
STEP 4  critical χ²(0.05, 2) = 5.9915
        15.1111 > 5.9915                     →  REJECT H₀
        p-value = P(χ²₂ > 15.1111) = 0.000524

STEP 5  There is very strong evidence at the 5% level of an association
        between smoking status and exercise habit.

WHERE THE ASSOCIATION IS
        Smoker / Never exercises   7.20   ← 32 observed vs. 20 expected
        Smoker / Regularly         4.00   ← 15 observed vs. 25 expected
        Smokers are over-represented among non-exercisers and
        under-represented among regular exercisers.

EFFECT SIZE (Cramér's V)
        V = √( χ² / (n × min(r−1, c−1)) ) = √(15.1111 / (300 × 1))
          = √0.050370 = 0.2244            →  a SMALL-to-medium association

CAUSATION: this is an OBSERVATIONAL survey. It shows association only.
Age, income, and education plausibly drive both behaviours.
' Observed in B2:D3
=$E2*B$4/$E$4                       ' expected table in B7:D8
=SUMPRODUCT((B2:D3-B7:D8)^2/B7:D8)  ' χ² -> 15.1111
=(2-1)*(3-1)                        ' df -> 2
=CHISQ.INV.RT(0.05, 2)              ' 5.99146
=CHISQ.TEST(B2:D3, B7:D8)           ' p  -> 0.000524
=SQRT(15.1111/(300*1))              ' Cramér's V -> 0.2244
tab <- matrix(c(32, 28, 15,
                48, 92, 85), nrow = 2, byrow = TRUE,
              dimnames = list(smoking  = c("Smoker","Non-smoker"),
                              exercise = c("Never","Sometimes","Regularly")))
test <- chisq.test(tab)
test                       # X-squared = 15.111, df = 2, p-value = 0.0005243
test$expected
round(test$residuals^2, 3) # cell contributions
round(test$stdres, 2)      # adjusted residuals; |value| > 2 is influential

sqrt(test$statistic / (sum(tab) * min(dim(tab) - 1)))    # Cramér's V
mosaicplot(tab, shade = TRUE)
tab = pd.DataFrame([[32,28,15],[48,92,85]],
                   index=["Smoker","Non-smoker"],
                   columns=["Never","Sometimes","Regularly"])
chi2, p, dof, expected = stats.chi2_contingency(tab)
chi2, dof, p
((tab - expected)**2 / expected).round(3)
np.sqrt(chi2 / (tab.values.sum() * (min(tab.shape) - 1)))

Q3: A test that does not reject

Is preference for a product related to region?

North South East Total
Prefer A 45 52 43 140
Prefer B 35 38 37 110
Total 80 90 80 250

Test at α = 0.05.

Solution

EXPECTED
    Prefer A:  140×80/250 = 44.8    140×90/250 = 50.4    140×80/250 = 44.8
    Prefer B:  110×80/250 = 35.2    110×90/250 = 39.6    110×80/250 = 35.2

CONTRIBUTIONS
    (45−44.8)²/44.8 = 0.00089
    (52−50.4)²/50.4 = 0.05079
    (43−44.8)²/44.8 = 0.07232
    (35−35.2)²/35.2 = 0.00114
    (38−39.6)²/39.6 = 0.06465
    (37−35.2)²/35.2 = 0.09205
                      ─────────
                χ²  = 0.28184

df = (2−1)(3−1) = 2
critical χ²(0.05, 2) = 5.9915
0.28184 < 5.9915                        →  FAIL TO REJECT H₀
p-value = P(χ²₂ > 0.28184) = 0.8685

CONCLUSION: no evidence of an association. Preference appears the same
across regions — which is itself a useful marketing finding: one national
campaign, not three regional ones.

Cramér's V = √(0.28184/250) = 0.0336 — essentially zero.

Q4: Test of homogeneity

Three clinics each treat 100 patients. Outcomes:

Improved No change Worse Total
Clinic A 62 28 10 100
Clinic B 55 33 12 100
Clinic C 41 40 19 100
Total 158 101 41 300

Test at α = 0.05 whether the three clinics have the same outcome distribution.

Solution

STEP 1  H₀: the three clinics have the SAME outcome distribution
        H₁: at least one clinic differs
        α = 0.05

    NOTE: this is a test of HOMOGENEITY, not independence, because the
    row totals (100 each) were FIXED BY DESIGN — three separate samples,
    not one sample cross-classified. The arithmetic is identical; only
    the wording of the hypotheses changes.

EXPECTED (each row total is 100, so E = 100 × col total / 300)
    Improved:   158/3 = 52.667      each clinic
    No change:  101/3 = 33.667
    Worse:       41/3 = 13.667

CONTRIBUTIONS
    A: (62−52.667)²/52.667 = 1.6540   (28−33.667)²/33.667 = 0.9538
       (10−13.667)²/13.667 = 0.9837
    B: (55−52.667)²/52.667 = 0.1034   (33−33.667)²/33.667 = 0.0132
       (12−13.667)²/13.667 = 0.2033
    C: (41−52.667)²/52.667 = 2.5842   (40−33.667)²/33.667 = 1.1917
       (19−13.667)²/13.667 = 2.0813
                                       ────────────────────
                                 χ²  = 9.7686

df = (3−1)(3−1) = 4
critical χ²(0.05, 4) = 9.4877
9.7686 > 9.4877                        →  REJECT H₀
p-value = P(χ²₄ > 9.7686) = 0.0446

CONCLUSION
    There is (just) sufficient evidence at the 5% level that the clinics
    differ in outcome distribution. Clinic C contributes most of the
    statistic: fewer "Improved" (41 vs. 52.7) and more "Worse"
    (19 vs. 13.7) than expected.

    p = 0.045 is a borderline result. Report it as such rather than as
    a firm finding, and follow up with more data before acting on it.

Cramér's V = √(9.7686/(300 × 2)) = √0.016281 = 0.1276 — small.
tab <- matrix(c(62,28,10, 55,33,12, 41,40,19), nrow = 3, byrow = TRUE,
              dimnames = list(clinic = c("A","B","C"),
                              outcome = c("Improved","No change","Worse")))
chisq.test(tab)            # X-squared = 9.7686, df = 4, p-value = 0.04456
round(chisq.test(tab)$expected, 3)
round(chisq.test(tab)$residuals^2, 3)

barplot(prop.table(tab, 1), beside = TRUE,
        col = c("#5B2A86","#8A5FBF","#0FA3A3"), legend.text = rownames(tab))

Q5: A 2 × 2 table with small counts

A pilot study: 9 of 12 patients on treatment improved; 4 of 11 on control improved. Test at α = 0.05.

Solution

Improved Not improved Total
Treatment 9 3 12
Control 4 7 11
Total 13 10 23
EXPECTED
    Treatment / Improved  = 12 × 13/23 = 6.783
    Treatment / Not       = 12 × 10/23 = 5.217
    Control  / Improved   = 11 × 13/23 = 6.217
    Control  / Not        = 11 × 10/23 = 4.783

CONDITION CHECK: all E ≥ 5?  The smallest is 4.783 < 5.   ✗ MARGINAL

ORDINARY CHI-SQUARE (no correction)
    χ² = (9−6.783)²/6.783 + (3−5.217)²/5.217
       + (4−6.217)²/6.217 + (7−4.783)²/4.783
       = 0.7246 + 0.9421 + 0.7906 + 1.0289
       = 3.4862
    df = 1,  critical 3.8415,  p = 0.0619   →  fail to reject

WITH YATES' CONTINUITY CORRECTION (R's default on 2×2)
    χ²_Yates = 2.0913,  p = 0.1481          →  fail to reject

FISHER'S EXACT TEST — the right tool here
    p = 0.0995 (two-sided)                  →  fail to reject

ALL THREE AGREE: not significant at α = 0.05.

WHY THE THREE DIFFER SO MUCH
    With n = 23 the chi-square approximation to the exact distribution
    is poor. Yates' correction over-corrects; Fisher's test computes the
    exact hypergeometric probability and needs no approximation.

RECOMMENDATION for any 2×2 table with an expected count below 5:
    report FISHER'S EXACT TEST.

    Note also the practical picture: 75% improved on treatment vs. 36% on
    control. That is a large apparent effect — the study is simply too
    small to establish it. Compute the sample size needed and run a
    proper trial.
tab <- matrix(c(9, 3, 4, 7), nrow = 2, byrow = TRUE)
chisq.test(tab)                     # Yates correction ON by default
chisq.test(tab, correct = FALSE)    # uncorrected
fisher.test(tab)                    # EXACT — use this one
chisq.test(tab)$expected            # shows the 4.78 cell
stats.chi2_contingency([[9,3],[4,7]])                   # Yates by default
stats.chi2_contingency([[9,3],[4,7]], correction=False)
stats.fisher_exact([[9,3],[4,7]])                       # exact

Q6: Build the table from raw data

You have a data frame with columns gender and product. Produce the contingency table, the test, and the row percentages.

Solution

' 1. Insert ▸ PivotTable
'      Rows    = gender
'      Columns = product
'      Values  = product (set to Count)
' 2. Copy the interior counts (WITHOUT the Grand Total row/column)
'    to a clean block, add margins with SUM.
' 3. Build the expected table:  =$E2*B$5/$E$5
' 4. =CHISQ.TEST(observed_range, expected_range)
'
' Row percentages: right-click a PivotTable value ▸
'    Show Values As ▸ % of Row Total
'
' Straight from raw data without a pivot:
=COUNTIFS($A:$A, $F2, $B:$B, G$1)
tab <- table(df$gender, df$product)
addmargins(tab)

test <- chisq.test(tab)
test
test$expected
round(prop.table(tab, 1), 3)            # row percentages
round(test$residuals^2, 3)              # contributions

# Check the condition before trusting the result
any(test$expected < 5)
tab = pd.crosstab(df["gender"], df["product"])
tab_with_margins = pd.crosstab(df["gender"], df["product"], margins=True)

chi2, p, dof, expected = stats.chi2_contingency(tab)
pd.DataFrame(expected, index=tab.index, columns=tab.columns).round(3)

pd.crosstab(df["gender"], df["product"], normalize="index").round(3)
(expected < 5).any()

Q7: Independence vs. homogeneity

Explain the difference using two study designs that produce the same 2 × 2 table.

Solution

THE SAME TABLE, TWO DIFFERENT STUDIES

DESIGN 1 — TEST OF INDEPENDENCE
    Draw ONE random sample of 200 people. Record TWO variables on each:
    smoker (yes/no) and has-asthma (yes/no).
    Nobody decides in advance how many smokers there will be —
    BOTH margins are random.

    H₀: smoking and asthma are INDEPENDENT
    Question: "are these two variables related?"

DESIGN 2 — TEST OF HOMOGENEITY
    Deliberately recruit 100 smokers AND 100 non-smokers. Record whether
    each has asthma.
    The ROW TOTALS ARE FIXED BY DESIGN — you chose 100 and 100.

    H₀: the two POPULATIONS have the same asthma rate
    Question: "do these groups differ?"

WHAT IS IDENTICAL
    • expected counts:  row × col / grand
    • the χ² statistic
    • df = (r−1)(c−1)
    • the p-value and the decision

WHAT DIFFERS
    • the wording of H₀ and of the conclusion
    • what you may generalize to: Design 2 says nothing about the
      PREVALENCE of smoking, because you fixed it yourself.

PRACTICAL UPSHOT: software cannot tell them apart — chisq.test() gives
the same output either way. YOU supply the interpretation from the design.

Q8: Effect size and sample size

A 2 × 2 table shows χ² = 4.2 with n = 500. Another shows χ² = 4.2 with n = 50. Compare.

Solution

BOTH have df = 1 and the same p-value:
    p = P(χ²₁ > 4.2) = 0.0404   →  both reject at α = 0.05

BUT THE ASSOCIATIONS ARE COMPLETELY DIFFERENT IN STRENGTH:

    n = 500:  φ = √(4.2/500) = √0.0084 = 0.0917    →  NEGLIGIBLE
    n =  50:  φ = √(4.2/50)  = √0.084  = 0.2898    →  SMALL-to-MEDIUM

    (for a 2×2 table, Cramér's V = φ)

INTERPRETATION
    With n = 500, a trivially weak association reaches significance
    because the test has a lot of power. With n = 50, the SAME statistic
    reflects a genuinely noticeable pattern.

    χ² alone tells you only whether the pattern is distinguishable from
    noise — it conflates EFFECT SIZE with SAMPLE SIZE.

ALWAYS REPORT
    χ², df, p, n, AND an effect size (φ or Cramér's V), plus the
    percentage table so the reader can see the pattern for themselves.

BENCHMARKS for Cramér's V with min(r−1, c−1) = 1:
    0.10 small · 0.30 medium · 0.50 large

⬅️ Previous: 12-01: Exercises — Chi-Square Goodness-of-Fit Test ➡️ Next: 12-03: Exercises — One-Way ANOVA