BOSCHLOO_EXACT
Overview
The BOSCHLOO_EXACT function performs Boschloo’s exact test on a 2×2 contingency table to examine the association between two categorical variables. Introduced by R.D. Boschloo in 1970, this test is a uniformly more powerful alternative to Fisher’s exact test when analyzing 2×2 tables. For more information, see the SciPy documentation and the Wikipedia article on Boschloo’s test.
Boschloo’s test uses the p-value from Fisher’s exact test as its test statistic. The Boschloo p-value represents the probability under the null hypothesis of observing a Fisher’s exact p-value at least as extreme as the one computed from the observed data. This two-stage approach provides increased statistical power while maintaining exact validity.
The test evaluates whether two binomial proportions p_1 and p_2 (representing success rates in each column of the contingency table) are equal. Three alternative hypotheses are supported:
- Two-sided (H_1: p_1 \neq p_2): Tests whether the proportions differ in either direction
- Less (H_1: p_1 < p_2): Tests whether the first proportion is smaller
- Greater (H_1: p_1 > p_2): Tests whether the first proportion is larger
The function returns two values: the statistic (the p-value from Fisher’s exact test) and the p-value (Boschloo’s test p-value). A small p-value (typically < 0.05) suggests evidence to reject the null hypothesis of equal proportions. This implementation uses SciPy’s boschloo_exact function from the scipy.stats module.
This example function is provided as-is without any representation of accuracy.
Excel Usage
=BOSCHLOO_EXACT(table, bex_alternative)
table(list[list], required): 2x2 contingency table with non-negative integer entries.bex_alternative(str, optional, default: “two-sided”): Defines the alternative hypothesis for the test.
Returns (list[list]): 2D list [[statistic, p_value]], or error message string.
Examples
Example 1: Two-sided test with moderate counts
Inputs:
| table | bex_alternative | |
|---|---|---|
| 12 | 5 | two-sided |
| 7 | 9 |
Excel formula:
=BOSCHLOO_EXACT({12,5;7,9}, "two-sided")
Expected output:
| Result | |
|---|---|
| 0.1137 | 0.1333 |
Example 2: Greater alternative with larger sample
Inputs:
| table | bex_alternative | |
|---|---|---|
| 74 | 31 | greater |
| 43 | 32 |
Excel formula:
=BOSCHLOO_EXACT({74,31;43,32}, "greater")
Expected output:
| Result | |
|---|---|
| 0.0483 | 0.0356 |
Example 3: Less alternative hypothesis test
Inputs:
| table | bex_alternative | |
|---|---|---|
| 10 | 20 | less |
| 15 | 25 |
Excel formula:
=BOSCHLOO_EXACT({10,20;15,25}, "less")
Expected output:
| Result | |
|---|---|
| 0.4585 | 0.3719 |
Example 4: Two-sided test with small counts
Inputs:
| table | bex_alternative | |
|---|---|---|
| 8 | 2 | two-sided |
| 1 | 5 |
Excel formula:
=BOSCHLOO_EXACT({8,2;1,5}, "two-sided")
Expected output:
| Result | |
|---|---|
| 0.0245 | 0.0244 |
Python Code
from scipy.stats import boschloo_exact as scipy_boschloo_exact
import math
def boschloo_exact(table, bex_alternative='two-sided'):
"""
Perform Boschloo's exact test on a 2x2 contingency table.
See: https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.boschloo_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.
bex_alternative (str, optional): Defines the alternative hypothesis for the test. Valid options: Two-sided, Less, Greater. Default is 'two-sided'.
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 structure
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."
# Validate table values are integers
try:
arr = [[int(x) for x in row] for row in table]
except (TypeError, ValueError):
return "Invalid input: table must contain integers."
# Validate non-negative values
if any(x < 0 for row in arr for x in row):
return "Invalid input: table must contain non-negative integers."
# Validate alternative parameter
if bex_alternative not in ['two-sided', 'less', 'greater']:
return "Invalid input: bex_alternative must be 'two-sided', 'less', or 'greater'."
try:
res = scipy_boschloo_exact(arr, alternative=bex_alternative)
stat = float(res.statistic)
pval = float(res.pvalue)
# Check for nan or inf values
if math.isnan(stat) or math.isnan(pval) or math.isinf(stat) or math.isinf(pval):
return "Invalid result: nan or inf encountered."
return [[stat, pval]]
except Exception as e:
return f"scipy.stats.boschloo_exact error: {e}"