概要
自作したデータで最小二乗法(単回帰分析)により近似直線を計算する。
x(横軸) : 身長 [cm]、y(縦軸) : 年収[万円] とする。
- データ (左列x : 身長 [cm], 右列y : 年収 [万円])
height-income.csv
162,420
165,470
168,540
170,550
171,565
172,570
175,580
178,590
181,600
183,625
- 身長と年収には正の相関があると聞いたことがある。確かに、特に男性だと体格の良い方が強くて頼もしく見えるから、結果的に仕事も上手くいきやすいのかもしれない...逆に小さいと舐められるケースもあるから、それで少し不利になるのかもしれない...
とはいえ、上記のデータほどの差はないだろうけど。 - 今回は可変長のデータを扱えるvectorを用いた。
push_back()関数で要素を付け足すことができる
計算過程
- フローチャート
- ソースコード(main関数)
int main(void){
int i;
// x(身長), y(年収)
// y_predictive(近似直線から予想される値), error(実際の値と予測値の誤差)
vector<float> x, y, y_predictive, error;
char inputFilename[30] = "height-income.csv";
char outputFilename[50] = "height-income-approximation.csv";
// Load a file
// ファイル読み込み
readFile(inputFilename, x, y);
// Calculate the approximate line
// 近似直線を求める
float a = calcSlope(x, y);
float b = calcIntercept(x, y);
// xとyの相関係数を求める
float corre = calcCorrelationCoefficient(x, y);
// 近似直線の傾き, 切片を表示する
printf("slope : a = %f\n", a);
printf("intercept : b = %f\n", b);
printf("correlation coefficience : %5.4f\n", corre);
printf("\n");
// 近似直線による予測値を配列に格納する
// 実際の値と予測値の誤差も配列の格納する
for(i = 0; i < x.size(); i++){
y_predictive.push_back(a*x[i]+b);
error.push_back(y[i] - (a*x[i]+b));
}
// x, y, y(予測値), 誤差 すべてを表示
for(i = 0; i < x.size(); i++){
printf("No%3d : x = %6.2f, y = %6.2f, y(predictive) = %6.2f, error = %6.2f\n", i+1, x[i], y[i], y_predictive[i], error[i]);
}
// ファイル書き出し
writeFile(outputFilename, x, y, y_predictive);
return(0);
}
- 近似直線の傾きの計算
float calcSlope(vector<float> &x, vector<float> &y){
float Xdecentration = calcDecentration(x);
float covariance = calcCovariance(x, y);
float slope = covariance/Xdecentration;
return slope;
}
- 近似直線の切片の計算
float calcIntercept(vector<float> &x, vector<float> &y){
float Xdecentration = calcDecentration(x);
float covariance = calcCovariance(x, y);
float Xaverage = calcAverage(x);
float Yaverage = calcAverage(y);
float intercept = Yaverage - (covariance/Xdecentration)*Xaverage;
return intercept;
}
- 傾きや切片を求めるために必要な関数一覧
平均値
float calcAverage(vector<float> &f){
float average = 0.0;
for(int i = 0; i < f.size(); i++){
average += f[i]/(float)(f.size());
}
return average;
}
2乗平均
float calcMeanSquare(vector<float> &f){
float meanSquare = 0.0;
for(int i = 0; i < f.size(); i++){
meanSquare += f[i]*f[i]/(float)(f.size());
}
return meanSquare;
}
分散 (varianceのほうが正しいと思うが..)
float calcDecentration(vector<float> &f){
float average = calcAverage(f);
float meanSquare = calcMeanSquare(f);
float decentration = meanSquare - average*average;
return decentration;
}
共分散
float calcCovariance(vector<float> &f, vector<float> &g){
float covariance = 0.0;
for(int i = 0; i < f.size(); i++){
covariance += f[i]*g[i]/(float)(f.size());
}
covariance -= calcAverage(f) * calcAverage(g);
return covariance;
}
計算結果
- 近似直線の式
y = 8.41x - 899
slope : a = 8.408483
intercept : b = -899.463379
correlation coefficience : 0.9197
