LoginSignup
5
4

More than 5 years have passed since last update.

単回帰分析(最小二乗法, C++, 動的配列クラス)

Last updated at Posted at 2018-09-23

C++による単回帰分析
x(横軸):身長、y(縦軸):体重で示された身2次元データの近似直線を最小二乗法を計算してみた。

clr.cpp
#include <iostream>
#include <vector>

#include <stdio.h>

using namespace std;

/* 平均・標準偏差を計算するStatisticsクラス */
class Statistics{

public:
    /* 平均を計算 */
    float average(vector<float> &f, int N){

        float ave = 0.0;

        for(int i = 0; i < N; i++) ave += f[i]/(float)N;

        return ave;

    }

    /* 2乗の平均を計算 */
    float average2(vector<float> &f, int N){

        float ave2 = 0.0;

        for(int i = 0; i < N; i++) ave2 += f[i]*f[i]/(float)N;

        return ave2;

    }

    /* 標準偏差を計算 */
    float variance(vector<float> &f, int N){

        float ave  = average(f, N);
        float ave2 = average2(f, N);

        float var = ave2 - ave*ave;

        return var;

    }

    /* 共分散を計算 */
    float covariance(vector<float> &f, vector<float> &g, int N){

        float cov = 0.0;

        for(int i = 0; i < N; i++) cov += f[i]*g[i]/(float)N;

        cov -= average(f, N) * average(g, N);

        return cov;

    }

};

クラスStatistics
平均(average関数)、2乗平均(average2関数)、分散(variance関数)、共分散(covariance関数)を計算

slr.cpp
/* Statisticsクラスを継承したクラス */
/* Least_squares_method (最小二乗法) クラスで
 * 近似直線を計算 */

class Least_squares_method : public Statistics{

public:
    /* 傾きを計算 */
    float slope(vector<float> &x, vector<float> &y, int N){

        float a = 0.0;
        float x_var = variance(x, N);
        float cov   = covariance(x, y, N);

        a = cov / x_var;

        return a;

    }

    /* 切片を計算 */
    float intercept(vector<float> &x, vector<float> &y, int N){

        float b = 0.0;
        float x_var = variance(x, N);
        float cov   = covariance(x, y, N);
        float x_ave = average(x, N);
        float y_ave = average(y, N);

        b = y_ave - (cov / x_var) * x_ave;

        return b;

    }

};

クラスStatisticsを継承したLeast_squares_methodクラスにより近似直線を計算

以下はファイル読み込み&書き込み

slr.cpp
/* ファイル読み込み */
int read_file(char *filename, vector<float> &x, vector<float> &y){

    int N = 0;
    float x_tmp = 0.0, y_tmp = 0.0;

    FILE *fp;

    fp = fopen(filename, "r");

    if(fp == NULL){
        cout << "can't open file ." << endl;
        exit(1);
    }

    while(true){

        if(fscanf(fp, "%f, %f\n", &x_tmp, &y_tmp) == EOF) break;
        x.push_back(x_tmp);
        y.push_back(y_tmp);
        N++;

    }

    fclose(fp);

    /* 要素数を返す */
    return(N);

}

/* ファイル書き出し */
int write_file(char *filename, vector<float> &x, vector<float> &y, vector<float> &y2, int N){

    FILE *fp;

    fp = fopen(filename, "w");

    if(fp == NULL){
        cout << "can't open file ." << endl;
        exit(1);
    }

    for(int i = 0; i < N; i++){
            fprintf(fp, "%f, %f, %f\n", x[i], y[i], y2[i]);

    fclose(fp);

    return(0);

}

main関数内で近似直線の傾きと切片を表示
ベクトルy2には近似直線を格納、write_file関数にてファイルに書き出される

slr.cpp
int main(void){

    vector<float> x, y, y2;
    char filename[30] = "height_weight.csv";

    /* ファイル読み込み */
    int N = read_file(filename, x, y);

    /* オブジェクトdata1を生成
     * data1オブジェクトにより2次元データの近似直線を計算 */
    Least_squares_method data1;

    /* 近似直線を計算 */
    float a = data1.slope(x, y, N);
    float b = data1.intercept(x, y, N);

    cout << "slope : " << a << endl;
    cout << "intercept : " << b << endl;

    /* 近似直線の配列に格納 */
    for(int i = 0; i < N; i++)
        y2.push_back(a * x[i] + b);

    char filename2[30] = "height_weight_slr.csv";

    /* 近似直線のデータを出力 */
    write_file(filename2, x, y, y2, N);

    return(0);

}

slr.png
元の2次元データ(青点)と近似直線(赤線)

5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4