Fisher’s Exact Test in R: Association in Small Contingency Tables
The exact alternative to chi-square when cell counts are small — test association in 2×2 and larger tables, with the odds ratio
Biostatistics
Learn to run Fisher’s exact test in R — the exact alternative to the chi-square test of independence when expected cell counts are small (< 5). Test association in a 2×2 table (with the odds ratio and its confidence interval) or a larger R×C table, with base fisher.test() and rstatix fisher_test().
Published
June 23, 2026
Modified
July 7, 2026
TipKey takeaways
Fisher’s exact test tests whether two categorical variables are associated — like the chi-square test — but it is exact, so it is correct when cell counts are small (expected counts < 5) and chi-square is not.
Run it with base fisher.test() or rstatix::fisher_test() on the contingency table.
For a 2×2 table it also returns the odds ratio and its confidence interval — a built-in effect size.
For large R×C tables it can be slow; add simulate.p.value = TRUE for a Monte-Carlo p-value.
Use it whenever a chi-square’s expected-count assumption fails; otherwise either gives the same answer.
Introduction
Fisher’s exact test analyses a contingency table to test whether two categorical variables are associated — the same question as the chi-square test of independence. The difference: chi-square relies on a large-sample approximation that breaks down when cell counts are small, whereas Fisher’s test computes the exact probability of the observed table (and more extreme ones) under independence. So it is the test to reach for with small counts.
This lesson runs it on a small 2×2 table (where it also gives an odds ratio) and a larger R×C table, with base R and rstatix.
Note
Fisher’s exact test hypotheses.H₀: the row and column variables are independent. Hₐ: they are associated. For a 2×2 table this is equivalent to testing whether the odds ratio = 1.
A 2×2 table: small-sample association + odds ratio
A small trial: of 10 patients on a drug, 9 were cured; of 10 on placebo, 3 were cured. With counts this small, chi-square is unreliable — use Fisher:
outcome
treatment Cured NotCured
Drug 9 1
Placebo 3 7
fisher.test(tx)
Fisher's Exact Test for Count Data
data: tx
p-value = 0.01977
alternative hypothesis: true odds ratio is not equal to 1
95 percent confidence interval:
1.387127 1039.290968
sample estimates:
odds ratio
17.27587
The association is significant, p = 0.02. For a 2×2 table Fisher’s test also reports the odds ratio — here ≈ 17, meaning the odds of being cured are about 17× higher on the drug — with a wide 95% CI (reflecting the small sample). The CI excludes 1, consistent with the significant p-value.
Tip
One-sided tests. For a directional hypothesis (e.g. the drug is better, not just different), pass alternative = "greater" (or "less").
A larger R×C table
Fisher’s test is not limited to 2×2. Here is the classic Agresti job-satisfaction table (income × satisfaction, 4×4) with several small cells:
Fisher's Exact Test for Count Data
data: job
p-value = 0.7827
alternative hypothesis: two.sided
The p-value is 0.78 — not significant, so no association could be established between income and job satisfaction. (For a large R×C table the exact computation can be slow or memory-heavy; add simulate.p.value = TRUE to get a fast Monte-Carlo p-value instead.)
With rstatix
rstatix::fisher_test() is the pipe-friendly wrapper (it returns a tidy row; pass a table/as.table object):
Fisher’s exact test showed a significant association between treatment and cure (9/10 cured on the drug vs 3/10 on placebo), p = 0.02, odds ratio ≈ 17 (95% CI 1.4 to 1039). The wide interval reflects the small sample.
NoteThe math (optional)
For a 2×2 table with fixed margins, the probability of any particular table follows the hypergeometric distribution: \(P = \dfrac{\binom{a+b}{a}\binom{c+d}{c}}{\binom{n}{a+c}}\). Fisher’s test sums this probability over the observed table and all tables at least as extreme, giving an exact p-value with no large-sample approximation — which is why it stays valid when chi-square’s expected-count assumption fails.
Try it live
Run Fisher’s test on your own 2×2 counts and see the odds ratio. Edit the four numbers and re-run; the sandbox boots on first Run.
🟢 With an AI agent
Ask Prova“my contingency table has small counts — run Fisher’s exact test instead of chi-square and give me the odds ratio” — it answers with code you can run on your own table. The runtime is the judge.Ask Prova →
Common issues
Your R×C Fisher test is slow or errors on memory. Exact computation on a large table with big counts is expensive — add simulate.p.value = TRUE (and optionally B = 10000) for a Monte-Carlo p-value.
You wanted the odds ratio but have a larger table. Fisher’s odds ratio is defined only for 2×2 tables. For bigger tables, report the test p-value and use Cramér’s V for the strength.
You used chi-square with small counts anyway. If any expected count is below 5, the chi-square p-value is unreliable — Fisher’s exact test is the correct choice.
Frequently asked questions
NoteWhen should I use Fisher’s exact test instead of chi-square?
Use Fisher’s exact test when your contingency table has small cell counts — specifically when any expected count is below 5, where the chi-square approximation is unreliable. Fisher’s test computes an exact p-value, so it is valid for small samples. For large counts, the two tests agree.
NoteDoes Fisher’s exact test work for tables bigger than 2×2?
Yes — fisher.test() handles R×C tables. But the exact computation can be slow or memory-intensive for large tables with big counts; add simulate.p.value = TRUE for a Monte-Carlo p-value. The built-in odds ratio, however, is only reported for 2×2 tables.
NoteWhat is the odds ratio from Fisher’s test?
For a 2×2 table, fisher.test() returns the conditional maximum-likelihood odds ratio and its confidence interval — an effect size for the association. An odds ratio of 1 means no association; the CI excluding 1 corresponds to a significant test. For more on odds ratios see the dedicated odds ratio lesson.
Test your understanding
ImportantPractice
Run it. In the live cell, run Fisher’s exact test on the 2×2 table. Fill the blank. Is the association significant, and what is the odds ratio?
Conceptual. A reviewer asks why you used Fisher’s exact test instead of chi-square on your 2×2 table where one expected count was 3. What’s your answer?
NoteHint
Fill the blank with fisher.test. Look at the p-value and the odds-ratio estimate. For question 2, recall the chi-square assumption that fails with small expected counts.
NoteSolution
tab <-matrix(c(9, 1, 3, 7), nrow =2, byrow =TRUE)fisher.test(tab) #> p = 0.02, odds ratio ≈ 17 — significant association.
For question 2: chi-square relies on a large-sample approximation that requires every expected count to be at least ~5. With an expected count of 3, that assumption fails and the chi-square p-value would be unreliable, so Fisher’s exact test — which computes the exact probability — is the correct choice.
TipWhich test when?
Two categorical variables, small counts → Fisher’s exact test (this lesson).
You can now run Fisher’s exact test in R: fisher.test() (or rstatix::fisher_test()) on the contingency table gives an exact p-value for association — correct when cell counts are too small for chi-square — plus the odds ratio for a 2×2 table. Reach for it whenever the chi-square expected-count assumption fails, and use simulate.p.value = TRUE for large R×C tables.
Every result on this page was produced by the code shown, run at build time against a pinned R environment — edit any block and Run to reproduce it yourself.