特異値分解と主成分分析 (1)

ー MATLAB 版
ベータ版 3.0
 
早稲田大学教育・総合科学学術院 新井仁之
 

概要

今回は特異値分解を主成分分析に応用する.前回の「特異値分解のアルゴリズムと次元圧縮」(URLは本ノート末尾参照)の続編である。特異値分解による主成分分析についての数学の解説は
新井仁之,線形代数 基礎と応用,日本評論社,2006
の第17章「多変量解析と線形代数」を元にしている.
本ノートはMATLABのライブエディターを使用して作成した.
 

 

必要な用語解説

をデータからなる行列とする.ここで N は個体数(観測対象)の個数であり,p は変量(観測値)の個数である.たとえばある学校(あるいはクラス)の生徒の成績をイメージしてほしい.ここで N は生徒数 p は教科数, は学生番号 j の生徒の科目 k の点数を表す.
変量 k の平均値を とおき, とおく. とおく.
平均偏差行列という.
分散共分散行列という. とおくと
となっている.
をデータ 分散共分散という.
また,相関係数 を用いて
相関係数行列という.
データ X(:,j) と X(:,k) は相関係数 が 1 に近いときに相関は強いといい, のとき相関はないという.
とおき,X の標準得点行列という.標準化したものは,各変量の平均が 0 であり,相関係数行列は変わらない.
標準化したデータ行列の分散共分散行列は元のデータの標準化係数行列と一致している.
相関係数行列と分散共分散行列には次の関係式がある.
clear

 

平均偏差行列,分散共分散行列,相関係数行列,標準得点行列の計算

function A = MDmatrix(X)
% 平均偏差行列(mean deviation matrix)
A = X - mean(X,1);
end
 
function A = VCmatrix(X)
% 分散共分散行列(variance-covariance matrix)
M = MDmatrix(X);
A = (1/size(M,1))*(M')*M;
end
 
function A = CRmatrix(X)
% 相関係数行列(correleation matrix)
% MATLAB には corrcoef(X) がある.
S = VCmatrix(X);
p = size(S,1);
A = zeros(p,p);
for j =1:p
for k = 1:p
A(j,k) = S(j,k)/(sqrt(S(j,j))*sqrt(S(k,k)));
end
end
end
 
function D = MD_CR(X)
A = diag(VCmatrix(X));
A = sqrt(A);
A = 1./A;
D = diag(A);
end
 
function Y = stand(X)
% 標準得点行列
Y = MDmatrix(X)*MD_CR(X);
end
コメント:このうち,相関係数行列に関しては MATLAB の corrcoef があるが,それと上記の CRmatrix は同じであることを確認する.
たとえば次のデータ行列 X を考える.
X = [8 9;9 6;1 0]
X = 3×2
8 9 9 6 1 0
disp(char('CRmatrix(X) = ', num2str(CRmatrix(X))));
CRmatrix(X) = 1 0.90113 0.90113 1
disp(char('corrcoef(X) = ', num2str(corrcoef(X))));
corrcoef(X) = 1 0.90113 0.90113 1
データの標準化の具体例
データ行列としては前掲の X を考える.
XS = stand(X);
disp(char('X の標準化 = ', num2str(stand(X))));
X の標準化 = 0.56195 1.069 0.84293 0.26726 -1.4049 -1.3363
disp(char('X の標準化の変量の平均 = ', num2str(mean(XS))));
X の標準化の変量の平均 = 0 0
標準化したデータ行列の分散共分散行列は元のデータの標準化係数行列と一致している
disp(char('X の標準化の分散共分散行列 = ', num2str(VCmatrix(XS))));
X の標準化の分散共分散行列 = 1 0.90113 0.90113 1
disp(char('X の相関係数行列 = ', num2str(CRmatrix(X))));
X の相関係数行列 = 1 0.90113 0.90113 1

 

特異値分解による主成分ベクトル

X を 行列で,N は個体数,p は変量の数とする.もしデータの単位などが不揃いの場合は,標準化したデータを考えていく.X の分散共分散行列( 標準化したデータの場合の分散共分散行列は元データの相関係数行列)の特異値分解をする.
N 次ユニタリー行列 Up 次ユニタリー行列 V により,その特異値標準形が により与えられる.
の場合を考える. の特異値とする. とする.このとき特異値分解は
で与えられる.
k 主成分(第 k 主成分ベクトル) を固体 nk主成分得点という.
を第 k 主成分の寄与率 を第 k 主成分までの累積寄与率という.一般には累積寄与率が 80%を超えるような k までの主成分を考慮することが望ましいとされている.

 

具体例の計算

次の仮想的な成績のデータで試してみよう.
testkekka.xlsx (readtableで読み込んだものを下に表示する

なおこれは数学的に作った架空の数値データで,実在の学校・個人とは一切関係ない.

データは,50人の架空の人たちの 架空の学生番号,数学,物理,アート,英語の点数からなっている.
filename = 'Score.xlsx'; % ここは自分の好きなデータを使う.
% その場合はこのセクションはデータの形態に合わせて変更
Score = readtable(filename,"TextType","string","VariableNamingRule","preserve")
Score = 50×5 table
 Student_IDMathematicsPhysicsArtEnglish
11449379100
22861008644
33891007077
4470807691
55301008453
66452864100
7721406458
888807196
9965456281
101083559475
111142478617
12121001008188
1313100439053
141456468534
15152772706
161640439877
171761868081
181858588414
1919401008972
202069917049
212148477077
222222936917
232352987886
242458978759
252590170100
262636537930
27276397651
2828681006846
292907970100
3030726175100
313131348755
323250487579
33331004979100
3434100928475
353585787772
363655100899
37373466890
383872069100
3939539879100
404021997578
414160736259
424234876853
4343936886100
4444467179100
4545806574100
4646442873100
474761459390
4848650692
494951816827
50501005078100

各科目の平均点

mean(Score(:,["Mathematics" "Physics" "Art" "English"]))
ans = 1×4 table
 MathematicsPhysicsArtEnglish
159.160063.940077.560066.6200

各教科の最低点(Min),中央値(Median),最高点(Max)

summary(Score(:,2:end))
Variables: Mathematics: 50×1 double Values: Min 0 Median 58 Max 100 Physics: 50×1 double Values: Min 0 Median 67 Max 100 Art: 50×1 double Values: Min 62 Median 77.5 Max 98 English: 50×1 double Values: Min 0 Median 76 Max 100

個人の平均点を計算して表に付加

Score.Average = round(mean(Score{:,2:5},2),1) %小数点2桁目を四捨五入
Score = 50×6 table
 Student_IDMathematicsPhysicsArtEnglishAverage
1144937910079
2286100864479
3389100707784
447080769179.3000
5530100845366.8000
6645286410059.3000
772140645845.8000
88880719663.8000
996545628163.3000
10108355947576.8000
11114247861748
1212100100818892.3000
131310043905371.5000
14145646853455.3000
1515277270643.8000
16164043987764.5000
17176186808177
18185858841453.5000
191940100897275.3000
20206991704969.8000
21214847707760.5000
22222293691750.3000
23235298788678.5000
24245897875975.3000
25259017010065.3000
26263653793049.5000
2727639765149.8000
282868100684670.5000
29290797010062.3000
303072617510077
31313134875551.8000
32325048757963
3333100497910082
343410092847587.8000
35358578777278
36365510089963.3000
3737346689047.3000
38387206910060.3000
393953987910082.5000
40402199757868.3000
41416073625963.5000
42423487685360.5000
434393688610086.8000
444446717910074
454580657410079.8000
464644287310061.3000
47476145939072.3000
484865069234
49495181682756.8000
5050100507810082

平均点のヒストグラム

histogram(Score.Average,10)

 

主成分分分析

X = table2array(Score(:,2:5));
X_org = X;
X = MDmatrix(X);
 
% データの標準化をしたいときは % を外す.
% X = stand(X);
 
% 特異値標準化
[Sigma, U, V]=SingNormal(X); % 前回の特異値分解のレクチャーで作った特異値標準形を使う.
% もちろんMATLAB の svd を使っても良い.
PCAdata = X*V; %主成分得点

主成分ベクトルの表示

for j = 1:size(V,1)
disp(char(['第',num2str(j),'主成分ベクトル'], num2str(V(:,j))));
end
第1主成分ベクトル 0.41056 -0.48254 -0.028703 0.77316 第2主成分ベクトル 0.11919 0.86853 0.032773 0.47998 第3主成分ベクトル 0.9003 0.10029 0.099132 -0.4118 第4主成分ベクトル -0.081852 -0.052566 0.99412 0.047564

寄与率と累積寄与率の計算

%寄与率と累積寄与率の計算
Kiyo = zeros(length(Sigma),1);
Ruiseki_Kiyo = zeros(length(Sigma),1);
RK = 0;
for j = 1:length(Sigma)
Kiyo(j)=Sigma(j)^2/sum(Sigma.^2);
RK = RK + Kiyo(j);
Ruiseki_Kiyo(j) = RK;
end
for j=1:length(Sigma)
disp(['第',num2str(j),'主成分寄与率 =',num2str(Kiyo(j))]);
end
第1主成分寄与率 =0.44855 第2主成分寄与率 =0.33504 第3主成分寄与率 =0.18841 第4主成分寄与率 =0.028006
for j=1:length(Sigma)
disp(['第',num2str(j),'累積寄与率 =',num2str(Ruiseki_Kiyo(j))]);
end
第1累積寄与率 =0.44855 第2累積寄与率 =0.78359 第3累積寄与率 =0.97199 第4累積寄与率 =1

平均値による順位(order_avg),第一主成分得点(fristPC),第二主成分得点(secondPC),第三主成分得点(thirdPC)を表に付加.

Score_avg = sortrows(Score,6,'descend');
a = table2array(Score_avg(:,1));
[~,p] = sort(a,'ascend');
Score.order_avg = p; %平均点による順位
 
Score.firstPC = PCAdata(:,1); %第1主成分得点
Score.secondPC = PCAdata(:,2); %第2主成分得点
Score.thirdPC = PCAdata(:,3); %第3主成分得点
Score_sub = Score(:,["Student_ID" "Average" "order_avg" "firstPC" "secondPC" "thirdPC"])
Score_sub = 50×6 table
 Student_IDAverageorder_avgfirstPCsecondPCthirdPC
1179105.520239.5015-24.3372
227911-24.111923.937537.9320
338443.093339.610025.4574
4479.3000915.595426.89131.1755
5566.800025-40.087421.5173-16.3891
6659.30003837.7261-17.3252-31.4427
7745.800048-10.3905-29.9225-34.5508
8863.80002865.5976-38.20946.8031
9963.30003023.1016-9.3617-4.1059
101076.80001620.1088-0.362218.7454
11114846-37.4775-40.29814.1222
121292.3000115.798546.561431.9213
131371.50002115.9840-19.449041.5100
141455.300040-18.0747-31.37119.5263
151543.800049-63.7448-26.1769-3.9314
161664.5000279.6767-14.8185-21.5980
171777141.158726.3611-1.8109
181853.500041-38.4785-30.342820.6673
191975.300017-21.435331.9927-14.7147
202069.800023-22.423615.970118.0792
212160.50003511.8347-11.3085-16.7701
222250.300043-67.3974-3.2868-10.9557
232378.500012-4.403538.0451-10.9673
242475.300018-22.591325.22726.3449
252565.30002669.0575-35.21546.9576
262649.500045-32.5841-29.7918-6.7252
272749.80004416.0550-54.80764.2249
282870.500022-29.439022.162219.1187
292962.300033-5.530621.8029-66.2466
3030771532.571814.9148-2.7347
313151.800042-6.3693-34.6280-22.6341
3232633213.5761-9.0778-15.1972
333382649.74317.960721.6667
343487.800029.521633.471736.7698
353578138.000117.855222.4028
363663.300031-63.98603.541724.7332
373747.300047-63.1600-32.81106.1233
383860.30003762.1787-38.2620-9.4471
393982.500056.802644.9168-15.7331
404068.300024-23.712631.2807-35.7793
414163.500029-9.47183.80163.2603
424260.500036-31.713010.1788-15.6778
434386.8000337.500123.857917.9640
4444741916.957120.6323-24.7430
454579.8000833.954819.30964.7697
464661.30003437.0572-17.1494-31.4508
474772.30002027.5280-4.5026-8.3402
48483450-16.4649-86.134524.6072
494956.800039-41.9404-5.48579.7323
505082749.28938.796521.6679

 

補足

以下は前回のレクチャー「特異値分解のアルゴリズムと次元圧縮」より必要なコマンド
Lemma. 足りない正規直交系を補完して正規直交基底にするアルゴリズム
function A = HokanONB(Q)
 
% Q は Nxp 配列,ただし N>=p (行数 >= 列数)である必要がある.
% Q(:,1),...,Q(:,p) は正規直交系になるようにしておく.
% A は NxN 配列で,A(:,1),...,A(:,N)は正規直交基底であり,かつ A(:,k)=Q(:,k), 1<=k<=p
% Q は正規直交系でない場合,Q の部分も合わせて直交化される.
 
N=size(Q,1);
n=size(Q,2);
 
if N<n
disp('入力配列の列数が行数を超えてます.')
return
end
 
 
if n == N
A = Q;
return
end
 
A=zeros(N,N);
 
A(:,1:n) = Q;
 
NB = eye(N);
 
%m=0;
M=0;
for k = 1:N
l = k+M;
A(:,n+l)=NB(:,k);
rank_A = rank(A);
if rank_A < n+1
A(:,n+l)=zeros(N,1);
M=M-1;
end
if rank_A == N
A=GramSchmidt(A);
return
end
end
 
 
end
 
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function Q = GramSchmidt(A)
% グラム・シュミットの正規直交化
 
% 正規直交基底を保存するための行列
Q = zeros(size(A));
n = size(A, 2);
 
for i = 1:n
v = A(:, i);
for j = 1:i-1
v = v - (Q(:, j)' * A(:, i)) * Q(:, j);
end
% 直交ベクトルを正規化
Q(:, i) = v / norm(v);
end
 
end
特異値標準形を与えるアルゴリズム
以上の準備のもとに特異値標準系を与えるアルゴリズムを記す.このアルゴリズム自体は線形代数でよく知られたものである.(たとえば [1] 参照.)
function [Sigma, U, W]=SingNormal(A)
% 行列 A を与える.Aは MxN 行列
% Sigma は A の特異値 min(M,N)x1 配列
% U,W は U'*A*W が特異値標準形を与えるユニタリー行列
 
[S1,S2]=size(A);
% A_org = A;
if S1>S2
A=A';
end
[N1,N2]=size(A);
B=A'*A;
L = min(N1,N2);
%N = size(B,1);
[V,D]=eig(B);
r = rank(B);
Sigma = sqrt(diag(D));
[Sigma,p] = sort(Sigma,'descend');
Sigma = Sigma(1:L);
VV = zeros(size(V));
for k = 1:size(V,1)
VV(:,k) = V(:,p(k));
end
U1 = zeros(N1,r);
for k=1:r
U1(:,k)=Sigma(k)^(-1)*A*VV(:,k);
end
MinSize = min(N1,N2);
if r == MinSize
U=U1;
W=VV;
else
U = HokanONB(U1);
W=VV;
end
if S1>S2
U_org = U;
U=W;
W=U_org;
end
end

 

参考文献

[1] 新井仁之,線形代数 基礎と応用,日本評論社,2006. Kindle版(2022年版)があります.
Hyousi.jpg
履歴
ベータ版 1.1 2024/7/19 公開
ベータ版 2.0 2024/7/20 不十分な箇所をいくつか修正及び補足の追加
ベータ版 3.0 2024/8/2 minor changes