Python で Fisher's exact test を行えるようにした話
まず,目的の FORTRAN プログラムソースをダウンロードする
http://netlib.org/toms/ の中にある file: 643.gz
"unordered rxc contingency tables, Fisher's exact test"
展開してできる 643 というファイルを開き,
1965 行以降のテストデータを削除する。
1~31 行のメインプログラムを以下のサブルーチンプログラムで置き換える。
名前を fisher_lib.f90 として保存する。
C ALGORITHM 643, COLLECTED ALGORITHMS FROM ACM.
C THIS WORK PUBLISHED IN TRANSACTIONS ON MATHEMATICAL SOFTWARE,
C VOL. 19, NO. 4, DECEMBER, 1993, PP. 484-488.
c Driving program to read and
c process the data file
subroutine fisher_sub(nrow, ncol, expect, percnt, emin, table,
& prt, pre)
implicit none
integer i, j, ncol, nrow
double precision emin, expect, percnt, pre, prt, table(*)
intent(in) nrow, ncol, expect, percnt, emin, table
intent(out) prt, pre
c
external fexact
c output TABLE
c write (*,99998)
c do 20 i=1, nrow
c write (*,99997) (table(i+(j-1)*nrow),j=1,ncol)
c 20 continue
call fexact (nrow, ncol, table, nrow, expect, percnt, emin, prt,
& pre)
c99997 format (1x, 10f7.0)
c99998 format (/, 2x, 'The contingency table for this problem is:')
return
end
ターミナルで,以下を実行する(fisher_lib*.so ファイルができる)
$ f2py -m fisher_lib -c fisher_lib.f90
使用法は,fisher_lib をインポートして,fisher_lib.fisher_sub 関数を呼び出すだけ。
下の fisher() 関数のようなシュガーコートを介して呼び出すと楽ちん。
import numpy as np
import fisher_lib
def fisher(table, expect=0, percnt=0, emin=0):
nrow, ncol = np.array(table).shape
prt, pre = fisher_lib.fisher_sub(nrow, ncol, expect, percnt, emin, table)
return pre
集計表は,以下のように二重リストで与えればよい(numpy.array() でもよい)。
>>> import numpy as np
>>> import fisher_lib
>>> def fisher(table, expect=0, percnt=0, emin=0):
... nrow, ncol = np.array(table).shape
... prt, pre = fisher_lib.fisher_sub(nrow, ncol, expect, percnt, emin, table)
... return pre
...
>>> table = [[3, 5, 2, 5], [4, 1, 8, 3], [2, 1, 3, 4]]
>>> p = fisher(table)
>>> print(p)
0.26187809176685495
>>> p = fisher(np.array([[12, 2], [5, 11]]))
>>> print(p)
0.003913481855563419
R の fisher.test() で結果が正しいことを確かめる。
> options(digits=15)
> fisher.test(matrix(c(3, 5, 2, 5, 4, 1, 8, 3, 2, 1, 3, 4), byrow = TRUE, ncol = 4))$p.value
[1] 0.261878091766855
> fisher.test(matrix(c(12, 2, 5, 11), 2))$p.value
[1] 0.00391348185556345