上へ

カイ2乗検定

分割表の分析

 

分割表(contingency table)のカイ二乗検定を行うC++Builderコンソール・アプリケーションを作成した。これらのカイ2乗検定については<岡本安晴「データ分析のための統計学入門」2009、おうふう;第3章:検定>において、「分布の違いの検定」および「カテゴリの独立性の検定」の2つの場合が説明されているが、計算式は同じなので両方の場合に共通に用いることができるプログラムとして用意した。プログラムのファイルを圧縮ファイルchi2_xtable.zipとしてまとめたものは、その圧縮ファイル名chi2_xtable.zipをクリックしてダウンロードすることができる。ダウンロードした圧縮ファイルchi2_xtable.zipは、マウスの右ボタンクリックで表示されるメニュから「解凍」あるいは「展開」などを選ぶと解凍できる。解凍したプログラムファイルはC++Builder2010で開くことができる。

 入力データとしての分割表は次のような形式でテキストファイルとして用意して、プログラムのファイル(*.cppファイルなど)と同じフォルダに置いておく。下記の入力データ例は圧縮ファイルchi2_xtable.zipDataCk.txtとして収めてある。

 

========================================

 

/             行数       列数

              3                          6

              28          32          24          30          32          33

              26          29          26          26          22          28

              18          19          32          33          26          32

             

========================================

 

プログラムを実行すると、図1のコンソールウィンドウが表示される。

図1

 

プログラムの実行時のカレントディレクトリは、プログラムファイルの置かれているフォルダの1つ下のDebugフォルダであるので、プログラムファイルの置かれているフォルダと同じフォルダにデータファイルがあるときは、ファイル名の先頭に「..\」を付ける(図2)。計算結果の出力用ファイルもプログラムファイルの置かれているフォルダと同じところに出力するときはファイル名の先頭に「..\」を付ける(図2)。

図2

 

出力ファイル名の設定後、Enterキーを押すと計算が始まり、計算が終了すると図3の画面になる。

図3

 

何か文字キーを1つ押してからEnterキーを押すとプログラムは終了する。終了後、出力ファイルを開くと以下のように出力が行われていることがわかる。

 

==========================================

Input data file = ..\DataCk.txt

 

 

Data...

        28        32        24        30        32        33      sum(179)

        26        29        26        26        22        28      sum(157)

        18        19        32        33        26        32      sum(160)

sum

        72        80        82        89        80        93       496

 

Chi-squared = 8.61129     df = 10

 

===========================================

 

読み込まれた入力データの出力とともに、分割表に対するカイ2乗の値と自由度が出力されている。カイ2乗の値とその自由度に対するカイ2乗分布の累積確率とp値を求めるC++Builderプログラムはウェブサイト<カイ2乗分布>に用意した。

分割表のカイ2乗検定を行うプログラムでは、テキストファイルの読み込みは1行単位で行っている。このテキストを1行単位で読み込むためのクラス型がRdTxtLnであり、ヘッダーファイルmyLib.hには以下のように宣言されている。

 

================================================

               //

               //   テキストファイルから1行ごとに読み込むためのクラス型

               //

               class RdTxtLn {

                             public:

                               FILE * fin;

                               int iCond;  //  0: OK,  1: End of File,  9: Open Error, -1: Not initialized

                               string s;

 

                               RdTxtLn(): iCond(-1) {}

 

                               //

                               //    nmに読み込み用テキストファイル名を設定

                               //    オープンに成功すればiCondの値は0が設定される。

                               //

                               RdTxtLn( string nm );

 

                               virtual ~RdTxtLn();

 

                               void open( string nm );

  

                               void close();

 

                               //

                               //   メソッドreadlineを1回呼び出すと

                               //   ファイルから1行分がstrに読み込まれる

                               //

                               int readline( string & str );

               };

===========================================

 

メソッドreadln(string & str)によって、テキストデータ1行分がstrに読み込まれる。1行分のデータから空白(半角文字)あるいはタブによって区切られた部分文字列を取り出す関数としてヘッダーファイルmyLib.hに宣言されている関数SeekStr( string s, int ipos )を用いている。SeekStrsipos番目(0から数える)の部分文字列を返す。文字列が数値を表わすとき、「12345」とか「1.2345」など、その文字列の表わす数値を返す関数としてヘッダーファイルmyLib.hに宣言されているstrToInt( string s )とかstrToDouble( string s )を用いている。strTpIntは整数値を返すものであり、strToDoubleは実数値を返すものである。

 以上のクラス型および関数を用いて、プログラムでは以下のようにして分割表に対するカイ2検定のための計算を行っている。

 

===============================================

              //

              //    入力用テキストファイル名と入力ストリームの設定

              //

              cout << "Input data file = ";

              string fin_nm;

              cin >> fin_nm;

              RdTxtLn rd( fin_nm );

              if (rd.iCond != 0){

                            cout << "To open input data file failed." << endl;

                            return 9;

              }

 

              //

              //    出力用テキストファイル名と出力ストリームの設定

              //

              cout << "Output file = ";

              char  fout_nm[200];

              cin >> fout_nm;

              ofstream fout( fout_nm );

              fout << "Input data file = " << fin_nm << endl << endl;

 

              //

              //    入力ファイルの先頭部分("/"で始まる行まで)の読み飛ばし

              //

              string s;

              int ick = 0;

              do{

                            rd.readline( s );

                            if (s.size() > 0)

                                          if (s[0] == '/') ick = 1;

              }while( ick == 0 );

 

              rd.readline(s);

              int r = strToInt(SeekStr(s, 0));

              int c = strToInt(SeekStr(s, 1));

 

              double ** O, ** E;

              O = new double*[r];

              E = new double*[r];

              for (int i = 0; i < r; i++){

                            O[i] = new double[c];

                            E[i] = new double[c];

              }

 

 

               for (int i = 0; i < r; i++){

                             rd.readline(s);

                             for (int j = 0; j < c; j++)

                                           O[i][j] = strToDouble(SeekStr(s, j));

               }

 

               rd.close();

 

 

               double * R, * C;

               R = new double[r];

               for (int i = 0; i < r; i++){

                            R[i] = 0.0;

                            for (int j = 0; j < c; j++) R[i] += O[i][j];

               }

               C = new double[c];

               for (int j = 0; j < c; j++){

                            C[j] = 0.0;

                            for (int i = 0; i < r; i++) C[j] += O[i][j];

               }

 

               int N = 0;

               fout << endl << "Data..." << endl;

               for (int i = 0; i < r; i++){

                             for (int j = 0; j < c; j++)

                                          fout << right << setw(10) << O[i][j];

                             fout << right << setw(10) << "sum(" << R[i] << ")" << endl;

                             N += R[i];

               }

 

 

               fout << "sum" << endl;

               for (int j = 0; j < c; j++)

                            fout << right << setw(10) << C[j];

               fout << right << setw(10) << N << endl;

 

               for (int i = 0; i < r; i++)

                             for (int j = 0; j < c; j++)

                                           E[i][j] = R[i] * C[j] / N;

               double chi2 = 0.0;

               for (int i = 0; i < r; i++)

                             for (int j = 0; j < c; j++)

                                           chi2 += sqr(O[i][j] - E[i][j]) / E[i][j];

 

              fout << endl << "Chi-squared = " << chi2 << "     df = " << (r-1)*(c-1) << endl;

 

               fout.close();

 

               cout << endl << fout_nm << " was saved." << endl;

               cout << endl << "Press any char-key, then press enter-key." << endl;

               char ck_c[100];

               cin >> c;

 

               return 0;

===============================================

 

上へ