10.1 Pearson chi-square and Fisher exact tests
Pearson’s chi-square test compares the observed counts, \(O_{ij}\), with the counts expected if the two categorical variables were independent.
The expected count in cell \(i,j\) is
\[ E_{ij}=\frac{n_{i}n_{j}}{n}, \]
and the chi-square statistic is
\[ \chi^2=\sum_i\sum_j\frac{(O_{ij}-E_{ij})^2}{E_{ij}}. \]
A larger discrepancy between observed and expected counts produces a larger chi-square statistic and stronger evidence that the variables are associated.
Fisher’s exact test addresses the same general question without relying on the large-sample chi-square approximation, making it useful when expected counts are small.
10.2 Association between Treatment and clinical response
The following table compares treatment assignment with clinical response at 12 weeks.
resp_tab <- table(Treatment=dat$treatment_arm,
Response=dat$clinical_response_12w)
resp_tab
#> Response
#> Treatment 0 1
#> Control 88 32
#> Drug_A 68 52
#> Drug_B 50 70Under the null hypothesis,
\[ H_0:\text{clinical response is independent of treatment}. \]
Before selecting the test, examine the expected counts.
resp_chi <- suppressWarnings(chisq.test(resp_tab, correct=FALSE))
round(resp_chi$expected,2)
#> Response
#> Treatment 0 1
#> Control 68.67 51.33
#> Drug_A 68.67 51.33
#> Drug_B 68.67 51.33Expected counts are calculated under the assumption that treatment and clinical response are unrelated. They determine whether the chi-square approximation is sufficiently reliable.
10.3 Automated chi-square or Fisher selection
The following block checks the expected counts and performs the appropriate test.
expected <- resp_chi$expected
chi_ok <- all(expected >= 5)
# OR
chi_ok <- all(expected>=1) && mean(expected<5)<=.20
if(chi_ok){
selected_test <- "Pearson chi-square test"
fit <- resp_chi
} else {
selected_test <- "Fisher exact test"
fit <- fisher.test(resp_tab)
}
tibble(
test=selected_test,
minimum_expected=min(expected),
percent_expected_below_5=100*mean(expected<5),
p_value=fit$p.value
) |> kbl(digits=3, caption="Selected test for treatment and clinical response")| test | minimum_expected | percent_expected_below_5 | p_value |
|---|---|---|---|
| Pearson chi-square test | 51.33 | 0 | 0 |
The decision is straightforward:
- if expected counts are adequate, use Pearson’s chi-square test;
- if expected counts are sparse, use Fisher’s exact test.
A small p-value indicates evidence that clinical response differs across treatment groups. The global test does not show which treatment differs from Control or how large the difference is, so effect estimates should also be reported.
10.4 Risk differences and risk ratios
For a binary treatment outcome, risks and their contrasts provide a direct measure of treatment effect.
resp_effects <- bind_rows(
risk_contrast(dat,"clinical_response_12w","Drug_A"),
risk_contrast(dat,"clinical_response_12w","Drug_B")
) |> mutate(across(c(risk_exposed,risk_reference,risk_difference,rd_low,rd_high), ~100*.x))
kbl(resp_effects, digits=2,
caption="Clinical-response effects versus Control; risks and risk differences are percentage points")| comparison | risk_exposed | risk_reference | risk_difference | rd_low | rd_high | risk_ratio | rr_low | rr_high |
|---|---|---|---|---|---|---|---|---|
| Drug_A vs Control | 43.33 | 26.67 | 16.67 | 4.78 | 28.55 | 1.62 | 1.13 | 2.33 |
| Drug_B vs Control | 58.33 | 26.67 | 31.67 | 19.82 | 43.52 | 2.19 | 1.57 | 3.05 |
The risk difference measures the absolute difference in response probability:
\[ RD=P(Y=1\mid\text{treatment})-P(Y=1\mid\text{Control}), \]
whereas the risk ratio compares the two probabilities proportionally:
\[ RR=\frac{P(Y=1\mid\text{treatment})} {P(Y=1\mid\text{Control})}. \]
The global chi-square or Fisher test provides evidence of association; the risk difference and risk ratio describe the size of the treatment effect.