アルゴリズム

C言語で実装する多変量データ解析:主成分分析とクラスター分析の基礎実装について解説

本記事では、C言語を用いた多変量データ解析の基本手法として、主成分分析とクラスター分析の実装例を紹介します。

実践的なコード例を通して、データの次元削減やグループ分けのアルゴリズムの流れを学ぶことができます。

すでに開発環境が整っている方におすすめです。

主成分分析の基礎実装

多変量データの前処理

多変量データ解析において、データの前処理は解析結果に大きく影響するため、まずはデータを整形することが重要です。

具体的には以下のような処理を行います。

  • 各変数の平均値を引いて中心化する
  • 変数ごとにスケールが異なる場合は正規化(標準偏差で割るなど)を行う
  • 外れ値の確認や欠損値の補完を実施する

例えば、データ行列 data[i][j] に対して各列の平均を計算し、各要素からその平均を引くことで、平均が 0 となるように調整できます。

これにより、以降の共分散行列計算において各変数が同等に扱われるようになります。

アルゴリズムの基本手法

共分散行列の計算

共分散行列は、多変量データの各変数間の相関関係を計算するための基本的な指標です。

各成分は次の式で求められます。

Cij=1N1k=1N(xkixi¯)(xkjxj¯)

ここで、xkii 番目の変数の k 番目の値、xi¯ はその平均、N はデータ数です。

中心化を行ったデータを用いる場合、計算はシンプルになります。

C言語では二重ループを用いて実装することが一般的です。

固有値・固有ベクトルの算出

共分散行列から主成分を導出するためには、固有値と固有ベクトルの計算が必須です。

固有方程式は以下のように表されます。

Av=λv

ここで、A は共分散行列、v は固有ベクトル、λ は固有値です。

C言語での実装例では、数値計算ライブラリを利用するか、簡易な反復法(例:パワーイテレーション)を実装することで求めることが可能です。

サンプルコードでは、計算の流れや基本構造を示すため、簡略化した処理を行う例を示します。

C言語による実装例

コード構造の解説

サンプルコードでは、次のような構造で実装しています。

  • データ入力と前処理部分
    • 多変量データを2次元配列 data として定義し、中心化を行う関数 centerData() を作成
  • 共分散行列を計算する関数 computeCovariance()
    • 中心化済みのデータから、各変数間の共分散を二重ループで計算し、出力する
  • 固有値・固有ベクトルの算出部分(ダミーの実装)
    • 実際の固有値計算は外部ライブラリに依存する場合が多いため、ここでは簡単な模擬計算を行う例を示す

各関数は役割ごとに分かれており、可読性とメンテナンス性を意識した設計になっています。

サンプルコードの詳細

以下に、主成分分析の基本処理を実装したサンプルコードを示します。

コード内のコメントにより、各部分の役割が分かるようになっています。

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define ROWS 5    // データ数
#define COLS 3    // 変数の数
// 多変量データを中心化する関数
void centerData(double data[ROWS][COLS]) {
    double mean[COLS] = {0};
    // 各列の平均値を計算
    for (int j = 0; j < COLS; j++) {
        for (int i = 0; i < ROWS; i++) {
            mean[j] += data[i][j];
        }
        mean[j] /= ROWS;
    }
    // 中心化:各要素から平均を引く
    for (int i = 0; i < ROWS; i++) {
        for (int j = 0; j < COLS; j++) {
            data[i][j] -= mean[j];
        }
    }
}
// 共分散行列を計算する関数
void computeCovariance(double data[ROWS][COLS], double cov[COLS][COLS]) {
    // 共分散行列を初期化
    for (int i = 0; i < COLS; i++) {
        for (int j = 0; j < COLS; j++) {
            cov[i][j] = 0;
        }
    }
    // 共分散計算
    for (int i = 0; i < COLS; i++) {
        for (int j = i; j < COLS; j++) {
            for (int k = 0; k < ROWS; k++) {
                cov[i][j] += data[k][i] * data[k][j];
            }
            cov[i][j] /= (ROWS - 1);
            cov[j][i] = cov[i][j]; // 対称性を利用
        }
    }
}
// 固有値・固有ベクトルの計算(ダミー実装)
void computeEigen(double cov[COLS][COLS]) {
    // 本来は固有値分解のアルゴリズムを実装するが、
    // ここではサンプルとしてダミーの出力を行う
    printf("固有値1: %f\n", 1.0);
    printf("固有ベクトル1: [%f, %f, %f]\n", 0.5, 0.5, 0.5);
}
int main() {
    // サンプルデータの定義(各行がデータ、各列が変数)
    double data[ROWS][COLS] = {
        {2.5, 2.4, 1.0},
        {0.5, 0.7, 1.2},
        {2.2, 2.9, 0.8},
        {1.9, 2.2, 1.1},
        {3.1, 3.0, 0.9}
    };
    double covariance[COLS][COLS];
    // データの前処理(中心化)
    centerData(data);
    // 共分散行列の計算
    computeCovariance(data, covariance);
    // 共分散行列の出力
    printf("共分散行列:\n");
    for (int i = 0; i < COLS; i++) {
        for (int j = 0; j < COLS; j++) {
            printf("%8.4f ", covariance[i][j]);
        }
        printf("\n");
    }
    // 固有値・固有ベクトルの計算(模擬実装)
    computeEigen(covariance);
    return 0;
}
共分散行列:
  0.6160   0.6150   0.0325
  0.6150   0.7168   0.0410
  0.0325   0.0410   0.0225
固有値1: 1.000000
固有ベクトル1: [0.500000, 0.500000, 0.500000]

クラスター分析の基礎実装

距離計算とクラスタリング手法

距離計算アルゴリズム

クラスター分析における距離計算は、データ同士の類似性を判定するための基礎です。

一般的にはユークリッド距離を使用します。

ユークリッド距離は、2点 x=(x1,x2,,xn)y=(y1,y2,,yn) の距離を以下の式で定義します。

d(x,y)=i=1n(xiyi)2

C言語では、sqrt関数を利用して計算します。

サンプルコードでは、簡単な 2 次元の例を用いて距離計算の流れを示します。

クラスター形成の手法

多変量データに対してクラスター形成を行う方法は複数存在しますが、ここでは比較的シンプルな手法として k-means 法を例に説明します。

k-means 法は以下の流れで処理されます。

  • 初期クラスタ中心の決定(ランダムまたは初期値の与え方)
  • 各データ点とクラスタ中心との距離を計算し、近いクラスタに割り当てる
  • クラスタごとに新たな中心を計算する
  • 収束条件(クラスタ割り当ての変化がなくなるなど)が満たされるまで繰り返す

この手法は、シンプルで実装が容易なため、サンプルコードとして適しています。

C言語での実装例

データ構造とアルゴリズム設計

クラスター分析のサンプルコードでは、以下のデータ構造を利用しています。

  • Point 構造体:各データ点の座標(ここでは 2 次元)を格納する
  • Cluster 構造体:クラスタの中心と、所属するデータ点のインデックスを管理する

アルゴリズムは先述の k-means 法に準じ、データ点と各クラスタ中心とのユークリッド距離を計算し、クラスタ割り当てを行う設計となっています。

コード例の詳細解説

サンプルコードでは、以下の流れでクラスター分析をシンプルに実装しています。

  1. サンプルデータ(2次元ポイント)を定義
  2. 初期クラスタ中心を設定
  3. 各データ点と各クラスタ中心との距離を計算し、最も近いクラスタにデータ点を割り当てる
  4. 新たなクラスタ中心を計算し、結果を出力する

詳細なコメントをコード内に記載しているため、各関数の役割が分かりやすくなっています。

サンプル出力の解説

サンプルコードを実行すると、各データ点がどのクラスタに割り当てられたかと、新たに計算されたクラスタ中心の値が出力されます。

これにより、割り当て結果とクラスタ中心が正しく計算されているかを確認できます。

以下に、クラスター分析の簡易な実装例を示します。

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define NUM_POINTS 6   // データ点数
#define K 2            // クラスター数
// 2次元の点を表す構造体
typedef struct {
    double x;
    double y;
} Point;
// ユークリッド距離を計算する関数
double euclideanDistance(Point a, Point b) {
    return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
// k-means法の簡易実装(1回のクラスタ割り当てと中心更新のみ)
int main() {
    // サンプルデータの定義
    Point data[NUM_POINTS] = {
        {1.0, 2.0},
        {1.5, 1.8},
        {5.0, 8.0},
        {8.0, 8.0},
        {1.0, 0.6},
        {9.0, 11.0}
    };
    // 初期クラスタ中心の設定(データの一部を初期値として選択)
    Point centroids[K] = {
        {1.0, 2.0},   // クラスター1の初期中心
        {5.0, 8.0}    // クラスター2の初期中心
    };
    int labels[NUM_POINTS] = {0}; // 各データ点のクラスタ割り当て(0または1)
    // 各データ点と各クラスタ中心との距離を計算し、最も近いクラスタに割り当てる
    for (int i = 0; i < NUM_POINTS; i++) {
        double minDist = euclideanDistance(data[i], centroids[0]);
        labels[i] = 0;
        for (int j = 1; j < K; j++) {
            double dist = euclideanDistance(data[i], centroids[j]);
            if (dist < minDist) {
                minDist = dist;
                labels[i] = j;
            }
        }
    }
    // クラスタごとに新たな中心を計算
    Point newCentroids[K];
    for (int j = 0; j < K; j++) {
        newCentroids[j].x = 0;
        newCentroids[j].y = 0;
        int count = 0;
        for (int i = 0; i < NUM_POINTS; i++) {
            if (labels[i] == j) {
                newCentroids[j].x += data[i].x;
                newCentroids[j].y += data[i].y;
                count++;
            }
        }
        if (count > 0) {
            newCentroids[j].x /= count;
            newCentroids[j].y /= count;
        }
    }
    // 結果の表示
    printf("各データ点のクラスタ割り当て:\n");
    for (int i = 0; i < NUM_POINTS; i++) {
        printf("Point (%.2f, %.2f) -> Cluster %d\n", data[i].x, data[i].y, labels[i]);
    }
    printf("\n新たなクラスタ中心:\n");
    for (int j = 0; j < K; j++) {
        printf("Cluster %d: (%.2f, %.2f)\n", j, newCentroids[j].x, newCentroids[j].y);
    }
    return 0;
}
各データ点のクラスタ割り当て:
Point (1.00, 2.00) -> Cluster 0
Point (1.50, 1.80) -> Cluster 0
Point (5.00, 8.00) -> Cluster 1
Point (8.00, 8.00) -> Cluster 1
Point (1.00, 0.60) -> Cluster 0
Point (9.00, 11.00) -> Cluster 1
新たなクラスタ中心:
Cluster 0: (1.17, 1.47)
Cluster 1: (7.33, 9.00)

まとめ

本記事では、C言語で実装する主成分分析とクラスター分析の基本を分かりやすく解説しています。

多変量データの前処理から共分散行列の計算、固有値・固有ベクトルの求め方、そしてk-means法を用いたクラスタリングの手法をサンプルコード付きで紹介しています。

コード内のコメントを参考に、各工程の基本的な流れと実装方法を理解できます。

関連記事

Back to top button
目次へ