C 言語で決定係数 (R²) を計算する方法

CBeginner
オンラインで実践に進む

はじめに

この実験では、C 言語で決定係数 (R²) を計算する方法を学びます。この実験は、以下の手順で構成されています。

まず、線形回帰を用いて予測された y の値を計算します。シンプルな線形回帰モデルに基づいて予測値を計算するプログラムを作成します。次に、説明変動と総変動を用いて R² の値を計算します。最後に、R² の値を出力します。

この実験は、決定係数の概念とその実装方法を C プログラミングで理解するための実践的なアプローチを提供します。これは、統計データ分析において貴重なスキルです。

回帰を用いた予測値 y の計算

このステップでは、C 言語を用いて線形回帰による予測値 y の計算方法を学びます。シンプルな線形回帰モデルに基づいて予測値を計算するプログラムを作成します。

まず、回帰計算用の C ファイルを作成しましょう。

cd ~/project
nano regression_prediction.c

次に、以下のコードを入力します。

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

// 予測値 y を計算する関数
void computePredictedY(double *x, double *y, int n, double slope, double intercept, double *predicted_y) {
    for (int i = 0; i < n; i++) {
        predicted_y[i] = slope * x[i] + intercept;
    }
}

int main() {
    // サンプルデータポイント
    double x[] = {1.0, 2.0, 3.0, 4.0, 5.0};
    double y[] = {2.0, 4.0, 5.0, 4.0, 5.0};
    int n = sizeof(x) / sizeof(x[0]);

    // デモンストレーションのため、事前に傾きと切片を定義
    double slope = 0.6;
    double intercept = 1.5;

    // 予測値 y を格納する配列
    double predicted_y[n];

    // 予測値 y を計算
    computePredictedY(x, y, n, slope, intercept, predicted_y);

    // 元の y 値と予測値 y 値を出力
    printf("元の y 値と予測値 y 値:\n");
    for (int i = 0; i < n; i++) {
        printf("X: %.1f, 元の Y: %.1f, 予測 Y: %.1f\n",
               x[i], y[i], predicted_y[i]);
    }

    return 0;
}

プログラムをコンパイルします。

gcc -o regression_prediction regression_prediction.c -lm

プログラムを実行します。

./regression_prediction

出力例:

元の y 値と予測値 y 値:
X: 1.0, 元の Y: 2.0, 予測 Y: 2.1
X: 2.0, 元の Y: 4.0, 予測 Y: 2.7
X: 3.0, 元の Y: 5.0, 予測 Y: 3.3
X: 4.0, 元の Y: 4.0, 予測 Y: 3.9
X: 5.0, 元の Y: 5.0, 予測 Y: 4.5

このコードの主な構成要素を解説します。

  1. computePredictedY() 関数は、線形回帰式 (y = mx + b) を用いて予測値 y を計算します。
  2. デモンストレーションのため、傾き (0.6) と切片 (1.5) を事前に定義しています。
  3. プログラムは、元の y 値と予測値 y 値を比較するために、両方の値を出力します。

説明変動/総変動を用いた R² の計算

このステップでは、これまでの回帰プログラムを拡張して、データに対する回帰モデルの適合度を測る決定係数 (R²) を計算します。

まず、既存の C ファイルを修正します。

cd ~/project
nano r_squared_calculation.c

以下の包括的なコードを入力します。

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

// 配列の平均を計算する関数
double calculateMean(double *arr, int n) {
    double sum = 0.0;
    for (int i = 0; i < n; i++) {
        sum += arr[i];
    }
    return sum / n;
}

// R-二乗を計算する関数
double computeRSquared(double *x, double *y, int n, double slope, double intercept) {
    // 予測値 y を計算
    double predicted_y[n];
    double total_variation = 0.0;
    double explained_variation = 0.0;

    // 実測値 y の平均を計算
    double y_mean = calculateMean(y, n);

    // 変動を計算
    for (int i = 0; i < n; i++) {
        // 予測値 y の値
        predicted_y[i] = slope * x[i] + intercept;

        // 総変動 (平均からの距離)
        total_variation += pow(y[i] - y_mean, 2);

        // 説明変動 (予測値からの距離)
        explained_variation += pow(y[i] - predicted_y[i], 2);
    }

    // R-二乗を計算
    return 1 - (explained_variation / total_variation);
}

int main() {
    // サンプルデータポイント
    double x[] = {1.0, 2.0, 3.0, 4.0, 5.0};
    double y[] = {2.0, 4.0, 5.0, 4.0, 5.0};
    int n = sizeof(x) / sizeof(x[0]);

    // デモンストレーションのため、事前に傾きと切片を定義
    double slope = 0.6;
    double intercept = 1.5;

    // R-二乗を計算して出力
    double r_squared = computeRSquared(x, y, n, slope, intercept);

    printf("回帰分析結果:\n");
    printf("傾き:%.2f\n", slope);
    printf("切片:%.2f\n", intercept);
    printf("R-二乗 (R²): %.4f\n", r_squared);

    return 0;
}

プログラムをコンパイルします。

gcc -o r_squared_calculation r_squared_calculation.c -lm

プログラムを実行します。

./r_squared_calculation

出力例:

回帰分析結果:
傾き: 0.60
切片: 1.50
R-二乗 (R²): 0.5600

R² 計算の重要な構成要素:

  1. calculateMean() は配列の平均を計算します。
  2. computeRSquared() は、式 1 - (説明変動 / 総変動) を用いて R² を計算します。
  3. 総変動は、実測値 y の平均からの散らばりを表します。
  4. 説明変動は、予測値と実測値の差の散らばりを表します。
  5. R² の値は 0 から 1 の範囲で、値が高いほどモデルの適合度が良いことを示します。

R² 値の出力

この最終ステップでは、ファイルからデータを読み込み、回帰パラメータを計算し、詳細な解釈とともに R² 値を出力する包括的なプログラムを作成します。

まず、サンプルデータファイルを作成します。

cd ~/project
nano regression_data.txt

サンプル回帰データを追加します。

1.0 2.0
2.0 4.0
3.0 5.0
4.0 4.0
5.0 5.0

次に、最終的な R² 計算プログラムを作成します。

nano r_squared_print.c

以下のコードを入力します。

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

// 線形回帰パラメータを計算する関数
void calculateRegressionParameters(double *x, double *y, int n,
                                   double *slope, double *intercept) {
    double sum_x = 0, sum_y = 0, sum_xy = 0, sum_x_squared = 0;

    for (int i = 0; i < n; i++) {
        sum_x += x[i];
        sum_y += y[i];
        sum_xy += x[i] * y[i];
        sum_x_squared += x[i] * x[i];
    }

    // 最小二乗法を用いて傾きと切片を計算
    *slope = (n * sum_xy - sum_x * sum_y) / (n * sum_x_squared - sum_x * sum_x);
    *intercept = (sum_y - *slope * sum_x) / n;
}

// R-二乗を計算する関数
double computeRSquared(double *x, double *y, int n, double slope, double intercept) {
    double total_variation = 0.0;
    double explained_variation = 0.0;
    double y_mean = 0.0;

    // y の平均を計算
    for (int i = 0; i < n; i++) {
        y_mean += y[i];
    }
    y_mean /= n;

    // 変動を計算
    for (int i = 0; i < n; i++) {
        total_variation += pow(y[i] - y_mean, 2);
        double predicted_y = slope * x[i] + intercept;
        explained_variation += pow(y[i] - predicted_y, 2);
    }

    // R-二乗を計算
    return 1 - (explained_variation / total_variation);
}

// R-二乗値を解釈する関数
void interpretRSquared(double r_squared) {
    printf("\nR² の解釈:\n");
    if (r_squared < 0.3) {
        printf("適合度が低いモデル:モデルは変動の 30% 未満を説明しています。\n");
    } else if (r_squared < 0.5) {
        printf("適合度が中程度のモデル:モデルは変動の 30-50% を説明しています。\n");
    } else if (r_squared < 0.7) {
        printf("適合度の良いモデル:モデルは変動の 50-70% を説明しています。\n");
    } else {
        printf("適合度の優れたモデル:モデルは変動の 70% 以上を説明しています。\n");
    }
}

int main() {
    FILE *file;
    int n = 0, max_lines = 100;
    double x[100], y[100];
    double slope, intercept, r_squared;

    // データファイルを開く
    file = fopen("regression_data.txt", "r");
    if (file == NULL) {
        printf("ファイルを開くことができませんでした!\n");
        return 1;
    }

    // ファイルからデータを読み込む
    while (fscanf(file, "%lf %lf", &x[n], &y[n]) == 2) {
        n++;
        if (n >= max_lines) break;
    }
    fclose(file);

    // 回帰パラメータを計算
    calculateRegressionParameters(x, y, n, &slope, &intercept);

    // R-二乗を計算
    r_squared = computeRSquared(x, y, n, slope, intercept);

    // 結果を出力
    printf("回帰分析結果:\n");
    printf("データポイント数:%d\n", n);
    printf("傾き:%.4f\n", slope);
    printf("切片:%.4f\n", intercept);
    printf("R-二乗 (R²): %.4f\n", r_squared);

    // R-二乗を解釈
    interpretRSquared(r_squared);

    return 0;
}

プログラムをコンパイルします。

gcc -o r_squared_print r_squared_print.c -lm

プログラムを実行します。

./r_squared_print

出力例:

回帰分析結果:
データポイント数: 5
傾き: 0.6000
切片: 1.5000
R-二乗 (R²): 0.5600

R² の解釈:
適合度の良いモデル: モデルは変動の 50-70% を説明しています。

重要な点:

  1. 外部ファイルからデータを読み込みます。
  2. 最小二乗法を使用して回帰パラメータを計算します。
  3. R² 値を計算します。
  4. R² 値を解釈します。
  5. モデルの予測力を理解するのに役立ちます。

まとめ

この実験では、C 言語を用いて単純な線形回帰モデルを用いて予測値 y を計算する方法を学びました。このプログラムは、x と y のデータポイント、および回帰直線の傾きと切片を入力として受け取り、予測値 y を計算します。重要なステップは、回帰式に基づいて予測値 y を計算し、比較のために元の y 値と予測値 y 値を出力することでした。

次に、説明変動と総変動を用いて決定係数 (R²) を計算する方法を学びます。