BARNARD_EXACT
Overview
The BARNARD_EXACT function performs Barnard’s exact test on a 2×2 contingency table to examine the association between two categorical variables. Developed by George A. Barnard in 1945, this test is an unconditional exact test that provides a more powerful alternative to Fisher’s exact test when analyzing 2×2 tables. For implementation details, see the SciPy barnard_exact documentation.
Unlike Fisher’s exact test, which conditions on both row and column margins, Barnard’s test conditions only on the column totals, making it uniformly more powerful for comparing two binomial proportions. This makes Barnard’s test particularly suitable for clinical trials and other studies where one margin (e.g., treatment group sizes) is fixed by design but the other margin (e.g., outcomes) is random.
The test computes a Wald statistic to measure the difference between two proportions. When using pooled variance (the default), the statistic is calculated as:
T(X) = \frac{\hat{p}_1 - \hat{p}_2}{\sqrt{\hat{p}(1-\hat{p})\left(\frac{1}{c_1} + \frac{1}{c_2}\right)}}
where \hat{p}_1 and \hat{p}_2 are the sample proportions for each group, \hat{p} is the combined proportion, and c_1, c_2 are the column totals. When pooled is set to FALSE, unpooled variance is used instead, similar to Welch’s t-test approach.
The function supports three alternative hypotheses: "two-sided" (default) tests whether the proportions differ in either direction, "less" tests whether the first proportion is smaller, and "greater" tests whether the first proportion is larger. The returned p-value represents the maximum probability over all possible values of the nuisance parameter.
This example function is provided as-is without any representation of accuracy.
Excel Usage
=BARNARD_EXACT(table, barnard_alternative, pooled)
table(list[list], required): 2x2 contingency table with non-negative integer entries.barnard_alternative(str, optional, default: “two-sided”): Defines the alternative hypothesis.pooled(bool, optional, default: true): Whether to use pooled variance (True) or unpooled variance (False).
Returns (list[list]): 2D list [[statistic, p_value]], or error message string.
Examples
Example 1: Demo case 1
Inputs:
| table | |
|---|---|
| 7 | 12 |
| 8 | 3 |
Excel formula:
=BARNARD_EXACT({7,12;8,3})
Expected output:
| Result | |
|---|---|
| -1.894 | 0.06815 |
Example 2: Demo case 2
Inputs:
| table | barnard_alternative | |
|---|---|---|
| 7 | 12 | less |
| 8 | 3 |
Excel formula:
=BARNARD_EXACT({7,12;8,3}, "less")
Expected output:
| Result | |
|---|---|
| -1.894 | 0.03408 |
Example 3: Demo case 3
Inputs:
| table | barnard_alternative | |
|---|---|---|
| 7 | 12 | greater |
| 8 | 3 |
Excel formula:
=BARNARD_EXACT({7,12;8,3}, "greater")
Expected output:
| Result | |
|---|---|
| -1.894 | 1 |
Example 4: Demo case 4
Inputs:
| table | barnard_alternative | pooled | |
|---|---|---|---|
| 7 | 12 | two-sided | false |
| 8 | 3 |
Excel formula:
=BARNARD_EXACT({7,12;8,3}, "two-sided", FALSE)
Expected output:
| Result | |
|---|---|
| -2.019 | 0.06815 |
Python Code
from scipy.stats import barnard_exact as scipy_barnard_exact
def barnard_exact(table, barnard_alternative='two-sided', pooled=True):
"""
Perform Barnard's exact test on a 2x2 contingency table.
See: https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.barnard_exact.html
This example function is provided as-is without any representation of accuracy.
Args:
table (list[list]): 2x2 contingency table with non-negative integer entries.
barnard_alternative (str, optional): Defines the alternative hypothesis. Valid options: Two-sided, Less, Greater. Default is 'two-sided'.
pooled (bool, optional): Whether to use pooled variance (True) or unpooled variance (False). Default is True.
Returns:
list[list]: 2D list [[statistic, p_value]], or error message string.
"""
def to2d(x):
return [[x]] if not isinstance(x, list) else x
table = to2d(table)
# Validate table
if not isinstance(table, list) or len(table) != 2 or not all(isinstance(row, list) and len(row) == 2 for row in table):
return "Invalid input: table must be a 2x2 list of non-negative integers."
try:
arr = [[int(x) for x in row] for row in table]
except Exception:
return "Invalid input: table must contain integers."
if any(x < 0 for row in arr for x in row):
return "Invalid input: table must contain non-negative integers."
# Validate alternative
if barnard_alternative not in ['two-sided', 'less', 'greater']:
return "Invalid input: barnard_alternative must be 'two-sided', 'less', or 'greater'."
# Validate pooled
if not (pooled is True or pooled is False):
return "Invalid input: pooled must be True or False."
try:
res = scipy_barnard_exact(arr, alternative=barnard_alternative, pooled=pooled)
stat = float(res.statistic)
pval = float(res.pvalue)
# Disallow nan/inf
if any([s in [float('inf'), float('-inf')] or s != s for s in [stat, pval]]):
return "Invalid output: statistic or p-value is not finite."
return [[stat, pval]]
except Exception as e:
return f"scipy.stats.barnard_exact error: {e}"