Skip to content

How to run Fisher’s exact test in SciPy (with Code)

The Short Answer (TL;DR)

Call scipy.stats.fisher_exact with a two-by-two table of nonnegative integer counts and an explicit alternative. For this default-method two-sided example, SciPy reports the sample odds ratio and sums fixed-margin table probabilities no greater than the observed table probability.

from scipy import stats
table = [[4, 9], [10, 3]]
print(stats.fisher_exact(table, alternative="two-sided", method=None))

Detailed Implementation

This example uses synthetic data and does not need a network connection. Install the recorded package versions in a separate Python environment before running it.

python -m pip install numpy==2.5.4 scipy==1.18.1
from pathlib import Path
import numpy as np
from scipy import stats

# Rows: two independent groups; columns: success and failure.
table = np.array([[4, 9], [10, 3]], dtype=int)
if table.shape != (2,2) or not np.issubdtype(table.dtype, np.integer):
    raise ValueError("This example requires a two-by-two table of integer counts.")
if np.any(table<0) or np.any(table.sum(axis=0)==0) or np.any(table.sum(axis=1)==0):
    raise ValueError("Use nonnegative counts with positive row and column totals.")
result = stats.fisher_exact(table, alternative="two-sided", method=None)
report = (
    f"Table (success, failure): {table.tolist()}\n"
    f"Row totals: {table.sum(axis=1).tolist()}\n"
    f"Column totals: {table.sum(axis=0).tolist()}\n"
    f"Sample odds ratio, first group versus second: {result.statistic:.6f}\n"
    f"Conditional exact two-sided p-value: {result.pvalue:.8g}\n"
)
print(report)
Path("fisher-exact-output.txt").write_text(report, encoding="utf-8")

The text reports row totals [13, 13], column totals [14, 12], and a sample odds ratio of 2/15, approximately 0.133333. Its exact two-sided p-value agrees with enumeration of all thirteen feasible fixed-margin tables using rational arithmetic.

How should the rows and columns be labelled?

The first row contains four successes and nine failures; the second contains ten successes and three failures. Every independent unit belongs to one group and contributes one outcome. State that orientation with the result, because swapping the two groups reverses the odds-ratio direction. Paired success/failure measurements need a method that retains their pairing.

Why is the two-sided probability not simply twice one tail?

For the conditional calculation, keep the observed row and column totals fixed and consider all compatible tables. SciPy includes tables whose null probability is no greater than the probability of the observed table. Other two-sided conventions can produce different values. The validation uses exact rational combinatorial probabilities to implement this probability ordering rather than assuming a doubled-tail shortcut.

What do the odds ratio and exact label mean?

With method=None and a two-by-two table, the reported statistic is the sample odds ratio ad/(bc), not a risk ratio or a conditional maximum-likelihood estimate. Zero cells can make this estimate zero or infinite; exact inference does not guarantee a finite effect estimate. The p-value describes a fixed-margin null calculation. It is not the probability that the null hypothesis is true, and small samples can still give imprecise effect estimates.

Mathematical Context

For the displayed table, the two row totals are thirteen, the success-column total is fourteen and the grand total is twenty-six. Conditional on those margins under an odds ratio of one, let A be the upper-left count. Its probability at k is choose(14,k) times choose(12,13-k), divided by choose(26,13). The feasible integers are one through thirteen. The checks enumerate their thirteen rational probabilities, confirm that they sum to one, and add those no larger than the probability at A=4. The sample odds ratio is (4 times 3)/(9 times 10)=2/15. Changing the row order reciprocates this estimate while leaving the two-sided conditional probability unchanged.

\[ P(A=k)=\frac{\binom{C_1}{k}\binom{N-C_1}{R_1-k}}{\binom{N}{R_1}} \]

Reproducibility

Executed with Python 3.12.14; numpy 2.5.4, scipy 1.18.1. Automated checks confirm the stated output properties.

Primary documentation

Related guides