FisherTest

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