prcompWir wollen hier (möglichst) genau untersuchen, was prcomp macht, indem wir (fast) alle Ergebnisse nachrechnen. Führen wir zunächst die PCA wieder am iris-Datensatz durch und schauen uns das Ergebnis an:
data(iris)
pca <- prcomp(iris[,-5], center = TRUE, scale = TRUE)
str(pca)
## List of 5
## $ sdev : num [1:4] 1.708 0.956 0.383 0.144
## $ rotation: num [1:4, 1:4] 0.521 -0.269 0.58 0.565 -0.377 ...
## ..- attr(*, "dimnames")=List of 2
## .. ..$ : chr [1:4] "Sepal.Length" "Sepal.Width" "Petal.Length" "Petal.Width"
## .. ..$ : chr [1:4] "PC1" "PC2" "PC3" "PC4"
## $ center : Named num [1:4] 5.84 3.06 3.76 1.2
## ..- attr(*, "names")= chr [1:4] "Sepal.Length" "Sepal.Width" "Petal.Length" "Petal.Width"
## $ scale : Named num [1:4] 0.828 0.436 1.765 0.762
## ..- attr(*, "names")= chr [1:4] "Sepal.Length" "Sepal.Width" "Petal.Length" "Petal.Width"
## $ x : num [1:150, 1:4] -2.26 -2.07 -2.36 -2.29 -2.38 ...
## ..- attr(*, "dimnames")=List of 2
## .. ..$ : NULL
## .. ..$ : chr [1:4] "PC1" "PC2" "PC3" "PC4"
## - attr(*, "class")= chr "prcomp"
Die Beschreibung des Outputs von prcomp befindet sich hier.
Wir versuchem im Folgenden, alle Komponenten der Liste
pca zu erklären. Die Komponenten sind:
names(pca) # "sdev" "rotation" "center" "scale" "x"
## [1] "sdev" "rotation" "center" "scale" "x"
Alle 5 Komponenten der Liste werden wir uns jetzt näher anschauen.
$rotationDie Haupkomponenten sind identisch mit den Eigenvektoren
der Kovarianzmatrix
der Daten. Bevor wir diese berechnen, zentrieren und skalieren wir die
vier Variablen (= Spalten) der iris-Daten, wie oben im
prcomp-Kommando:
zscore <- function(vector) (vector - mean(vector))/sd(vector) # center and scale to mean 0 and sd 1
iris0 = apply(iris[,-5], 2, zscore)
apply(iris0, 2, sd) # should be 1
## Sepal.Length Sepal.Width Petal.Length Petal.Width
## 1 1 1 1
apply(iris0, 2, mean) # should be zero, at least very small
## Sepal.Length Sepal.Width Petal.Length Petal.Width
## -4.484318e-16 2.034094e-16 -2.895326e-17 -2.989362e-17
Die Kovarianzmatrix wird aus den empirischen Daten mit Hilfe der Funktion cov berechnet. Die Eigenvektoren dieser Matrix werden dann mit der Funktion eigen ermittelt:
covmatrix = cov(iris0)
covmatrix # (we see in the main diagonal that the variance of all variables is 1)
## Sepal.Length Sepal.Width Petal.Length Petal.Width
## Sepal.Length 1.0000000 -0.1175698 0.8717538 0.8179411
## Sepal.Width -0.1175698 1.0000000 -0.4284401 -0.3661259
## Petal.Length 0.8717538 -0.4284401 1.0000000 0.9628654
## Petal.Width 0.8179411 -0.3661259 0.9628654 1.0000000
evectors = eigen(covmatrix)$vectors # eigenvectors
colnames(evectors) = c("PC1", "PC2", "PC3", "PC4")
rownames(evectors) = c("Sepal.Length", "Sepal.Width", "Petal.Length", "Petal.Width")
evectors
## PC1 PC2 PC3 PC4
## Sepal.Length 0.5210659 -0.37741762 0.7195664 0.2612863
## Sepal.Width -0.2693474 -0.92329566 -0.2443818 -0.1235096
## Petal.Length 0.5804131 -0.02449161 -0.1421264 -0.8014492
## Petal.Width 0.5648565 -0.06694199 -0.6342727 0.5235971
Wir vergleichen dieses Ergebniss mit pca$rotation:
pca$rotation
## PC1 PC2 PC3 PC4
## Sepal.Length 0.5210659 -0.37741762 0.7195664 0.2612863
## Sepal.Width -0.2693474 -0.92329566 -0.2443818 -0.1235096
## Petal.Length 0.5804131 -0.02449161 -0.1421264 -0.8014492
## Petal.Width 0.5648565 -0.06694199 -0.6342727 0.5235971
In pca$rotation sind die Eigenvektoren der
Kovarianzmatrix der Daten gespeichert. Damit ist bereits eine
Komponente des Objektes pca erklärt.
Es sei hier noch angemerkt, dass prcomp nicht den
gleichen Algorithmus wie hier angegeben benutzt, sondern aus Gründen der
numerischen Stabilität Singulärwertzerlegung
mit Hilfe der Funktion svd
ausführt. Die Ergebnisse sind jedoch identisch.
$centerIn $center sind einfach die Mittelwerten der Variablen
(= Spalten) der Originaldaten gespeichert. Zum Beweis berechnen wir die
Mittelwerte:
pca$center
## Sepal.Length Sepal.Width Petal.Length Petal.Width
## 5.843333 3.057333 3.758000 1.199333
apply(iris[,-5], 2, mean)
## Sepal.Length Sepal.Width Petal.Length Petal.Width
## 5.843333 3.057333 3.758000 1.199333
$scaleIn $scale sind einfach die Standardabweichungen der
Variablen der Originaldaten gespeichert. Zum Beweis berechnen wir
diese:
pca$scale
## Sepal.Length Sepal.Width Petal.Length Petal.Width
## 0.8280661 0.4358663 1.7652982 0.7622377
apply(iris[,-5], 2, sd)
## Sepal.Length Sepal.Width Petal.Length Petal.Width
## 0.8280661 0.4358663 1.7652982 0.7622377
$xBei der Komponente $x handelt es sich um die Koordinaten
der Punkte im PCA-Raum.
dim(pca$x)
## [1] 150 4
head(pca$x, 3)
## PC1 PC2 PC3 PC4
## [1,] -2.257141 -0.4784238 0.1272796 0.02408751
## [2,] -2.074013 0.6718827 0.2338255 0.10266284
## [3,] -2.356335 0.3407664 -0.0440539 0.02828231
Wir haben die ersten beiden Spalten pca$x[,1:2] bereits
hier
benutzt, um folgenden Plot zu machen:
color_num = as.numeric(iris$Species) + 1 # specify colours (add 1 for nicer colours)
plot(pca$x[,1:2], col = color_num, xlab = "PC1", ylab = "PC2", main = "PCA results", font.main = 1, pch = 16)
legend("topright", c("setosa", "versicolor", "virginica"), col = unique(color_num), pch = 16)
Wie werden diese Koordinaten berechnet? Dazu müssen
wir noch einmal auf den Abschnitt Theorie
zurückgreifen. Die Originaldaten (iris0) werden mit Hilfe
einer Drehmatrix in die PCA-Koordinaten überführt. Die Spalten der
Drehmatrix bestehen aus den Eigenvektoren der Kovarianzmatrix. Letztere
hatten wir schon oben erhalten:
D = evectors
D
## PC1 PC2 PC3 PC4
## Sepal.Length 0.5210659 -0.37741762 0.7195664 0.2612863
## Sepal.Width -0.2693474 -0.92329566 -0.2443818 -0.1235096
## Petal.Length 0.5804131 -0.02449161 -0.1421264 -0.8014492
## Petal.Width 0.5648565 -0.06694199 -0.6342727 0.5235971
Ebenso gut hätten wir natürlich auch pca$rotation als
Drehmatrix nehmen können, der Name kommt nicht von ungefähr:
pca$rotation
## PC1 PC2 PC3 PC4
## Sepal.Length 0.5210659 -0.37741762 0.7195664 0.2612863
## Sepal.Width -0.2693474 -0.92329566 -0.2443818 -0.1235096
## Petal.Length 0.5804131 -0.02449161 -0.1421264 -0.8014492
## Petal.Width 0.5648565 -0.06694199 -0.6342727 0.5235971
Die neuen PCA-Koordinaten - nennen wir sie \(U\) - werden nun, wie schon hier erwähnt, durch Multiplikation der skalierten Originaldaten (\(X\)) mit der Drehmatrix (\(D\)) errechnet:
\[ \boldsymbol{U} = \boldsymbol{X} \cdot \boldsymbol{D} \]
Wir führen die Matrixmultiplikation in R aus:
U = iris0 %*% D
Wir vergleichen U mit den in pca$x
gespeicherten Daten:
head(U)
## PC1 PC2 PC3 PC4
## [1,] -2.257141 -0.4784238 0.12727962 0.024087508
## [2,] -2.074013 0.6718827 0.23382552 0.102662845
## [3,] -2.356335 0.3407664 -0.04405390 0.028282305
## [4,] -2.291707 0.5953999 -0.09098530 -0.065735340
## [5,] -2.381863 -0.6446757 -0.01568565 -0.035802870
## [6,] -2.068701 -1.4842053 -0.02687825 0.006586116
head(pca$x)
## PC1 PC2 PC3 PC4
## [1,] -2.257141 -0.4784238 0.12727962 0.024087508
## [2,] -2.074013 0.6718827 0.23382552 0.102662845
## [3,] -2.356335 0.3407664 -0.04405390 0.028282305
## [4,] -2.291707 0.5953999 -0.09098530 -0.065735340
## [5,] -2.381863 -0.6446757 -0.01568565 -0.035802870
## [6,] -2.068701 -1.4842053 -0.02687825 0.006586116
all(abs(pca$x - U) < 1e-14) # we cannot expect exact agreement because of numerical errors
## [1] TRUE
Wir können also zusammenfassen, dass die neuen (PCA-) Koordinaten aus den Originaldaten durch Multiplikation mit der Drehmatrix enstehen. Die Spalten der Drehmatrix bestehen aus den Eigenvektoren der Kovarianzmatrix der Daten.
$sdevDas Subset sdev enthält die Standardabweichungen
entlang der Hauptkomponenten, also die Standardabweichungen der Spalten
der neuen (PCA-) Daten \(\boldsymbol{U}\).
pca$sdev
## [1] 1.7083611 0.9560494 0.3830886 0.1439265
apply(U, 2, sd) # U = rotated data
## PC1 PC2 PC3 PC4
## 1.7083611 0.9560494 0.3830886 0.1439265
Die Eigenwerte der Kovarianzmatrix der Originaldaten sind gleich den Varianzen der neuen Daten, deren Quadratwurzeln sind also ebenfalls die Standardabweichungen der neuen Daten:
evalues = eigen(cov(iris0))$values
sqrt(evalues)
## [1] 1.7083611 0.9560494 0.3830886 0.1439265
Die Diagonalelemente der Kovarinzmatrix der neuen Daten \(\boldsymbol{U}\) sind die Varianzen der neuen Daten, deren Quadratwurzeln sind also ebenfalls die Standardabweichungen der neuen Daten:
sqrt(diag(cov(U)))
## PC1 PC2 PC3 PC4
## 1.7083611 0.9560494 0.3830886 0.1439265
Zusammenssend stellen wir fest, dass folgende Vektoren identisch sind:
pca$dev des prcomp-
OutputsDie relativen Anteile der einzelnen Hauptkomponenten an der Varianz kann man durch Normierung der Eigenwerte auf 1 oder auf 100% berechnen:
perc = evalues/sum(evalues)*100 # proportions (%)
round(perc, 2)
## [1] 72.96 22.85 3.67 0.52
Note that all code snippets are designed for comprehensibility, not for efficiency!