Up

平均の差の検定

独立なサンプルの場合

 

果物屋さんA店からランダムにみかんをM個選び、別の果物屋さんB店からランダムにみかんをN個選んだとき、A店のみかんの平均の重さとB店のみかんの平均の重さから、A店とB店とではどちらの店の方がみかんの平均値が重いかの判断を行うとする。例えば、A店のみかんの重さが、206g、210g、208gで、B店のみかんの重さが216g、217g、213g、214gであれば、A店からの3個のみかんの重さの平均値は208gで、B店からの4個のみかんの重さの平均値は215gである。みかんの重さの平均値からの散らばり具合から見て、B店のみかんの方が重いと考えてよさそうである。

しかし、A店からのみかんの重さが、207g、227g、190gで、B店からのみかんの重さが、229g、234g、196g、201gのときは、A店のみかんの重さの平均値は208g、B店からのみかんの重さの平均値は215gで、平均値は先の例の場合と同じであるが、個々のみかんのおもさの散らばり具合が平均値の差と比較して大きいので、B店のみかんの方が重いと結論付けるのは難しい。

平均値の差の比較を行うときは、データの散らばり具合を考えて行わなければならない。

いま、2つの独立なサンプル(例えば、A店からのみかんとB店からのみかん)を、、・・・、、および、・・・、で表す。それぞれの平均値を

および

とおく。

データサンプルが正規分布をする母集団からのものであるという仮定のもとで、平均値およびは正規分布に従い、その差も正規分布に従うが、分散あるいは標準偏差はわからない。しかし、平均値に差がないという帰無仮説のもとで、平均値の差をその標準偏差の推定値で割ったもの(t 値)

t 分布という分布に従う。自由度はである。標準偏差の推定値は

で与えられている。

上のt値を求めるプログラム例を示す。次のプログラム1は、コンソール出力で、データはプログラムに直接書き込む方法をとっている。データの入力をファイルから行い、計算結果の出力もファイルに書き出すプログラム例は、このウェブサイトの後半で示す。

 

プログラム1

 

#include <iostream>

#include "Calc_t_valueIndep.h"

 

using namespace std;

using namespace calctvalueindep;

 

int main() {

//      double dataX[] = { 207, 227, 190 };

//      int nX = 3;

//      double dataY[] = { 229, 234, 196, 201 };

//      int nY = 4;

 

        double dataX[] = { 206, 210, 208 };

        int nX = 3;

        double dataY[] = { 216, 217, 213, 214 };

        int nY = 4;

 

        cout << "dataX =" << endl;

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

                cout << dataX[i] << endl;

        cout << endl << "dataY =" << endl;

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

                cout << dataY[i] << endl;

 

        double t, meanX, meanY, sdX, sdY;

        int df;

        calc_t_value_indep(dataX, nX, dataY, nY, t, df, meanX, meanY, sdX, sdY);

 

        cout << endl << "t = " << t << "   df = " << df << endl << endl;

        cout << "meanX = " << meanX << endl;

        cout << "sdX   = " << sdX << endl;

        cout << "meanY = " << meanY << endl;

        cout << "sdY   = " << sdY << endl;

 

        cout << endl << "何か文字キーを押してから、Enterキーを押す。" << endl;

        char ck;

        cin >> ck;

        return 0;

}

 

プログラム1を実行すると図1のように計算結果が画面に表示される。

 

図1

 

図1の値t=-4.83046 は、自由度5に対して十分に大きい値である。すなわち、A店の平均値とB店の平均値には差があるといえる。十分大きいという判断は有意水準を設定してt分布に基づいて行われるが、t分布については別のウェブサイトで説明している。

この統計的検定の考え方は<岡本安晴「データ分析のための統計学入門」おうふう>の第3章「検定」で詳しく説明している。

プログラム1では、t値を、次のように関数calc_t_value_indep を呼び出して求めている。

        calc_t_value_indep(dataX, nX, dataY, nY, t, df, meanX, meanY, sdX, sdY);

この関数は、ヘッダーファイルCalc_t_valueIndep.hおよびソースコードファイルCalc_t_valueIndep.hで以下のように宣言・定義されている。

 

ヘッダーファイル Calc_t_valueIndep.h

 

#ifndef CALC_T_VALUEINDEP_CK

#define CALC_T_VALUEINDEP_CK

 

namespace calctvalueindep {

 

 

        void calc_t_value_indep(double * X, int nX, double * Y, int nY, double & t, int & df,

                                                        double & meanX, double & meanY, double & sdX, double & sdY);

       

}

 

#endif

 

 

 

ソースコードファイル Calc_t_valueIndep.cpp

 

#include <cmath>

#include "Calc_t_valueIndep.h"

 

using namespace std;

 

namespace calctvalueindep {

 

        double sqr(double v) {

                return v * v;

        }

 

        void calc_t_value_indep(double * X, int nX, double * Y, int nY, double & t, int & df,

                double & meanX, double & meanY, double & sdX, double & sdY) {

                double sum = 0.0;

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

                        sum += X[i];

                meanX = sum / nX;

                sum = 0.0;

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

                        sum += Y[i];

                meanY = sum / nY;

 

                double sumXX = 0.0;

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

                        sumXX += sqr(X[i] - meanX);

                sdX = sqrt(sumXX / nX);

                double sumYY = 0.0;

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

                        sumYY += sqr(Y[i] - meanY);

                sdY = sqrt(sumYY / nY);

 

                df = nX + nY - 2;

                double ss = (sumXX + sumYY) / df;

                double ss_diff_mean = ss * (nX + nY) / double(nX * nY);

                t = (meanX - meanY) / sqrt(ss_diff_mean);

        }

 

}

 

プログラムのソースコードファイなどを圧縮ファイルt_test_indep.zipとしてまとめたものも用意した。クリックしてダウンロード展開すれば、Visual Studio 2015で開くことができる。

 

 

ファイル入出力によるプログラム例

ファイルの入出力によるプログラム例を以下に示す。

 

プログラム2

 

#include <iostream>

#include <fstream>

#include "extrctstr.h"

#include "Calc_t_valueIndep.h"

 

using namespace std;

using namespace extrctsubstr;

using namespace calctvalueindep;

 

int main() {

        double dataX[1000], dataY[1000];

        int nX, nY;

 

        cout << "Input file name = ";

        char fin_name[100];

        cin >> fin_name;

        FILE *fin;

        if (fopen_s(&fin, fin_name, "r") != 0) {

                cout << "Error in opening the input file..." << endl;

                cout << "Input file name = " << fin_name << endl;

                cout << "Press any character key, then Enter key." << endl;

                char ck;

                cin >> ck;

                return 0;

        }

 

        cout << "Output file name = ";

        char fout_name[100];

        cin >> fout_name;

        ofstream fout(fout_name);

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

 

        char cstr[1000];

        while (true) {

                fgets(cstr, 1000, fin);

                if (cstr[0] == '/') break;

        }

        ExtrctStr extrct;

        nX = 0;

        while (true) {

                fgets(cstr, 1000, fin);

                if (cstr[0] == '/') break;

                extrct.CheckStr(string(cstr));

                dataX[nX++] = atof(s_to_c(extrct.getStrR(0), cstr));

        }

        while (true) {

                fgets(cstr, 1000, fin);

                if (cstr[0] == '/') break;

        }

        nY = 0;

        while (true) {

                fgets(cstr, 1000, fin);

                if (cstr[0] == '/') break;

                extrct.CheckStr(string(cstr));

                dataY[nY++] = atof(s_to_c(extrct.getStrR(0), cstr));

        }

        fclose(fin);

       

        fout << "dataX =" << endl;

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

                fout << dataX[i] << endl;

        fout << endl << "dataY =" << endl;

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

                fout << dataY[i] << endl;

 

        double t, meanX, meanY, sdX, sdY;

        int df;

        calc_t_value_indep(dataX, nX, dataY, nY, t, df, meanX, meanY, sdX, sdY);

 

        fout << endl << "t = " << t << "   df = " << df << endl << endl;

        fout << "meanX = " << meanX << endl;

        fout << "sdX   = " << sdX << endl;

        fout << "meanY = " << meanY << endl;

        fout << "sdY   = " << sdY << endl;

 

        fout.close();

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

 

        cout << endl << "何か文字キーを押してから、Enterキーを押す。" << endl;

        char ck;

        cin >> ck;

        return 0;

}

 

入力データファイルは、図2.1のように用意されているものとする。

2.1

 

行の第1文字がスラッシュ / で始まる行によって、データが区切られている。このスラッシュで始まる行であるかどうかは、次のコード

                fgets(cstr, 1000, fin);

                if (cstr[0] == '/') break;

によって判定されている。関数 fgets Cの関数である。C++では、Cの機能も使える。読み込み文字数の最大値を行あたりの文字数より十分に大きくとっておけば、行単位の読み込みになる。

次のコード

                extrct.CheckStr(string(cstr));

                dataX[nX++] = atof(s_to_c(extrct.getStrR(0), cstr));

は、読み込んだ1行分のデータから、0番目の文字列を取り出して、その表す数値に変換するものである。オブジェクトextrctは、読み込んだ行から区切り文字で区切られた文字列を取り出すクラス型ExtrctStrのオブジェクトであり、次のように定義されている。

ExtrctStr extrct;

クラス型ExtrctStについては、ウェブサイトで説明している。

プログラム2を実行すると入力データファイル名の設定が求められる(図2.2)。

2.2

 

入力データファイル名を設定してEnterキーを押すと、次に出力用データファイル名の設定が求められる(図2.3)。

2.3

 

出力用ファイル名は、テキストファイルの名前として設定する(図2.4)。

2.4

 

出力用ファイル名を設定してEnterキーを押すと計算が始まる。計算が終了すると、出力用ファイルが保存されたメッセージが出力される。何か文字キーを押してEnterキーを押すと、プログラムの終了となる。

プログラムの終了後、出力用ファイルを開くと、図2.5のような内容になっている。

2.5

 

プログラム2のソースコードファイルなどを圧縮ファイルt_test_indep_f.zipとしてまとめた。クリック・ダウンロード・展開すれば、Visual Studio 2015で開くことが出来る。

 

 

参考文献

岡本安晴(2009)「データ分析のための統計学入門――統計学の考え方――」おうふう

 

Up