超幾何分布を perl で計算

September 15, 2006


用意するもの

やってみよう

100個のボールのうち赤いものが20個あって、 100個の中からランダムに12個取り出した時に、 12個のうち赤いものがx個になる確率を計算する。 xが0から12までを自動的に計算する。太字のところを入力する。 出力の1カラム目がx、2カラム目が赤いボールがx個になる確率、 3カラム目が確率を上から足していった値である。ここで、 色以外の属性から赤いボールをできるだけ集めるような クラスタリングの手法を考えるとき、その優劣を定量的に表すには p value を使うのが一般的である。ここでは、クラスタリングの結果赤いボールが x 個拾い出せたとするとき、1から x に対応する第3カラム目の値を引けば p value になる。 12個取り出したときに赤いボールがない確率は約26%、5個入ってる確率は 0.1 % である。あるクラスタリングの手法で赤いボールが3個拾い出せたとき、 その p value は 0.0275 で、かなり良いと判断できる。 参考文献:Z. Tavazoie, Nature Genetics (1999)

スクリプトの中身

超幾何分布の計算には、nCr(組み合わせ)の計算がたくさん出てくる。 母集団の要素数が多くなると階乗がオーバーフローで計算できなくなる。 ので、ガンマ関数の対数を使って計算する。 参考文献:奥村晴彦著、C言語による最新アルゴリズム事典、技術評論社

while (<>) {
    ($N, $p, $x) = split;
    $s = 0;
    for ($i = 0; $i <= $x; $i++) {
        if ($i > $p) { last; }
        @x = (0, $p+1, $i+1, $N-$p+1, $x-$i+1, $N+1, $x+1);
        $e = &loggamma($x[1]) - &loggamma($x[2]) - &loggamma($x[1]-$x[2]+1)
           + &loggamma($x[3]) - &loggamma($x[4]) - &loggamma($x[3]-$x[4]+1)
           - &loggamma($x[5]) + &loggamma($x[6]) + &loggamma($x[5]-$x[6]+1);
        $e = exp($e);
        $s += $e;
        print "$i\t$e\t$s\n";
    }
}

$PI     = 3.14159265358979324;

sub loggamma
{
    local($x) = @_;
    local($v, $w, @B, $LOG2PI, $N);
    $LOG2PI = 1.83787706640934548;
    $N      = 8;
    @B = (1, -0.5, 0.16666666666666666666, 0, -0.03333333333333333333, 0,
          0.02380952380952380952, 0, -0.03333333333333333333, 0,
          0.07575757575757575757, 0, -0.25311355311355311355, 0,
          1.16666666666666666666, 0, -7.09215686274509803921);

    $v = 1;
    while ($x < $N) { $v *= $x; $x++; }
    $w = 1 / ($x * $x);
    (((((((($B[16]/240)  * $w + ($B[14]/182)) * $w + ($B[12]/121)) * $w
         + ($B[10]/ 90)) * $w + ($B[ 8]/ 56)) * $w + ($B[ 6]/ 30)) * $w
         + ($B[ 4]/ 12)) * $w + ($B[ 2]/  2)) / $x
         + 0.5 * $LOG2PI - log($v) - $x + ($x - 0.5) * log($x);
}