主成分分析     Last modified: May 15, 2002

 分散共分散行列からスタートすることもできる主成分分析です。以下のスクリプトの他に,固有値・固有ベクトルを求める関数が必要です。

 使用法は jgawk -f pca.awk -f eigen.awk test.dat のようになっています。


BEGIN { # 環境設定
   FS = "[, \t]+" # 数値の区切りは,カンマ,空白,タブ
   covariance = 0 # 分散共分散行列から出発するときは 1 にする
   eps = 1e-10    # 固有値の精度(大きすぎないように)
}

{ # データファイル処理
   if (NR == 1) { # データファイルの 1 行目の変数の個数を記憶
       nv = NF
   }
   else if (NF != nv) { # 2 行目以降の変数の個数のチェック
       printf "1ケースあたりの変数の個数が違います!\n\n" > "/dev/stderr"
       error = 1
       exit(1)
   }
   nc++
   for (i = 0; i < nv; i++) { # 平均値,変動の積算
       temp[i] = vx = $(i+1)-mean[i]
       mean[i] += vx/nc
       for (k = 0; k <= i; k++) {
           a[i,k] += (nc-1)*temp[k]*vx/nc
       }
   }
}

END { # 前処理が終了したら,本処理
   if (error) exit(1)
   nc = NR
   printf "ケース数: %i  変数の個数: %i\n\n", nc, nv
   printf "%4s%14s%15s%15s\n", "変数", "平均値", "不偏分散", "標準偏差"
   for (i = 0; i < nv; i++) {
       sd[i] = sqrt(var = a[i,i]/(nc-1))
       printf "X%02i%15.7g%15.7g%15.7g\n", i+1, mean[i], var, sd[i]
   }
   printf covariance ? "\n\n分散共分散行列\n\n" : "\n\n相関係数行列\n\n"
   for (i = 0; i < nv; i++) {
       printf "X%02i", i+1
       for (j = 0; j <= i; j++) {
           if (covariance) {
               a[i,j] /= nc-1 # 分散共分散行列
           }
           else if (sd[i]*sd[j] == 0) {
               exit(1)
           }
           else {
               a[i,j] /= sd[i]*sd[j]*(nc-1) # 相関係数行列
           }
           printf "%9.5f", a[j,i] = a[i,j]
       }
       printf "\n"
   }
   printf "   "
   for (i = 1; i <= nv; i++) {
       printf "      X%02i", i
   }
   printf "\n"
   if (eigen2(a, nv, nv, eps, e, v) == 0) { # 固有値・固有ベクトル
       output("固有値・固有ベクトル", nv, e, 1)
       output("因子負荷量", nv, e, 0)
   }
}

function output(string, n, e, f_eval,     i, j) # 結果出力
{
   printf "\n%s\n\n   ", string
   for (i = 0; i < n; i++) {
       printf "%9i", i+1
   }
   printf "\n"
   for (j = 0; j < n; j++) {
       printf "X%02i", j+1
       for (i = 0; i < n; i++) {
           if (covariance) {
               printf "%9.5f", v[i,j]*(f_eval ? 1 : sqrt(e[i])/sd[j])
           }
           else {
               printf "%9.5f", v[i,j]*(f_eval ? 1 : sqrt(e[i]))
           }
       }
       printf "\n"
   }
   printf "EV "
   for (i = 0; i < n; i++) {
       printf "%9.5f", e[i]
   }
   printf "\n"
}


・ 直前のページへ戻る  ・ E-mail to Shigenobu AOKI