rによるprincomp関数を使わない主成分分析
TRANSCRIPT
2012.12 作成
princomp 関数を使わない主成分分析
主成分分析の種類
A. 相関行列から算出するもの <= データを標準化する場合
B. 共分散行列から算出するもの <= データを標準化しない場合
主成分分析の流れ
① データを標準化 [A(相関行列出発)の場合のみ]
② 相関行列、または共分散行列の計算
※ データが標準化されていれば、共分散行列=相関行列となる。
③ 相関行列/共分散行列から固有ベクトルと固有値を算出
④ 大きい固有値に対応する固有ベクトルから、第一主成分、第二主成分・・・になる。
⑤ 固有値から寄与率を算出し、何番目の主成分まで採用するか決める。
⑥ 主成分負荷量及び主成分得点の計算
A. 標準化したデータと固有ベクトルとの内積
B. 主成分を標準化し、中心化したデータと内積
[デモ]R によるあやめデータの主成分分析
ステップ 1 : 前処理
まずR を起動し、組込のあやめのデータセットを呼び出す。[a]
データセット名の iris と入力すると、データの内容が表示される。[b]
このデータは、三つの異なる品種のあやめの花弁の寸法を測ったもので、5 変数 150 データ(各
品種 50データずつ)、1~4変数までが量的変数で、第 5 変数は品種名。データはリスト属性で、
データ番号・変数名つき。縦がデータ数、横が変数。
サンプルデータ(データ番号 1, 51, 101)
リスト属性のデータは、そのまま計算に使うのは不都合なので、行列(matrix)属性データに変
換し、さらに数量項目ではない第 5変数を落とし、作業用の datというデータを作る。[c]
通常データは scale関数などを用いて標準化することが多いが[d]、今回は標準化しない例を取
り上げる。
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1 5.1 3.5 1.4 0.2 setosa
51 7.0 3.2 4.7 1.4 versicolor
101 6.3 3.3 6.0 2.5 virginic
あやめデータの散布図
data(iris) #[a]
iris #[b]
dat<- as.matrix(iris[,1:4]) #[c]
# dat<- scale(as.matrix(iris[,1:4])) #[d]
fg1 <- as.numeric(iris[,5]) # 品種別フラグ
pairs(dat, pch=21, bg=c("red", "green", "blue")[fg1]) # 散布図
Sepal.Length
2.0 3.0 4.0 0.5 1.5 2.5
4.5
5.5
6.5
7.5
2.0
3.0
4.0
Sepal.Width
Petal.Length1
23
45
67
4.5 5.5 6.5 7.5
0.5
1.5
2.5
1 2 3 4 5 6 7
Petal.Width
ステップ 2 : 固有値計算
共分散行列を求め[e]、これから固有値・固有ベクトルを算出する。[f]
固有ベクトルが主成分(主成分得点の係数)で、固有値は主成分得点の分散にあたる。
データが 4 変数なので、共分散行列は四行四列。
4 つの固有値は大きい順に並んだ一つのベクトル(下の例では eg$value)、固有ベクトルは
対応する固有値が大きい順に上から並んだ 4 行 4 列の行列が返される。
寄与率は、4つの固有値の合計で各固有値を割った値になる。[g]
上の例では、eigen 関数による固有値・固有ベクトルの計算結果を変数 eg1 に代入している
ので、eg1 とタイプすると結果が表示される。
$values が固有値、$vectors が固有ベクトル。それぞれ eg1$value, eg1$vector で個別にこれ
らの内容が参照できる。第 1主成分は、eg1$vectors[1,]、これに対応する固有値が eg1$values[1]、
寄与率 ct の計算結果を見れば、この第一主成分だけでデータ全体の 92%が説明できることが
わかる。
[結果出力のコンソール画面]
主成分分析を行う目的が次元縮約(たくさんある変数の数を減らすこと)であれば、主成
分採用の目安は経験的に累積寄与率が 70~80%以上とされているので、このデータの場合は
第一主成分だけを採用すればよいことになる。
cv <- cov(dat) #[e]
eg1 <- eigen(cv, symmetric=TRUE) #[f]
eg1a <- eg1$value # eg1から固有値を eg1aに取り出す
eg1v <- eg1$vectors # eg1から固有ベクトルを eg1vに取り出す
ct1 <- eg1a / sum(eg1a) #[g]
> eg1
$values
[1] 4.22824171 0.24267075 0.07820950 0.02383509
$vectors
[,1] [,2] [,3] [,4]
[1,] 0.36138659 0.65658877 0.58202985 0.3154872
[2,] -0.08452251 0.73016143 -0.59791083 -0.3197231
[3,] 0.85667061 -0.17337266 -0.07623608 -0.4798390
[4,] 0.35828920 -0.07548102 -0.54583143 0.7536574
> ct1
[1] 0.924618723 0.053066483 0.017102610 0.005212184
> eg1$vectors[1,]
[1] 0.3613866 0.6565888 0.5820299 0.3154872
> eg1
$values[1]
[1] 4.228242
ステップ 3 : 主成分得点と主成分負荷量の計算
ステップ 2 で、固有値分解により、固有値 ega と固有ベクトル egv を算出した。次に、主成分
得点(データの各主成分に対する値)と、主成分負荷量(構造係数とも呼ばれ、主成分と各変
量の間の相関係数と一致する)を計算してみる。
主成分得点は、データポイントから各主成分に射影したときのデータの重心(=平均)から
の影の長さになる。[図 1参照]
まず各データから平均値ベクトルを引き、データの中心を原点に持ってくる(中心化)。[k]
データを標準化せず、共分散行列から出発した場合、主成分も標準化されていないので、固
有値の平方根(=各主成分の標準偏差)で割って標準化する。[l]
(※中心化と主成分の標準化は、最初にデータを標準化した場合は必要がない。)
主成分得点(上の青いベクトル)は、標準化済主成分と中心化したデータの内積。[m]
主成分負荷量は、もとのデータと主成分得点との相関係数にあたる。
図 1. 主成分と主成分得点
cdat<- t(t(dat) - colMeans(dat, 2)) # [k]
spca<- eg1v %*% diag(1/sqrt(eg1a)) # [l] 主成分を標準化
tok<- cdat %*% spca # [m] 主成分得点
dev.new()
plot(tok[,1:2], pch=21, bg=c("red", "green", "blue")[fg1])
# 第 1・2主成分を x, y軸としてプロット
xpca<- solve(spca) # 主成分の逆行列
xpca<- solve(spca) # 主成分の逆行列 =>もとの軸を示すものになる
arrows(0, 0, x1=xpca[,1], y1=xpca[,2], col="red")
# あまり見栄えがよくないが、主成分得点プロットに元の軸を矢印で表示してみた
-1.5 -1.0 -0.5 0.0 0.5 1.0 1.5
-3-2
-10
12
tok[, 1:2][,1]
tok[,
1:2
][,2
]
princomp 関数との比較
princomp 関数による結果と比較してみる。ccr=FALSE指定で先と同じデータを標準化しな
い形での分析になる。結果は変わらないが、見栄えの良い biplotを出力することができる。
[結果出力のコンソール画面]
図 1 散布図
prc1 <- princomp(dat, cor=FALSE)
summary(prc1)
prc1$loadings # 因子負荷量表示
dev.new()
biplot(prc1)
> prc1 <- princomp(dat, cor=FALSE)
> summary(prc1)
Importance of components:
Comp.1 Comp.2 Comp.3 Comp.4
Standard deviation 2.0494032 0.49097143 0.27872586 0.153870700主成分標準偏差
Proportion of Variance 0.9246187 0.05306648 0.01710261 0.005212184寄与率
Cumulative Proportion 0.9246187 0.97768521 0.99478782 1.000000000累積寄与率
> prc1$loadings 因子負荷量のはずだが表示されているのは固有ベクトル(未標準化)
Loadings:
Comp.1 Comp.2 Comp.3 Comp.4
Sepal.Length 0.361 -0.657 -0.582 0.315
Sepal.Width -0.730 0.598 -0.320
Petal.Length 0.857 0.173 -0.480
Petal.Width 0.358 0.546 0.754
Comp.1 Comp.2 Comp.3 Comp.4
SS loadings 1.00 1.00 1.00 1.00
Proportion Var 0.25 0.25 0.25 0.25
Cumulative Var 0.25 0.50 0.75 1.00
-0.2 -0.1 0.0 0.1 0.2
-0.2
-0.1
0.0
0.1
0.2
Comp.1
Com
p.2
1
23
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
202122
23 24
2526
27
2829
3031
32
33
34
35
36
37
38
39
4041
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
6768
6970
7172
73
74
75
767778
79
80
8182
8384
85
86
87
8889
9091
92
93
94
95
9697
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116117
118
119
120
121
122
123
124
125
126
127128
129
130131
132
133134
135
136
137138
139
140
141
142
143
144145146
147
148149
150
-20 -10 0 10 20
-20
-10
010
20
Sepal.LengthSepal.Width
Petal.LengthPetal.Width
結果を比較するかぎり、どうも princomp は主成分を標準していないと思われる。先の spca
は標準化した主成分で、これが上の因子負荷量と同じになるはずだが、上の$loadings で表示
されているのは eg1v、つまり生の固有ベクトルと同じもの。
このままこれを用いてスコア計算すれば、標準偏差が 1にならないはずなので、以下は検証。
> eg1v
[,1] [,2] [,3] [,4]
[1,] 0.36138659 -0.65658877 -0.58202985 0.3154872
[2,] -0.08452251 -0.73016143 0.59791083 -0.3197231
[3,] 0.85667061 0.17337266 0.07623608 -0.4798390
[4,] 0.35828920 0.07548102 0.54583143 0.7536574
>spca
[,1] [,2] [,3] [,4]
[1,] 0.1757487 -1.3328606 -2.0812081 2.043494
[2,] -0.0411048 -1.4822115 2.1379949 -2.070931
[3,] 0.4166141 0.3519427 0.2726031 -3.108044
[4,] 0.1742424 0.1532248 1.9517707 4.881638
>apply(prc1$scores, 2, sd) # やっぱり princompのスコアは標準化されていない
Comp.1 Comp.2 Comp.3 Comp.4
2.0562689 0.4926162 0.2796596 0.1543862
>tokx<- cdat %*% eg1v # 主成分を標準化せずにスコア計算
>apply(tokx, 2, sd) # 標準化しないときのスコアの分散と同じ
[1] 2.0562689 0.4926162 0.2796596 0.1543862
# 今度は個々のデータのスコアの比較。
> tail(prc1$scores) # princompの主成分得点
Comp.1 Comp.2 Comp.3 Comp.4
[145,] 2.418746 -0.30479820 0.5044827 0.2410910
[146,] 1.944110 -0.18753230 0.1778251 0.4261959
[147,] 1.527167 0.37531698 -0.1218982 0.2543674
[148,] 1.764346 -0.07885885 0.1304816 0.1370013
[149,] 1.900942 -0.11662796 0.7232516 0.0445953
[150,] 1.390189 0.28266094 0.3629096 -0.1550386
> tail(tokx) # 標準化しないいで計算した主成分得点
[,1] [,2] [,3] [,4]
[145,] 2.418746 -0.30479820 0.5044827 0.2410910
[146,] 1.944110 -0.18753230 0.1778251 0.4261959
[147,] 1.527167 0.37531698 -0.1218982 0.2543674
[148,] 1.764346 -0.07885885 0.1304816 0.1370013
[149,] 1.900942 -0.11662796 0.7232516 0.0445953
[150,] 1.390189 0.28266094 0.3629096 -0.1550386
> tail(tok) #標準化した主成分得点
[,1] [,2] [,3] [,4]
[145,] 1.1762791 -0.6187336 1.8039168 1.5616100
[146,] 0.9454550 -0.3806864 0.6358626 2.7605835
[147,] 0.7426882 0.7618851 -0.4358805 1.6476050
[148,] 0.8580326 -0.1600817 0.4665730 0.8873934
[149,] 0.9244616 -0.2367522 2.5861852 0.2888555
[150,] 0.6760735 0.5737954 1.2976834 -1.0042261