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 값 출력
    printf("원본 대 예측된 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 값:
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 값을 모두 출력합니다.

설명된/총 변동을 이용한 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²) 를 계산하는 방법을 배울 것입니다.