主成分分析方法从统计学上讲对子特征提取,典型特征,相似性有重要的作用。简而言之,就是在导致结果的所有因素中发现哪些少数因素是概括了数据集整个特征空间中所包含的信息,因而称为主成分分析。常用的方法有 :
PCA (principle component analysis)
LDA(latent discriminative analysis) <-- 偶看了一篇论文apple 2005年时候的语音合成技术就是基于此算法,在此提一下
SVD(single value decomposition)
不过在我看来,核心都是利用正交化的思想构造特征值矩阵。
R语言中,实现这个方法的函数 princomp , prcomp 等,这里来介绍这两种方法的不同 。读了开发人的邮件后,总结主要如下:
princomp | prcomp | |
R mode / Q mode | 只适用于R mode | 适用于R mode 和 Q mode |
基于算法 | 基于 协方差(covariance) 或者 相关矩阵(correlation) 提取的特征(eigen) , 也叫 spectral decomposition | 基于SVD分解 |
载荷因子的R用法(不懂请把PCA基本算法搞懂) | princomp.loadings | prcomp.rotation |
- 什么是R mode 和Q mode:
- 首先,什么是 observation 和 variable:
- observation 指 所有观察到的记录数,也就是数据集里面的行数,我们有时也会说有几条数据这个意思,而variable是指因素的个数。
- observation 指 所有观察到的记录数,也就是数据集里面的行数,我们有时也会说有几条数据这个意思,而variable是指因素的个数。
- 首先,什么是 observation 和 variable:
R mode 是指基于variable的分析 ,意思就是说在所有的observation里面,来研究variable之间的关系,哪些是主要的?是典型的?英文:similarity in the variables over subjects. 这里的R 我认为指normal,就是最平常的情况,数据数量大于变量(因素)数量,例如人口统计中,是人口多还是统计指标多?对吧。这里文档里面翻译成 observation 大于 variable。
Q mode 是指基于observation的分析,意思就是说在所有的variable里面,来研究observation之间的关系,哪两条记录相似? 英文:similarity in the subjects over variables。所以这里是指变量(因素)数量大于数据数量,与R 相对。
清楚了什么是R / Q mode ,来看看 具体使用时候他们的异同点:
拿 iowa 一本书里的 protein.csv 数据举例,
1. R mode / Q mode 实验
food = read.csv("protein.csv")
food[1,]
Country RedMeat WhiteMeat Eggs Milk Fish Cereals Starch Nuts Fr.Veg 1 Albania 10.1 1.4 0.5 8.9 0.2 42.3 0.6 5.5 1.7
dim(food)[1] 25 10 这里 数据大于变量,即行大于列,则应该是R mode的数据,来看prcomp 和 princomp得到的载荷(loadings)
pcafood <- prcomp(food[,-1], scale=TRUE) str(pcafood$rotation)num [1:9, 1:9] -0.303 -0.311 -0.427 -0.378 -0.136 ... - attr(*, "dimnames")=List of 2 ..$ : chr [1:9] "RedMeat" "WhiteMeat" "Eggs" "Milk" ... ..$ : chr [1:9] "PC1" "PC2" "PC3" "PC4" ...这里的载荷是一个9*9的矩阵
pcafood2 <- princomp(food[,-1], scale=TRUE)
str(pcafood2$loadings)
loadings [1:9, 1:9] -0.1507 -0.1295 -0.0673 -0.4254 -0.127 ... - attr(*, "dimnames")=List of 2 ..$ : chr [1:9] "RedMeat" "WhiteMeat" "Eggs" "Milk" ... ..$ : chr [1:9] "Comp.1" "Comp.2" "Comp.3" "Comp.4" ...
这里的载荷同样是9*9的矩阵
但是, 如果选用Q mode的数据(在那个网站下得到),再来看一下:
raw=read.csv("unempstates.csv")
raw[1,]
AL AK AZ AR CA CO CT DE FL GA HI ID
1 6.4 7.1 10.5 7.3 9.3 5.8 9.4 7.7 10 8.3 9.9 5.5
IL IN IA KS KY LA ME MD MA MI MN MS MO
1 6.4 6.9 4.2 4.3 5.7 6.2 8.8 6.9 11.1 10 6.2 7 5.8
MT NE NV NH NJ NM NY NC ND OH OK OR
1 5.8 3.6 9.8 7.2 10.5 8.9 10.2 6.7 3.2 8.3 6.4 10.1
PA RI SC SD TN TX UT VT VA WA WV WI
1 8.1 7.8 7.6 3.6 5.9 5.9 6.1 8.8 6.2 8.7 8.3 5.9
WY
1 4.2
unempstates=t(raw)
dim(unempstates)
这里是一个反应美国** 的数据集,原数据集的行是美国50个州,列是各个指标,这里转置一下,变为Q mode的数据集unempstates[1] 50 416
pcaunempstates <- prcomp(unempstates[,-1], scale=TRUE)
str(pcaunempstates$rotation)
num [1:415, 1:50] -0.031 -0.0315 -0.0316 -0.0321 -0.0328 ...
- attr(*, "dimnames")=List of 2
..$ : NULL
..$ : chr [1:50] "PC1" "PC2" "PC3" "PC4" ...
pcaunempstates2 <- princomp(unempstates[,-1], scale=TRUE)
str(pcaunempstates2$loadings)
Error in princomp.default(unempstates[, -1], scale = TRUE) :
'princomp'只能在单位比变量多的情况下使用
这里可以看到,princomp不支持对Q mode 数据的pca处理,而prcomp 是可以的.
2. 载荷值的比较
实验前提一点,使用SVD分解和协方差方式做出来的结果其实应该是一样的或者接近的,因为这两种方法其实原理大致相同,只是一方需要有正定矩阵的约束条件
还是回到protein的例子,
food = read.csv("protein.csv")
pcafood <- prcomp(food[,-1],scale=TRUE)
PC1 PC2 PC3
RedMeat -0.3026094 -0.05625165 -0.29757957
WhiteMeat -0.3105562 -0.23685334 0.62389724
Eggs -0.4266785 -0.03533576 0.18152828
Milk -0.3777273 -0.18458877 -0.38565773
Fish -0.1356499 0.64681970 -0.32127431
Cereals 0.4377434 -0.23348508 0.09591750
Starch -0.2972477 0.35282564 0.24297503
Nuts 0.4203344 0.14331056 -0.05438778
Fr.Veg 0.1104199 0.53619004 0.40755612
PC4 PC5 PC6
RedMeat -0.646476536 0.32216008 -0.45986989
WhiteMeat 0.036992271 -0.30016494 -0.12100707
Eggs -0.313163873 0.07911048 0.36124872
Milk 0.003318279 -0.20041361 0.61843780
Fish 0.215955001 -0.29003065 -0.13679059
Cereals 0.006204117 0.23816783 0.08075842
Starch 0.336684733 0.73597332 0.14766670
Nuts -0.330287545 0.15053689 0.44701001
Fr.Veg -0.462055746 -0.23351666 0.11854972
PC7 PC8 PC9
RedMeat 0.15033385 -0.01985770 0.2459995
WhiteMeat -0.01966356 -0.02787648 0.5923966
Eggs -0.44327151 -0.49120023 -0.3333861
Milk 0.46209500 0.08142193 0.1780841
Fish -0.10639350 -0.44873197 0.3128262
Cereals 0.40496408 -0.70299504 0.1522596
Starch 0.15275311 0.11453956 0.1218582
Nuts -0.40726235 0.18379989 0.5182749
Fr.Veg 0.44997782 0.09196337 -0.2029503
pcafood2 <- princomp(food[,-1],scale=TRUE)
Loadings: Comp.1 Comp.2 Comp.3 Comp.4 Comp.5 Comp.6 RedMeat -0.151 -0.133 0.896 -0.289 0.229 WhiteMeat -0.129 -0.798 -0.185 -0.400 Eggs Milk -0.425 -0.831 0.220 -0.204 -0.117 -0.149 Fish -0.127 0.292 0.522 -0.285 -0.559 0.256 Cereals 0.861 -0.406 -0.274 0.114 Starch -0.112 -0.241 0.327 Nuts 0.114 0.166 0.123 -0.565 Fr.Veg 0.169 -0.533 -0.645 Comp.7 Comp.8 Comp.9 RedMeat WhiteMeat -0.246 -0.261 -0.146 Eggs 0.142 -0.255 0.940 Milk Fish -0.336 -0.229 Cereals Starch 0.871 -0.155 -0.167 Nuts -0.743 -0.232 Fr.Veg 0.180 0.482 为什么这里两种方式算出来的载荷这么不一样? 后来分析得知,原因在于princomp的 cor参数,这个参数其实有两个选项,TRUE和FALSE,TRUE表示采用correlation方式计算, FALSE 表示采用covariance方式计算。默认情况下,是采用FALSE值计算的。所以这里princomp计算的其实是covariance.而标准化(正态分布)后的covariance 其实 等价于correlation,并不再是 对应 covariance(X) 了。所以即使用covariance计算,也不可能产生和SVD 分解一样的结果,用式子表示, 就是 correlation = covariance( z-score(X) ) . 所以要使得两个结果一样,可以把 z-score的 normalization 给去掉,然后再试一次,这个时候应该是covariance(X)了:pcafood2 <- princomp(food[,-1], cor=FALSE,scale=FALSE)
Loadings: Comp.1 Comp.2 Comp.3 Comp.4 Comp.5 Comp.6 RedMeat -0.151 -0.133 0.896 -0.289 0.229 WhiteMeat -0.129 -0.798 -0.185 -0.400 Eggs Milk -0.425 -0.831 0.220 -0.204 -0.117 -0.149 Fish -0.127 0.292 0.522 -0.285 -0.559 0.256 Cereals 0.861 -0.406 -0.274 0.114 Starch -0.112 -0.241 0.327 Nuts 0.114 0.166 0.123 -0.565 Fr.Veg 0.169 -0.533 -0.645 Comp.7 Comp.8 Comp.9 RedMeat WhiteMeat -0.246 -0.261 -0.146 Eggs 0.142 -0.255 0.940 Milk Fish -0.336 -0.229 Cereals Starch 0.871 -0.155 -0.167 Nuts -0.743 -0.232 Fr.Veg 0.180 0.482pcafood <- prcomp(food[,-1],scale=FALSE)
PC1 PC2 PC3 RedMeat -0.15065437 -0.13269468 -0.03183702 WhiteMeat -0.12948879 -0.04343486 0.79838375 Eggs -0.06727094 -0.02094603 0.09809250 Milk -0.42537632 -0.83085609 -0.21964423 Fish -0.12697617 0.29230731 -0.52238799 Cereals 0.86086515 -0.40616852 -0.03819580 Starch -0.06685119 0.07604862 0.03432274 Nuts 0.11390881 0.07006621 -0.16639124 Fr.Veg 0.02023530 0.16922131 -0.02217752 PC4 PC5 PC6 RedMeat -0.89605088 0.28880267 -2.290658e-01 WhiteMeat 0.18534039 0.39975064 4.418893e-05 Eggs -0.07583562 0.07877533 6.934069e-02 Milk 0.20364469 0.11700605 1.494390e-01 Fish 0.28510797 0.55887353 -2.561571e-01 Cereals 0.03449999 0.27438410 -1.138637e-01 Starch 0.11186694 0.24093950 -3.270186e-01 Nuts -0.12290210 -0.08410214 5.645635e-01 Fr.Veg -0.07364522 0.53305272 6.450846e-01 PC7 PC8 PC9 RedMeat 0.07965762 -0.06594051 0.09593232 WhiteMeat 0.24627505 -0.26052755 0.14578003 Eggs -0.14159078 -0.25530955 -0.93990324 Milk -0.03659099 -0.01145501 0.03906204 Fish 0.33586748 -0.22854504 -0.03552198 Cereals 0.01897181 -0.03504168 -0.03807529 Starch -0.87124836 -0.15513110 0.16710135 Nuts -0.08325422 -0.74349988 0.23193980 Fr.Veg -0.18012371 0.48181384 -0.01306739
结果近似了,这里少许失真是因为svd是考虑平方根,所以算平方根的时候会有位数保留出现造成失真。
这是邮件
https://stat.ethz.ch/pipermail/r-help/2011-September/289101.html