平均値の差の検定
対応のあるデータ
表1は、ある訓練プログラムの前後におけるテスト得点の表である(仮想データ)。
表1 訓練プログラムの効果 |
||
参加者 |
訓練前のテストの点 |
訓練後のテストの点 |
A |
80 |
85 |
B |
90 |
88 |
C |
40 |
60 |
D |
55 |
70 |
E |
30 |
40 |
訓練前の点の散らばりと訓練後のテストの散らばりを比べても、訓練プログラムの効果があるかどうか判りにくい。しかし、参加者一人一人について、訓練前後のテスト得点の変化を見ると訓練後のテスト得点の上昇が見られるようである。それぞれの参加者の訓練前と訓練後のテスト得点のようにデータに対応のある場合は、その差の値に注目して分析が行われる。
参加者の訓練前のテスト得点を、訓練後のテスト得点をで表すとき、差に注目して分析を行う。人のデータに対して、差の平均
を求め、の標準偏差の推定値を求める。標準偏差が大きいとき、平均値はランダムに変動しやすいので、差がなくても平均値は偶然大きい値をとることもありうる。標準偏差との比をとることによって、平均値のランダムな変動を統制して分析を行うことが出来る。
の分散の推定値は、その不偏分散として次式
で与えられる。したがって、の平均値の分散の推定値は
で与えられる。
が平均0の正規分布に従うとき、その平均値と標準偏差の推定値との比
は自由度のt分布に従う。このことを用いて差の平均についての判断が行われるが、<岡本安晴「データ分析のための統計学入門」おうふう>「第3章 検定」などを参照されたい。
対応のあるデータの平均値の分析のためのt値を求めるプログラム例をプログラム1として以下に示す。プログラム1では、データはプログラムソースコード内に書かれていて、出力はコンソール画面に表示されるが、入出力をファイルによって行うプログラム例はこのウェブサイトの後半に示す。
プログラム1
#include <iostream>
#include "Calc_t_valueDep.h"
using namespace std;
using namespace calctvaluedep;
/******************************************
arry2
は、Calc_t_valueDep.h
で
次のように宣言されている。
struct
arry2 { double x, y; };
*******************************************/
int main() {
arry2
data[] = { {80,
85},
{90,
88},
{40,
60},
{55,
70},
{30,
40} };
int
n = sizeof(data) / sizeof(arry2);
cout
<< "Data..." << endl;
for
(int i = 0; i < n; i++)
cout
<< data[i].x << "
" << data[i].y << endl;
double
meanX, meanY, sd_diff;
double
t;
int
df;
calc_t_value_dep(data,
n, t, df, meanX, meanY, sd_diff);
cout
<< endl << "t = " << t << " df = " <<
df << endl;
cout
<< "meanX = "
<< meanX << endl;
cout
<< "meanY = "
<< meanY << endl;
cout
<< "sd_diff = " << sd_diff << endl;
cout
<< endl << "何か文字キーを押してから、Enterキーを押す。" << endl;
char
ck;
cin
>> ck;
return
0;
}
プログラム1では、t値は次の関数によって算出されている。
calc_t_value_dep(data, n, t, df,
meanX, meanY, sd_diff);
この関数calc_t_value_depは、Calc_t_valueDep.h およびCalc_t_valueDep.cppにおいて以下のように用意されている。
ヘッダーファイル Calc_t_valueDep.h
#ifndef CALC_T_VALUEDEP_CK
#define CALC_T_VALUEDEP_CK
namespace calctvaluedep {
struct
arry2 {
double
x, y;
};
void
calc_t_value_dep(arry2 * data, int n, double & t, int & df, double
& meanX, double & meanY, double & sd_diff);
}
#endif
ソースコードファイル Calc_t_valueDep.cpp
#include <cmath>
#include "Calc_t_valueDep.h"
using namespace std;
namespace calctvaluedep {
inline
double sqr(double v) {
return
v * v;
}
void
calc_t_value_dep(arry2 * data, int n, double & t, int & df, double
& meanX, double & meanY, double & sd_diff) {
double
sumx = 0.0, sumy = 0.0;
for
(int i = 0; i < n; i++) {
sumx
+= data[i].x;
sumy
+= data[i].y;
}
meanX
= sumx / n;
meanY
= sumy / n;
double
diff_means = meanX - meanY;
double
ssum_diff = 0.0;
for
(int i = 0; i < n; i++)
ssum_diff
+= sqr((data[i].x - data[i].y) - (meanX - meanY));
df
= n - 1;
double
ss = ssum_diff / df;
t
= diff_means / sqrt(ss / n);
sd_diff
= sqrt(ssum_diff / n);
}
}
プログラム1を実行すると図1のコンソール画面が表示される。
図1
プログラムのソースコードファイルなどは圧縮ファイルt_test_dep.zipとしてまとめた。クリック・ダウンロード・展開すれば、Visual Studio 2015で開くことができる。
データをファイルから読み込み、計算結果などをファイルに書き出すプログラム例を以下に示す。
プログラム2
#include <iostream>
#include <fstream>
#include "extrctstr.h"
#include "Calc_t_valueDep.h"
using namespace std;
using namespace extrctsubstr;
using namespace calctvaluedep;
/******************************************
arry2
は、Calc_t_valueDep.h
で
次のように宣言されている。
struct
arry2 { double x, y; };
*******************************************/
int main() {
arry2
data[1000];
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;
int
n = 0;
while
(true) {
fgets(cstr,
1000, fin);
if
(cstr[0] == '/') break;
extrct.CheckStr(string(cstr));
data[n].x
= atof(s_to_c(extrct.getStrR(0), 100, cstr));
data[n++].y
= atof(s_to_c(extrct.getStrR(1), 100, cstr));
}
fclose(fin);
fout
<< endl << "Data..." << endl;
for
(int i = 0; i < n; i++)
fout
<< data[i].x << "
" << data[i].y << endl;
double
meanX, meanY, sd_diff;
double
t;
int
df;
calc_t_value_dep(data,
n, t, df, meanX, meanY, sd_diff);
fout
<< endl << "t = " << t << " df = " <<
df << endl;
fout
<< "meanX = "
<< meanX << endl;
fout
<< "meanY = "
<< meanY << endl;
fout
<< "sd_diff = " << sd_diff << endl;
fout.close();
cout
<< endl << fout_name << " was saved." <<
endl;
cout
<< endl << "何か文字キーを押してから、Enterキーを押す。" << endl;
char
ck;
cin
>> ck;
return
0;
}
上のプログラムを実行すると、図2の画面が表示される。
図2
入力データファイル名を設定する(図3)。
図3
図3は、データファイルData.txtがプログラム ソースコード ファイルと同じフォルダにある場合の設定である(プログラム ソースコード ファイルのあるディレクトリがプログラム実行時のカレントディレクトリである)。
図4
入力データファイルは、図4の形式で用意されているものとする。すなわち、データの書かれている部分が、第1文字目がスラッシュ / で始まる行によって挟まれている。このスラッシュで始まる行を見つけるコードは以下のようになっている。
fgets(cstr,
1000, fin);
if
(cstr[0] == '/') break;
関数fgetsはCの関数で、行単位でデータを読み込むものであるが、読み込みは2番目の引数に設定された文字数によって制限されている。C++では、Cの機能も使える。
スラッシュで始まる行で挟まれた部分の各行に含まれるデータ x と y の値は、次のコードによって取り出されている。
int
n = 0;
while
(true) {
fgets(cstr,
1000, fin);
if
(cstr[0] == '/') break;
extrct.CheckStr(string(cstr));
data[n].x
= atof(s_to_c(extrct.getStrR(0), 100, cstr));
data[n++].y
= atof(s_to_c(extrct.getStrR(1), 100, cstr));
}
fclose(fin);
クラス型ExtrctStrのオブジェクト
ExtrctStr
extrct;
によって各行に含まれるデータが取り出されているが、クラス型ExtrctStrについては別のウェブサイトで説明している。
入力データファイル名の設定後(図3)、Enterキーを押すと、出力用テキストファイル名の設定が求められる(図5)。
図5
出力ファイル名の設定後、Enterキーを押すと、計算が始まり、計算結果などが出力ファイルに書き出され、出力ファイルが保存された旨のメッセージ表示される(図6)。
図6
適当な文字キーを押してから、Enterキーを押すと、プログラムの実行終了となる。
プログラムの実行終了後、出力ファイルを開くと、図7に示されているような内容が表示される。
図7
プログラム2で用いられている関数 s_to_c は、2番目の引数で指定されている数の文字数以上の文字を処理しようとすると例外が生成されるもので、以下のようなコードである。
char * s_to_c(string s, int max_nchar,
char * c) {
if
(s.length() > max_nchar - 1) {
char
cstr[] = "Too long string...";
throw
cstr;
}
for
(int i = 0; i < s.length(); i++)
c[i]
= s[i];
c[s.length()]
= 0;
return
c;
}
この関数を、次のプログラムのように、例外に対する処理を行わずに実行すると、図8のようなメッセージが表示される。
#include <iostream>
using namespace std;
char * s_to_c(string s, int max_nchar,
char * c) {
if
(s.length() > max_nchar - 1) {
char
cstr[] = "Too long string...";
throw
cstr;
}
for
(int i = 0; i < s.length(); i++)
c[i]
= s[i];
c[s.length()]
= 0;
return
c;
}
int main() {
string
strs[] = { "a", "abc", "abcde" };
char
cstr[1000];
// try{
for
(int i = 0; i < 3; i++) {
s_to_c(strs[i],
2, cstr);
cout
<< cstr << endl;
}
// }
// catch
(char * cs) {
// cout
<< "Exception..." << cs << endl;
// }
char
ck;
cin
>> ck;
return
0;
}
図8
しかし、次に示すように、例外に対する処理を記述しておくと、図9に示すように処理が進む。
#include <iostream>
using namespace std;
char * s_to_c(string s, int max_nchar,
char * c) {
if
(s.length() > max_nchar - 1) {
char
cstr[] = "Too long string...";
throw
cstr;
}
for
(int i = 0; i < s.length(); i++)
c[i]
= s[i];
c[s.length()]
= 0;
return
c;
}
int main() {
string
strs[] = { "a", "abc", "abcde" };
char
cstr[1000];
try{
for
(int i = 0; i < 3; i++) {
s_to_c(strs[i],
2, cstr);
cout
<< cstr << endl;
}
}
catch
(char * cs) {
cout
<< "Exception..." << cs << endl;
}
char
ck;
cin
>> ck;
return
0;
}
図9
なお、プログラム2のソースコードファイルなどは圧縮ファイルt_test_dep_f.zipとして用意したので、クリック・ダウンロード・展開すれば、Visual Studio 2015で開くことが出来る。