単回帰分析
2変量のデータに対して回帰直線を当てはめるVisual C++/win32プログラムを作成した。なお、Win32コンソールアプリケーションの新規作成については<Win32コンソールアプリケーションの新規作成>で、C++の標準入出力(コンソール入出力)については<標準入出力cinとcout>で、C++におけるテキストファイル入出力については<テキストファイル入出力>で説明している。
プログラムの実行例を図1に示す。
図1
入力ファイルはプログラム・ソースコード・ファイル*.cppと同じフォルダに用意する。
入力データファイル設定のソースコードは以下のようになっている。
cout << "入力データファイル = ";
string ifnm;
cin >> ifnm;
ifstream fin( ifnm );
if ( ! fin){ // 入力ファイルの設定が失敗するとfinはfalseを返す。
cout << "入力ファイルエラー" << endl;
cout << "何か文字を入力して終了。" << endl;
string s;
cin >> s;
return 9;
}
入出力ストリーム・オブジェクトの変数finを条件式に用いると、ストリームに何かエラーがあるとfalseが返される。
入力ファイルは図2の形式で用意する。
図2
第1行目に基準値を置く。2行目からデータを1行に1組ずつ書く。最後のデータの次の行に基準値より小さい値を置いてデータの終わりであることを示す。データの終わりを示す基準値より小さい値が書かれた行の後は読み込みの対象とならないので、任意のコメントを書くことができる。
エディタでデータを新しく用意したときは、保存してからプログラムで読み込む。エディタの画面に打ち込んだだけでは、その内容はファイルに反映されない。
次のコードによってデータ数を数えている。
double c;
fin >> c; // 基準値の設定
int n = 0;
double v;
do{
fin >> v;
if (v < c) break; // 基準値より小さい値が読み込まれた。
fin >> v;
if (fin.fail()){
cout << endl << "データ読込エラー...n = " << n << endl;
cout << "何か文字を入力して終了" << endl;
string ck;
cin >> ck;
return 9;
}
n++; // データ数のカウント
}while(true);
fin.close(); // 入力ファイルを一旦閉じる
計算を終えて結果の書き出しが終了すると「何か文字を入力して終了」と表示されるので、適当な文字キーを押してEnterキーを押すとプログラムの終了となる。この部分は以下のコードによって処理されている。
fout.close();
cout << endl << "計算終了" << endl;
cout << "何か文字を入力して終了" << endl;
string ckstr;
cin >> ckstr;
return 0;
出力ファイルもファイル名を図1のように設定すると、プログラムソースコードファイル*.cppと同じフォルダ内に作成される。図2のデータでプログラムを実行したときの出力ファイルをVisualC++のエディタで開くと以下の内容になっている。
====================================
入力データファイル = Data.txt
データ...
1: x = 66 y = 72
2: x = 46 y = 41
3: x = 87 y = 93
4: x = 82 y = 82
5: x = 42 y = 41
6: x = 72 y = 64
7: x = 31 y = 27
8: x = 69 y = 68
9: x = 84 y = 79
10: x = 68 y = 67
11: x = 50 y = 57
12: x = 90 y = 90
13: x = 85 y = 87
14: x = 29 y = 29
15: x = 76 y = 75
16: x = 69 y = 66
17: x = 84 y = 78
18: x = 35 y = 31
Xの平均 = 64.7222
Xの分散 = 399.867
Xの標準偏差 = 19.9967
Xの不偏分散 = 423.389
Xの不偏分散の平方根 = 20.5764
Yの平均 = 63.7222
Yの分散 = 432.978
Yの標準偏差 = 20.8081
Yの不偏分散 = 458.448
Yの不偏分散の平方根 = 21.4114
共分散 = 408.034
相関係数 = 0.98063
a = 1.02042
b = -2.32185
Y = 1.02042 * X + -2.32185
決定係数 = 0.961636
非決定係数 = 0.0383645
====================================
回帰直線の係数aとbは次のコードで求めている(岡本安晴「データ分析のための統計学入門」おうふう)。
double a = (n * sRxRy - sx * sy) / (n * ssRx - sqr(sx));
double b = (ssRx * sy - sx * sRxRy) / (n * ssRx - sqr(sx));
sRxRyなどは以下のコードで算出されている。
double sx = 0.0, sy = 0.0;
for (int i = 0; i < n; i++){
sx += x[i];
sy += y[i];
}
double MeanX = sx / n;
double MeanY = sy / n;
double ssx = 0.0, ssy = 0.0, sxy = 0.0, sRxRy = 0.0, ssRx = 0.0;
for (int i = 0; i < n; i++){
ssx += sqr(x[i] - MeanX);
ssy += sqr(y[i] - MeanY);
sxy += (x[i] - MeanY) * (y[i] - MeanY);
sRxRy += x[i] * y[i];
ssRx += sqr(x[i]);
}
プログラムソースコード全体は以下の通りである。
==============================================
// regression.cpp : コンソール アプリケーションのエントリ ポイントを定義します。
//
#include "stdafx.h" // Visual C++が自動的に用意するもの
#include <iostream> // cin, coutの使用のため
#include <fstream> // ifstream, ifstreamの使用のため
#include <string> // string型の使用のため
using namespace std; // cin, coutなどが属する名前空間
double sqr( double v ){
return v * v;
}
int _tmain(int argc, _TCHAR* argv[])
{
//
// 入力データファイルの設定
//
cout << "入力データファイル = ";
string ifnm;
cin >> ifnm;
ifstream fin( ifnm );
if ( ! fin){ // 入力ファイルの設定が失敗するとfinはfalseを返す。
cout << "入力ファイルエラー" << endl;
cout << "何か文字を入力して終了。" << endl;
string s;
cin >> s;
return 9;
}
double c;
fin >> c; // 基準値の設定
int n = 0;
double v;
do{
fin >> v;
if (v < c) break; // 基準値より小さい値が読み込まれた。
fin >> v;
if (fin.fail()){
cout << endl << "データ読込エラー...n = " << n << endl;
cout << "何か文字を入力して終了" << endl;
string ck;
cin >> ck;
return 9;
}
n++; // データ数のカウント
}while(true);
fin.close(); // 入力ファイルを一旦閉じる
double * x, * y;
x = new double[n];
y = new double[n];
fin.open( ifnm ); // 入力ファイルをオープン
fin >> v;
for (int i = 0; i < n; i++){
fin >> x[i] >> y[i]; // データの読み込み
if (fin.fail()){
cout << endl << "データ読込エラー...i = " << i << endl;
cout << "何か文字を入力して終了" << endl;
string ck;
cin >> ck;
return 7;
}
}
//
// 出力ファイルの設定
//
cout << "出力ファイル名 = ";
string ofnm;
cin >> ofnm;
ofstream fout( ofnm );
//
// 入力データの書き出し
//
fout << "入力データファイル = " << ifnm << endl << endl;
fout << "データ..." << endl;
for (int i = 0; i < n; i++){
fout << i+1 << ": x = " << x[i] << " y = " << y[i] << endl;
}
double sx = 0.0, sy = 0.0;
for (int i = 0; i < n; i++){
sx += x[i];
sy += y[i];
}
double MeanX = sx / n;
double MeanY = sy / n;
double ssx = 0.0, ssy = 0.0, sxy = 0.0, sRxRy = 0.0, ssRx = 0.0;
for (int i = 0; i < n; i++){
ssx += sqr(x[i] - MeanX);
ssy += sqr(y[i] - MeanY);
sxy += (x[i] - MeanY) * (y[i] - MeanY);
sRxRy += x[i] * y[i];
ssRx += sqr(x[i]);
}
double SDX = ::sqrt(ssx / n);
double SDY = ::sqrt(ssy / n);
fout << endl;
fout << "Xの平均 = " << MeanX << endl;
fout << "Xの分散 = " << ssx/n << endl;
fout << "Xの標準偏差 = " << SDX << endl;
fout << "Xの不偏分散 = " << ssx/(n-1) << endl;
fout << "Xの不偏分散の平方根 = " << ::sqrt(ssx/(n-1)) << endl << endl;
fout << "Yの平均 = " << MeanY << endl;
fout << "Yの分散 = " << ssy/n << endl;
fout << "Yの標準偏差 = " << SDY << endl;
fout << "Yの不偏分散 = " << ssy/(n-1) << endl;
fout << "Yの不偏分散の平方根 = " << ::sqrt(ssy/(n-1)) << endl << endl;
//
// 共分散
//
double cov = sxy / n;
fout << "共分散 = " << cov << endl << endl;
//
// 相関係数
//
double r = sxy / ::sqrt(ssx * ssy);
fout << "相関係数 = " << r << endl << endl;
/*************************************
y = a * x + b;
**************************************/
double a = (n * sRxRy - sx * sy) / (n * ssRx - sqr(sx));
double b = (ssRx * sy - sx * sRxRy) / (n * ssRx - sqr(sx));
fout << "a = " << a << endl;
fout << "b = " << b << endl << endl;
fout << "Y = " << a << " * X" << " + " << b << endl << endl;
fout << "決定係数 = " << r*r << endl;
fout << "非決定係数 = " << 1.0 - r*r << endl;
fout.close();
cout << endl << "計算終了" << endl;
cout << "何か文字を入力して終了" << endl;
string ckstr;
cin >> ckstr;
return 0;
}
==============================================
プログラムソースコードファイルなどをまとめたものは圧縮ファイルregression.zipとして用意した。圧縮ファイル名regression.zipをクリックしてダウンロードすることが出来る。ダウンロードした圧縮ファイルregression.zipはマウスの右ボタンクリックで表示されるメニュから「展開」あるいは「解凍」を選ぶと解凍できる。解凍したファイルはVisual C++2010で開くことができる。
Visual C++プログラミングの入門書として<岡本安晴「大学生のための心理学VC++プログラミング入門」勁草書房>を上梓した。