<- diag(I) / I M
Notes on Multiple Factor Analysis
The notes contain all step-by-step calculations, tables and graphics on multiple factors analysis as found in the Abdi & Valentin (2007) chapter done in R.
Preparing the raw data
Table 1 from Abdi & Valentin (2007), p.4
oaktype | fruity1 | woody1 | coffee1 | redfruit2 | roasted2 | vanillin2 | woody2 | fruity3 | butter3 | woody3 | |
---|---|---|---|---|---|---|---|---|---|---|---|
expert | NA | 1 | 1 | 1 | 2 | 2 | 2 | 2 | 3 | 3 | 3 |
wine 1 | 1 | 1 | 6 | 7 | 2 | 5 | 7 | 6 | 3 | 6 | 7 |
wine 2 | 2 | 5 | 3 | 2 | 4 | 4 | 4 | 2 | 4 | 4 | 3 |
wine 3 | 2 | 6 | 1 | 1 | 5 | 2 | 1 | 1 | 7 | 1 | 1 |
wine 4 | 2 | 7 | 1 | 2 | 7 | 2 | 1 | 2 | 2 | 2 | 2 |
wine 5 | 1 | 2 | 5 | 4 | 3 | 5 | 6 | 5 | 2 | 6 | 6 |
wine 6 | 1 | 3 | 4 | 4 | 3 | 5 | 4 | 5 | 1 | 7 | 5 |
Three studies (i.e. groups of variables) normalized by column.
Finding the global space
Assign a mass to each observation, i.e. each observation gets a weight (row weights). Here all objects get the same weight.
Divide each matrix by root of first values to standardize variance. The root of the first eigenvalue of
<- function(x) x / svd(x)$d[1]
standardize_matrix <- standardize_matrix(X1)
Z1 <- standardize_matrix(X2)
Z2 <- standardize_matrix(X3) Z3
Building the global matrix
<- cbind(Z1, Z2, Z3) Z
Computing the global PCA
The singular value decomposition of
<- svd(Z)
dec <- dec$u # or as eigendecomposition eigen(Z %*% t(Z))
U <- dec$d
d <- diag(d)
D <- dec$v V
and
and squaring gives the eigenvalues of the PCA
and
The factor score in conventional PCA would be
= diag(diag(M)^(-1/2)) %*% U %*% D
S colnames(S) <- paste0("GPC", 1:length(d)) # names global PC
<- S[, -6] # kill zero column S
The first PC contributes
<- round(d^2 / sum(d^2), 5)
con <- data.frame(PC=seq_along(d), contribution=percent(con)) k
PC | contribution |
---|---|
1 | 84.54% |
2 | 10.64% |
3 | 3.44% |
4 | 0.99% |
5 | 0.38% |
6 | 0.00% |
A plot of the six wines in the global PCA space.
plot(S, xlab = "Global PC1", ylab = "Global PC2", pch=16, col="blue",
asp=1, cex=1, main="Six wines in the global space")
abline(h=0, v=0, col="grey")
text(S, labels=1:6, pos=4)
Partial analysis
It can be shown that
can be rewritten as
This shows that the matrix
= diag(diag(M)^(-1/2)) %*% U %*% diag(diag(D)^-1)[,-6] # projection matrix, kill zero column P
The projection matrix is used to project the crossproduct of the single persons into the global compontent space using the formula.
Note the additional
<- 3
N = N * (Z1 %*% t(Z1)) %*% P
S1 = N * (Z2 %*% t(Z2)) %*% P
S2 = N * (Z3 %*% t(Z3)) %*% P
S3 colnames(S1) <- colnames(S2) <- colnames(S3) <- paste0("PC", 1:5)
The figure below shows the expert positions projected into the global space.
<- rbind(S, S1, S2, S3)
S.all plot(S.all, xlab = "Global PC1", ylab = "Global PC2", type="n",
asp=1, main="Experts projected into the global space")
abline(h=0, v=0, col="grey")
points(S, pch=16, col="blue", cex=1.5)
text(S, labels=1:6, pos=4)
<- list(S1, S2, S3)
ss for (i in seq_along(ss)) {
<- ss[[i]]
s points(s, pch=18, col=i)
for (j in 1:6)
segments(S[j,1], S[j,2], s[j,1], s[j,2], col=i)
}
The original variables and the global analysis
The loadings of the variables on the component equals the correlation between the gloabl factor score and the original or column normalized variable scores (as one is just a linear transformation the other, the correlations will be identical).
= cor(cbind(S[, 1:3], X1, X2, X3)) # calc all loadings
R.all = t(R.all[4:13, 1:3]) # variable loadings on first 3 GPCs
L
# or a bit simpler to calculate using matrix multiplication
# (as scores in U are already normalized)
= t(U[, 1:3]) %*% cbind(X1, X2, X3)
L rownames(L) <- paste0("GPC", 1:3)
Table 2 upper part from Abdi & Valentin (2007), p.11
fruity1 | woody1 | coffee1 | redfruit2 | roasted2 | vanillin2 | woody2 | fruity3 | butter3 | woody3 | |
---|---|---|---|---|---|---|---|---|---|---|
GPC1 | -0.97 | 0.98 | 0.92 | -0.89 | 0.96 | 0.95 | 0.97 | -0.59 | 0.95 | 0.99 |
GPC2 | 0.22 | -0.15 | -0.06 | 0.39 | -0.01 | -0.20 | 0.10 | -0.81 | 0.19 | 0.00 |
GPC3 | -0.02 | 0.02 | 0.37 | 0.21 | -0.28 | 0.00 | 0.14 | -0.01 | -0.24 | 0.11 |
The lower part of the table shows the correlations bewtween the eigenvectors of the standardized values of the studies (i.e.
# compute factor scores for PCA of every study
<- svd(Z1)
d1 <- svd(Z2)
d2 <- svd(Z3)
d3 = d1$u %*% diag(d1$d)
P1 = d2$u %*% diag(d2$d)
P2 = d3$u %*% diag(d3$d)
P3
# compute loadings, i.e. correlations with global factor scores
= cor(cbind(S[, 1:3], P1[, 1:2], P2[, 1:2], P3[, 1:2]))
L2 = L2[1:3, -(1:3)]
L2
# as the multiplication of U with the singular values is a linear
# transormation that will be reverted when calculating a correlations,
# we may simply use the unscaled values of U directly instead of UD
= t(svd(Z)$u[, 1:3]) # overall unscaled values for GPC 1-3
tUZ = tUZ %*% cbind(d1$u[, 1:2], d2$u[, 1:2], d3$u[, 1:2])
L2 = L2 * -1 # see note 2
L2 rownames(L2) <- paste0("GPC", 1:3)
colnames(L2) <- paste0("Exp-", rep(1:3, each=2), "-PC", rep(1:2, 3))
Table 2 lower part from Abdi & Valentin (2007), p.11 2
Exp-1-PC1 | Exp-1-PC2 | Exp-2-PC1 | Exp-2-PC2 | Exp-3-PC1 | Exp-3-PC2 | |
---|---|---|---|---|---|---|
GPC1 | 0.98 | 0.08 | 0.99 | -0.16 | 0.94 | -0.35 |
GPC2 | -0.15 | -0.28 | -0.13 | -0.76 | 0.35 | 0.94 |
GPC3 | 0.14 | -0.84 | -0.09 | -0.58 | -0.05 | 0.01 |
The study PCs and the loadings of the variables on the global PCs are plotted.
par(mfrow=c(1,3), mar=c(0,0,1,3))
for (i in 1:3) {
plot(c(-1,1), c(-1,1), type="n", asp=1, frame.plot = F,
xaxt="n", yaxt="n", xlab="", ylab="", main=paste("Study", i))
draw.circle(0,0,1, lwd=2)
# add global PCs
segments(c(-1,0), c(0,-1), c(1,0), c(0,1), col="blue", lwd=2)
text(0, .8, "GPC1", col="blue", cex=.9, pos=3)
text(.8, 0, "GPC2", col="blue", cex=.9, pos=3)
# add variable loadings in global space
= t(L[1:2, grep(i, colnames(L))])
P points(P, pch=16, col="red", cex=1.3)
text(P, labels=rownames(P), cex=.9, pos=4, xpd=T)
# add study PC loadings
= t(L2[1:2, grep(paste0("-", i), colnames(L2))])
G points(G, pch=15, col="darkgreen", cex=1.3)
text(G, labels=paste0("PC", 1:2), cex=.9, pos=2, col="darkgreen")
}
Figure 3. Circle of correlations from from Abdi & Valentin (2007), p.12
Analyzing the between study structure
The partial inertias for each study is calculated. The partial inertia refers to the contribution to the vectors in
= V^2 %*% D^2 # sum squared values for each study multiplied by eigenvalue
In <- rep(1:3, c(3,4,3)) # experts
e colnames(In) <- paste("Axis", 1:6)
<- by(In, e, colSums)
a <- do.call(rbind, a)
a <- rbind(a, colSums(a))[, -6] # remoce axis 6 as eigenvalue is zero
a rownames(a) <- c(paste("Expert", 1:3), "Eigenvalues (Lamda)")
Table 3. Partial inertias for the first three dimensions from Abdi & Valentin (2007), p.13.
Axis 1 | Axis 2 | Axis 3 | Axis 4 | Axis 5 | |
---|---|---|---|---|---|
Expert 1 | 0.96 | 0.03 | 0.05 | 0.01 | 0.01 |
Expert 2 | 0.98 | 0.06 | 0.04 | 0.02 | 0.00 |
Expert 3 | 0.90 | 0.28 | 0.03 | 0.00 | 0.00 |
Eigenvalues (Lamda) | 2.83 | 0.36 | 0.12 | 0.03 | 0.01 |
The figure below shows the partial inertias from each study and they contribution to each component.
par(mar=c(1,1,1,1))
plot(a[1:3,], xlim=c(0,1), ylim=c(0,1), pch=16, col="red",
frame.plot = F, xaxt="n", yaxt="n", cex=1.2, asp=1)
title(xlab="Dimension 1", ylab="Dimension 2", line = 0, col.lab="blue")
segments(c(0,0), c(0,0), c(1,0), c(0,1), col="blue", lwd=2)
text(a[1:3,], labels=1:3, pos=4, xpd=T)
Figure 4. Partial Inertia: Plot of the experts on the first two components. from from Abdi & Valentin (2007), p.14
Literature
Footnotes
Note however, that the term projection matrix is inaccurate, as the matrix is not idempotent, i.e.
, i.e. not a projection matrix in a strict sense.↩︎Compared to Abdi & Valentin (2007) all signs of the loadings are switched. As the directions of a PC is arbitrary, this does not mean that the result are different. Still I corrected this by multiplying by
for perfect congruence with the values from the paper.↩︎