import numpy as np
import pytest
import scirs2
class TestFisherExactTest:
def test_fisher_basic_2x2(self):
table = np.array([[8, 2], [1, 5]], dtype=np.float64)
result = scirs2.fisher_exact_py(table)
assert "odds_ratio" in result
assert "pvalue" in result
assert result["odds_ratio"] > 0
assert 0 <= result["pvalue"] <= 1
def test_fisher_strong_association(self):
table = np.array([[10, 1], [1, 10]], dtype=np.float64)
result = scirs2.fisher_exact_py(table)
assert result["odds_ratio"] > 10
assert result["pvalue"] < 0.05
def test_fisher_no_association(self):
table = np.array([[10, 10], [10, 10]], dtype=np.float64)
result = scirs2.fisher_exact_py(table)
assert abs(result["odds_ratio"] - 1.0) < 0.01
assert result["pvalue"] > 0.5
def test_fisher_alternative_hypotheses(self):
table = np.array([[15, 5], [3, 12]], dtype=np.float64)
result_two = scirs2.fisher_exact_py(table, alternative="two-sided")
assert "pvalue" in result_two
result_less = scirs2.fisher_exact_py(table, alternative="less")
assert "pvalue" in result_less
result_greater = scirs2.fisher_exact_py(table, alternative="greater")
assert "pvalue" in result_greater
def test_fisher_perfect_association(self):
table = np.array([[10, 0], [0, 10]], dtype=np.float64)
result = scirs2.fisher_exact_py(table)
assert np.isinf(result["odds_ratio"]) or result["odds_ratio"] > 1000
assert result["pvalue"] < 0.05
def test_fisher_perfect_negative_association(self):
table = np.array([[0, 10], [10, 0]], dtype=np.float64)
result = scirs2.fisher_exact_py(table)
assert result["odds_ratio"] == 0.0 or result["odds_ratio"] < 0.01
assert result["pvalue"] < 0.05
def test_fisher_small_sample(self):
table = np.array([[2, 1], [1, 2]], dtype=np.float64)
result = scirs2.fisher_exact_py(table)
assert result["odds_ratio"] > 0
assert 0 <= result["pvalue"] <= 1
def test_fisher_large_sample(self):
table = np.array([[100, 50], [30, 120]], dtype=np.float64)
result = scirs2.fisher_exact_py(table)
assert result["odds_ratio"] > 0
assert 0 <= result["pvalue"] <= 1
def test_fisher_invalid_alternative(self):
table = np.array([[10, 5], [5, 10]], dtype=np.float64)
with pytest.raises(RuntimeError, match="alternative"):
scirs2.fisher_exact_py(table, alternative="invalid")
def test_fisher_wrong_dimensions(self):
table = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]], dtype=np.float64)
with pytest.raises(RuntimeError, match="2x2"):
scirs2.fisher_exact_py(table)
def test_fisher_negative_values(self):
table = np.array([[10, -5], [5, 10]], dtype=np.float64)
with pytest.raises(RuntimeError):
scirs2.fisher_exact_py(table)
class TestOddsRatio:
def test_odds_ratio_basic(self):
table = np.array([[10, 5], [3, 12]], dtype=np.float64)
or_val = scirs2.odds_ratio_py(table)
assert abs(or_val - 8.0) < 0.001
def test_odds_ratio_equals_one(self):
table = np.array([[10, 10], [10, 10]], dtype=np.float64)
or_val = scirs2.odds_ratio_py(table)
assert abs(or_val - 1.0) < 0.001
def test_odds_ratio_less_than_one(self):
table = np.array([[2, 10], [10, 5]], dtype=np.float64)
or_val = scirs2.odds_ratio_py(table)
assert abs(or_val - 0.1) < 0.001
assert or_val < 1.0
def test_odds_ratio_greater_than_one(self):
table = np.array([[20, 5], [5, 20]], dtype=np.float64)
or_val = scirs2.odds_ratio_py(table)
assert abs(or_val - 16.0) < 0.001
assert or_val > 1.0
def test_odds_ratio_zero_cell(self):
table1 = np.array([[10, 0], [5, 10]], dtype=np.float64)
or_val1 = scirs2.odds_ratio_py(table1)
assert np.isinf(or_val1) or or_val1 > 1000
table2 = np.array([[10, 5], [0, 10]], dtype=np.float64)
or_val2 = scirs2.odds_ratio_py(table2)
assert np.isinf(or_val2) or or_val2 > 1000
def test_odds_ratio_diagonal_zeros(self):
table = np.array([[0, 10], [10, 0]], dtype=np.float64)
or_val = scirs2.odds_ratio_py(table)
assert or_val == 0.0
def test_odds_ratio_large_values(self):
table = np.array([[1000, 500], [200, 2000]], dtype=np.float64)
or_val = scirs2.odds_ratio_py(table)
assert abs(or_val - 20.0) < 0.001
def test_odds_ratio_small_values(self):
table = np.array([[1, 2], [2, 1]], dtype=np.float64)
or_val = scirs2.odds_ratio_py(table)
assert abs(or_val - 0.25) < 0.001
def test_odds_ratio_wrong_dimensions(self):
table = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.float64)
with pytest.raises(RuntimeError):
scirs2.odds_ratio_py(table)
def test_odds_ratio_negative_values(self):
table = np.array([[10, -5], [5, 10]], dtype=np.float64)
with pytest.raises(RuntimeError):
scirs2.odds_ratio_py(table)
class TestRelativeRisk:
def test_relative_risk_basic(self):
table = np.array([[20, 80], [10, 90]], dtype=np.float64)
rr_val = scirs2.relative_risk_py(table)
assert abs(rr_val - 2.0) < 0.001
def test_relative_risk_equals_one(self):
table = np.array([[10, 40], [10, 40]], dtype=np.float64)
rr_val = scirs2.relative_risk_py(table)
assert abs(rr_val - 1.0) < 0.001
def test_relative_risk_less_than_one(self):
table = np.array([[5, 45], [20, 30]], dtype=np.float64)
rr_val = scirs2.relative_risk_py(table)
assert abs(rr_val - 0.25) < 0.001
assert rr_val < 1.0
def test_relative_risk_greater_than_one(self):
table = np.array([[30, 20], [10, 40]], dtype=np.float64)
rr_val = scirs2.relative_risk_py(table)
assert abs(rr_val - 3.0) < 0.001
assert rr_val > 1.0
def test_relative_risk_zero_unexposed(self):
table = np.array([[10, 40], [0, 50]], dtype=np.float64)
rr_val = scirs2.relative_risk_py(table)
assert np.isinf(rr_val) or rr_val > 1000
def test_relative_risk_zero_exposed(self):
table = np.array([[0, 50], [10, 40]], dtype=np.float64)
rr_val = scirs2.relative_risk_py(table)
assert rr_val == 0.0
def test_relative_risk_high_incidence(self):
table = np.array([[80, 20], [70, 30]], dtype=np.float64)
rr_val = scirs2.relative_risk_py(table)
assert abs(rr_val - 1.142857) < 0.001
def test_relative_risk_low_incidence(self):
table = np.array([[2, 98], [1, 99]], dtype=np.float64)
rr_val = scirs2.relative_risk_py(table)
assert abs(rr_val - 2.0) < 0.001
def test_relative_risk_large_sample(self):
table = np.array([[500, 4500], [200, 4800]], dtype=np.float64)
rr_val = scirs2.relative_risk_py(table)
assert abs(rr_val - 2.5) < 0.001
def test_relative_risk_small_sample(self):
table = np.array([[2, 3], [1, 4]], dtype=np.float64)
rr_val = scirs2.relative_risk_py(table)
assert abs(rr_val - 2.0) < 0.001
def test_relative_risk_wrong_dimensions(self):
table = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.float64)
with pytest.raises(RuntimeError):
scirs2.relative_risk_py(table)
def test_relative_risk_negative_values(self):
table = np.array([[10, -5], [5, 10]], dtype=np.float64)
with pytest.raises(RuntimeError):
scirs2.relative_risk_py(table)
class TestContingencyTableComparisons:
def test_odds_ratio_vs_relative_risk(self):
table = np.array([[5, 95], [2, 98]], dtype=np.float64)
or_val = scirs2.odds_ratio_py(table)
rr_val = scirs2.relative_risk_py(table)
assert abs(or_val - rr_val) < 0.5
def test_common_disease_or_rr_divergence(self):
table = np.array([[60, 40], [30, 70]], dtype=np.float64)
or_val = scirs2.odds_ratio_py(table)
rr_val = scirs2.relative_risk_py(table)
assert or_val > rr_val
assert abs(or_val - 3.5) < 0.001
assert abs(rr_val - 2.0) < 0.001
def test_fisher_vs_chi2_small_sample(self):
table = np.array([[3, 1], [1, 3]], dtype=np.float64)
fisher_result = scirs2.fisher_exact_py(table)
assert "pvalue" in fisher_result
assert 0 <= fisher_result["pvalue"] <= 1
def test_consistency_of_measures(self):
table = np.array([[40, 10], [10, 40]], dtype=np.float64)
fisher_result = scirs2.fisher_exact_py(table)
or_val = scirs2.odds_ratio_py(table)
rr_val = scirs2.relative_risk_py(table)
assert abs(fisher_result["odds_ratio"] - or_val) < 0.001
assert or_val > rr_val
class TestContingencyEdgeCases:
def test_all_zeros(self):
table = np.array([[0, 0], [0, 0]], dtype=np.float64)
try:
fisher_result = scirs2.fisher_exact_py(table)
assert fisher_result["odds_ratio"] == 0.0 or np.isnan(fisher_result["odds_ratio"])
except RuntimeError:
pass
def test_single_nonzero(self):
table = np.array([[10, 0], [0, 0]], dtype=np.float64)
try:
fisher_result = scirs2.fisher_exact_py(table)
assert "pvalue" in fisher_result
except RuntimeError:
pass
def test_very_large_values(self):
table = np.array([[1e6, 1e5], [1e5, 1e6]], dtype=np.float64)
or_val = scirs2.odds_ratio_py(table)
rr_val = scirs2.relative_risk_py(table)
assert np.isfinite(or_val)
assert np.isfinite(rr_val)
def test_very_small_nonzero_values(self):
table = np.array([[0.001, 0.002], [0.002, 0.001]], dtype=np.float64)
or_val = scirs2.odds_ratio_py(table)
assert np.isfinite(or_val)
assert or_val > 0
if __name__ == "__main__":
pytest.main([__file__, "-v"])