See http://en.wikipedia.org/wiki/Fisher's_exact_test
The test is used to examine the significance of the association between two variables in a 2 x 2 contingency table.
# Created By : Dr. Detlef Groth
# Created : Mon Mar 31 09:50:42 2008
# Last Modified : <080403.2128>
#
# based on code of http://www.perlmonks.org/?node_id=482919 tlm
# and Alan Miller's FORTRAN-implementation http://lib.stat.cmu.edu/apstat/245
#
##############################################################################
namespace eval fisher {
set Tolerance 1
while {[expr {1 + $Tolerance/2.0}] > 1} {
set Tolerance [expr {$Tolerance / 2.0}]
}
}
proc fisher::test {a b c d {alternative greater}} {
if {$alternative eq "less"} {
return [fisherExactTest $c $d $a $b]
} else {
return [fisherExactTest $a $b $c $d]
}
}
proc fisher::fisherExactTest {a b c d {ts false}} {
set test [expr $a*( $a + $b + $c + $d ) - ( $a + $b )*( $a + $c )]
if {$test < 0 && $ts} {
return 1
}
if { $test < 0 } {
if { $d < $a } {
set p_val [_fishers_exact $d $c $b $a $ts true]
} else {
set p_val [_fishers_exact $a $b $c $d $ts true]
}
} else {
if { $b < $c } {
set p_val [_fishers_exact $b $a $d $c $ts false]
} else {
set p_val [_fishers_exact $c $d $a $b $ts false]
}
}
return $p_val;
}
proc fisher::_fishers_exact {a b c d ts complement} {
foreach {aa bb cc dd} [list $a $b $c $d] { break }
set delta [_single_term $aa $bb $cc $dd]
set first $delta
set p_val 0
while {true} {
set p_val [expr {$p_val + $delta}]
if {$aa < 1} {
break
}
incr bb
incr cc
set delta [expr {$delta * ( 1.0* ( $aa * $dd )/( $bb * $cc ) )}]
incr aa -1
incr dd -1
}
if { $ts } {
if {$b < $c} {
set m $b
} else {
set m $c
}
set aa [expr $a+$m]
set bb [expr $b-$m]
set cc [expr $c-$m]
set dd [expr $d+$m]
set delta [_single_term $aa $bb $cc $dd]
set bound [expr {-1.0*$Tolerance}]
while { $bound <= [expr {( $first - $delta )/$first}] && $aa > $a } {
set p_val [expr {$p_val + $delta}];
incr bb
incr cc
set delta [expr {$delta * ( ( $aa * $dd )/( $bb * $cc ) )}]
incr aa -1
incr dd -1
}
} elseif { $complement } {
set p_val [expr {1 - $p_val + $first}]
}
return $p_val
}
proc fisher::_single_term {a b c d} {
set r1 [expr {$a + $b}]
set r2 [expr {$c + $d}]
set c1 [expr {$a + $c}]
set c2 [expr {$b + $d}]
set N [expr {$r1 + $r2}]
set s1 [expr {[_ln_fact $r1]+[_ln_fact $r2]+[_ln_fact $c1]+[_ln_fact $c2]-[_ln_fact $N]}]
set s2 [expr {[_ln_fact $a]+[_ln_fact $b]+[_ln_fact $c]+[_ln_fact $d]}]
return [expr {exp($s1-$s2)}]
}
proc fisher::_ln_fact {n} {
if {$n <=1} { return 0 }
return [_ln_gamm [incr n]]
}
proc fisher::_ln_gamm {z} {
# Reference: "Lanczos, C. 'A precision approximation
# of the gamma function', J. SIAM Numer. Anal., B, 1, 86-96, 1964."
# Translation of Alan Miller's FORTRAN-implementation
# See http://lib.stat.cmu.edu/apstat/245
set x 0
set x [expr {$x + 0.1659470187408462e-06/($z+7)}]
set x [expr {$x + 0.9934937113930748e-05/($z+6)}]
set x [expr {$x - 0.1385710331296526 /($z+5)}]
set x [expr {$x + 12.50734324009056 /($z+4)}]
set x [expr {$x - 176.6150291498386 /($z+3)}]
set x [expr {$x + 771.3234287757674 /($z+2)}]
set x [expr {$x - 1259.139216722289 /($z+1)}]
set x [expr {$x + 676.5203681218835 /($z)}]
set x [expr { $x + 0.9999999999995183}]
return [expr {log($x)-5.58106146679532777-$z+($z-0.5)*log($z+6.5)}]
}Test
(dgroth) 127 % fisher::test 2 10 15 3 0.9999845190186861 (dgroth) 128 % fisher::test 2 10 15 3 less 0.00046518094336290726 (dgroth) 129 % fisher::test 2 10 15 3 greater 0.9999845190186861 (dgroth) 130 % fisher::test 3 1 1 3 greater 0.2428571428571418
Or the solve the first example from http://mathworld.wolfram.com/FishersExactTest.html
(dgroth) 131 % fisher::test 5 1 0 4 greater 0.02380952380952382