Package: fast-fisher
CreditThis is heavily based on the work ofpainyeph.I basically added numba compilation and easier installation. Nevertheless, it makes a big difference speed-wise!Installationpipinstallgit+https://github.com/MrTomRod/fast-fisher# orpipinstallfast-fisher# from https://pypi.org/project/fast-fisher/UsageRecommended usage:fromfast_fisherimportfast_fisher_exact,odds_ratioa,b,c,d=15,32,25,46pvalue=fast_fisher_exact(a,b,c,d,alternative='two-sided')odds=odds_ratio(a,b,c,d)Alternative usages:fast_fisher_exact_compatibilityhas the same syntax asscipy.stats.fisher_exact.frommathimportisclosefromscipy.statsimportfisher_exactfromfast_fisherimportfast_fisher_exact_compatibilitytable=[[15,32],[25,46]]foralternativein['two-sided','less','greater']:odds_s,pval_s=fisher_exact(table,alternative)odds_f,pval_f=fast_fisher_exact_compatibility(table,alternative)assertodds_s==odds_fassertisclose(pval_s,pval_f)# not always true!Advanced Usagetest typep-value-log( p-value )-log10( p-value )left-tailedtest1l(a, b, c, d)ortest2l(a, a+b, a+c, a+b+c+d)mlnTest1l(a, b, c, d)ormlnTest2l(a, a+b, a+c, a+b+c+d)mlog10Test1l(a, b, c, d)ormlog10Test2l(a, a+b, a+c, a+b+c+d)right-tailedtest1r(a, b, c, d)ortest2r(a, a+b, a+c, a+b+c+d)mlnTest1r(a, b, c, d)ormlnTest2r(a, a+b, a+c, a+b+c+d)mlog10Test1r(a, b, c, d)ormlog10Test2r(a, a+b, a+c, a+b+c+d)two-tailedtest1t(a, b, c, d)ortest2t(a, a+b, a+c, a+b+c+d)mlnTest1t(a, b, c, d)ormlnTest2t(a, a+b, a+c, a+b+c+d)mlog10Test1t(a, b, c, d)ormlog10Test2t(a, a+b, a+c, a+b+c+d)alltest1(a, b, c, d)ortest2(a, a+b, a+c, a+b+c+d)mlnTest1(a, b, c, d)ormlnTest2(a, a+b, a+c, a+b+c+d)mlog10Test1(a, b, c, d)ormlog10Test2(a, a+b, a+c, a+b+c+d)SpeedComparison ofscipy.stats.fisher_exact(scipy docs)fast_fisher.fast_fisher_python(this library)fast_fisher.fast_fisher_compiled(this library, compiled using numba)fast_fisher.fast_fisher_cpython(this library, compiled using cython)fisher.pvalue(frombrentp/fishers_exact_test)Seebenchmark.py.abcdtest typescipyf_pythonf_compiledf_cythonbrentp8215left-tailed130 us3 us0 us0 us0 us8215right-tailed124 us3 us0 us0 us0 us8215two-tailed895 us6 us1 us1 us0 us1010010100left-tailed143 us10 us1 us1 us0 us1010010100right-tailed147 us12 us1 us1 us1 us1010010100two-tailed198 us18 us2 us2 us1 us10100010000100000left-tailed139 us11 us1 us1 us110 us100100010000100000right-tailed238 us78 us6 us6 us142 us100100010000100000two-tailed186 us137 us11 us11 us136 us100001001000100000left-tailed1010 us8 us1 us1 us1486 us100001001000100000right-tailed150 us6 us1 us1 us1495 us100001001000100000two-tailed63192 us768 us55 us58 us1459 us10000100001000010000left-tailed967 us338 us27 us28 us2808 us10000100001000010000right-tailed969 us344 us27 us28 us2820 us10000100001000010000two-tailed177 us689 us54 us61 us2952 usWhile numba seems be marginally faster than the cython, cython is a much better build dependency. Therefore, by default,
only the cython version is installed.Precisionfromnumpyimportlog10,isinffromscipy.statsimportfisher_exactfromfast_fisherimportfast_fisherscipy_fisher=lambdat:fisher_exact([[t[0],t[1]],[t[2],t[3]]])[1]print(f"{'contingency table':<30}{'fast -log10(pvalue)':<21}{'scipy -log10(pvalue)'}")forexponentinrange(0,16):table=(100,1,10,10**exponent)fast_mlog=fast_fisher.mlog10Test1t(*table)scipy_mlog=-log10(scipy_fisher(table))ifisinf(scipy_mlog):scipy_mlog='failed to compute'print(f"{str(table):<30}{fast_mlog:<21}{scipy_mlog}")contingency table              fast -log10(pvalue)   scipy -log10(pvalue)
(100, 1, 10, 1)                0.7268124553699258    0.7268124553698625
(100, 1, 10, 10)               7.831294376070296     7.831294376070258
(100, 1, 10, 100)              46.49556750272154     46.4955675027216
(100, 1, 10, 1000)             128.93472935802276    128.93472935802373
(100, 1, 10, 10000)            226.62104816785       226.62104816785057
(100, 1, 10, 100000)           326.3812661048001     failed to compute
(100, 1, 10, 1000000)          426.35719912501844    failed to compute
(100, 1, 10, 10000000)         526.3547915160074     failed to compute
(100, 1, 10, 100000000)        626.3545507841483     failed to compute
(100, 1, 10, 1000000000)       726.3545273226812     failed to compute
(100, 1, 10, 10000000000)      826.3545146294285     failed to compute
(100, 1, 10, 100000000000)     926.354158998833      failed to compute
(100, 1, 10, 1000000000000)    1026.3583095975994    failed to compute
(100, 1, 10, 10000000000000)   1126.3427388160835    failed to compute
(100, 1, 10, 100000000000000)  1226.447616894783     failed to compute
<input>:11: RuntimeWarning: divide by zero encountered in log10
Traceback (most recent call last):
  File "/usr/lib64/python3.10/code.py", line 90, in runcode
    exec(code, self.locals)
  File "<input>", line 10, in <module>
  File "fast_fisher/fast_fisher_cython.pyx", line 236, in fast_fisher.fast_fisher_cython.mlog10Test1t
    cpdef inline double mlog10Test1t(long long a, long long b, long long c, long long d) except *:
  File "fast_fisher/fast_fisher_cython.pyx", line 237, in fast_fisher.fast_fisher_cython.mlog10Test1t
    return mlnTest2t(a, a + b, a + c, a + b + c + d) / LN10
  File "fast_fisher/fast_fisher_cython.pyx", line 195, in fast_fisher.fast_fisher_cython.mlnTest2t
    raise OverflowError('the grand total of contingency table is too large')
OverflowError: the grand total of contingency table is too large