Erwägen Sie, den Beitrag von @ amoeba und @ttnphns zu verbessern . Vielen Dank für Ihre Hilfe und Ihre Ideen.
Das Folgende stützt sich auf den Iris-Datensatz in R und insbesondere auf die ersten drei Variablen (Spalten) : Sepal.Length, Sepal.Width, Petal.Length
.
Ein Biplot kombiniert ein Belastungsdiagramm (nicht standardisierte Eigenvektoren) - in Beton die ersten beiden Belastungen - und ein Bewertungsdiagramm (gedrehte und erweiterte Datenpunkte, dargestellt in Bezug auf Hauptkomponenten). Unter Verwendung des gleichen Datensatzes beschreibt @amoeba 9 mögliche Kombinationen von PCA-Biplots basierend auf 3 möglichen Normalisierungen des Score-Plots der ersten und zweiten Hauptkomponente und 3 Normalisierungen des Ladeplots (Pfeile) der Anfangsvariablen. Um zu sehen, wie R mit diesen möglichen Kombinationen umgeht, ist es interessant, die biplot()
Methode zu betrachten:
Zuerst die lineare Algebra zum Kopieren und Einfügen bereit:
X = as.matrix(iris[,1:3]) # Three first variables of Iris dataset
CEN = scale(X, center = T, scale = T) # Centering and scaling the data
PCA = prcomp(CEN)
# EIGENVECTORS:
(evecs.ei = eigen(cor(CEN))$vectors) # Using eigen() method
(evecs.svd = svd(CEN)$v) # PCA with SVD...
(evecs = prcomp(CEN)$rotation) # Confirming with prcomp()
# EIGENVALUES:
(evals.ei = eigen(cor(CEN))$values) # Using the eigen() method
(evals.svd = svd(CEN)$d^2/(nrow(X) - 1)) # and SVD: sing.values^2/n - 1
(evals = prcomp(CEN)$sdev^2) # with prcomp() (needs squaring)
# SCORES:
scr.svd = svd(CEN)$u %*% diag(svd(CEN)$d) # with SVD
scr = prcomp(CEN)$x # with prcomp()
scr.mm = CEN %*% prcomp(CEN)$rotation # "Manually" [data] [eigvecs]
# LOADINGS:
loaded = evecs %*% diag(prcomp(CEN)$sdev) # [E-vectors] [sqrt(E-values)]
1. Reproduzieren des Ladeplots (Pfeile):
Hier hilft die geometrische Interpretation dieses Beitrags von @ttnphns sehr. Die Notation des Diagramms im Beitrag wurde beibehalten: steht für die Variable im Betreffraum . ist der entsprechende Pfeil, der letztendlich aufgetragen ist; und die Koordinaten und sind die Komponenten, die eine Variable in Bezug auf und :h ' a 1 a 2 V PC 1 PC 2VSepal L.
h′a1a2VPC1PC2
Die Komponente der Variablen Sepal L.
in Bezug auf lautet dann:PC1
a1=h⋅cos(ϕ)
welche, wenn die Bewertungen in Bezug auf - nennen wir sie - standardisiert sind, so dass ihreS 1PC1S1
∥S1∥=∑n1scores21−−−−−−−−−√=1 , die obige Gleichung entspricht dem Punktprodukt :V⋅S1
a1=V⋅S1=∥V∥∥S1∥cos(ϕ)=h×1×⋅cos(ϕ)(1)
Da ,∥V∥=∑x2−−−−√
Var(V)−−−−−√=∑x2−−−−√n−1−−−−−√=∥V∥n−1−−−−−√⟹∥V∥=h=var(V)−−−−−√n−1−−−−−√.
Gleichfalls,
∥S1∥=1=var(S1)−−−−−√n−1−−−−−√.
Zurück zu Gl. ,(1)
a1=h×1×⋅cos(ϕ)=var(V)−−−−−√var(S1)−−−−−√cos(θ)(n−1)
cos(ϕ) kann daher ein betrachtet werden Pearson Korrelationskoeffizient , , mit dem Vorbehalt , dass ich die Falten des nicht verstehen - Faktors.rn−1
Die roten Pfeile von duplizieren und blau überlappen biplot()
par(mfrow = c(1,2)); par(mar=c(1.2,1.2,1.2,1.2))
biplot(PCA, cex = 0.6, cex.axis = .6, ann = F, tck=-0.01) # R biplot
# R biplot with overlapping (reproduced) arrows in blue completely covering red arrows:
biplot(PCA, cex = 0.6, cex.axis = .6, ann = F, tck=-0.01)
arrows(0, 0,
cor(X[,1], scr[,1]) * 0.8 * sqrt(nrow(X) - 1),
cor(X[,1], scr[,2]) * 0.8 * sqrt(nrow(X) - 1),
lwd = 1, angle = 30, length = 0.1, col = 4)
arrows(0, 0,
cor(X[,2], scr[,1]) * 0.8 * sqrt(nrow(X) - 1),
cor(X[,2], scr[,2]) * 0.8 * sqrt(nrow(X) - 1),
lwd = 1, angle = 30, length = 0.1, col = 4)
arrows(0, 0,
cor(X[,3], scr[,1]) * 0.8 * sqrt(nrow(X) - 1),
cor(X[,3], scr[,2]) * 0.8 * sqrt(nrow(X) - 1),
lwd = 1, angle = 30, length = 0.1, col = 4)
Sehenswürdigkeiten:
- Die Pfeile können als Korrelation der ursprünglichen Variablen mit den von den ersten beiden Hauptkomponenten erzeugten Bewertungen reproduziert werden.
- Alternativ kann dies wie im ersten Plot in der zweiten Zeile erreicht werden, der in @ amoebas Beitrag mit ist:V∗S
oder im R-Code:
biplot(PCA, cex = 0.6, cex.axis = .6, ann = F, tck=-0.01) # R biplot
# R biplot with overlapping arrows in blue completely covering red arrows:
biplot(PCA, cex = 0.6, cex.axis = .6, ann = F, tck=-0.01)
arrows(0, 0,
(svd(CEN)$v %*% diag(svd(CEN)$d))[1,1] * 0.8,
(svd(CEN)$v %*% diag(svd(CEN)$d))[1,2] * 0.8,
lwd = 1, angle = 30, length = 0.1, col = 4)
arrows(0, 0,
(svd(CEN)$v %*% diag(svd(CEN)$d))[2,1] * 0.8,
(svd(CEN)$v %*% diag(svd(CEN)$d))[2,2] * 0.8,
lwd = 1, angle = 30, length = 0.1, col = 4)
arrows(0, 0,
(svd(CEN)$v %*% diag(svd(CEN)$d))[3,1] * 0.8,
(svd(CEN)$v %*% diag(svd(CEN)$d))[3,2] * 0.8,
lwd = 1, angle = 30, length = 0.1, col = 4)
oder sogar noch ...
biplot(PCA, cex = 0.6, cex.axis = .6, ann = F, tck=-0.01) # R biplot
# R biplot with overlapping (reproduced) arrows in blue completely covering red arrows:
biplot(PCA, cex = 0.6, cex.axis = .6, ann = F, tck=-0.01)
arrows(0, 0,
(loaded)[1,1] * 0.8 * sqrt(nrow(X) - 1),
(loaded)[1,2] * 0.8 * sqrt(nrow(X) - 1),
lwd = 1, angle = 30, length = 0.1, col = 4)
arrows(0, 0,
(loaded)[2,1] * 0.8 * sqrt(nrow(X) - 1),
(loaded)[2,2] * 0.8 * sqrt(nrow(X) - 1),
lwd = 1, angle = 30, length = 0.1, col = 4)
arrows(0, 0,
(loaded)[3,1] * 0.8 * sqrt(nrow(X) - 1),
(loaded)[3,2] * 0.8 * sqrt(nrow(X) - 1),
lwd = 1, angle = 30, length = 0.1, col = 4)
Verbindung mit der geometrischen Erklärung von Ladungen durch @ttnphns oder diesem anderen informativen Beitrag auch durch @ttnphns .
Es gibt einen Skalierungsfaktor : sqrt(nrow(X) - 1)
, der ein bisschen rätselhaft bleibt.
0.8 hat mit der Schaffung von Platz für das Etikett zu tun - siehe diesen Kommentar hier :
Außerdem sollte man sagen, dass die Pfeile so gezeichnet sind, dass die Mitte der Textbeschriftung dort ist, wo sie sein sollte! Die Pfeile werden dann vor dem Plotten mit 0,80,8 multipliziert, dh alle Pfeile sind kürzer als sie sein sollten, vermutlich um eine Überlappung mit der Textbeschriftung zu vermeiden (siehe Code für biplot.default). Ich finde das äußerst verwirrend. - Amöbe 19. März 15 um 10:06 Uhr
2. Zeichnen des biplot()
Punktediagramms (und der Pfeile gleichzeitig):
Die Achsen werden auf die Einheitssumme der Quadrate skaliert, die dem ersten Diagramm der ersten Zeile auf @ amöbens Post entspricht , das reproduziert werden kann, indem die Matrix der svd-Zerlegung (dazu später mehr) - " Spalten von : Dies sind Hauptkomponenten, die auf die Einheitssumme der Quadrate skaliert sind. "UU
Bei der Biplot-Konstruktion spielen auf der unteren und oberen horizontalen Achse zwei verschiedene Skalen eine Rolle:
Die relative Skalierung ist jedoch nicht sofort ersichtlich und erfordert eine Untersuchung der Funktionen und Methoden:
biplot()
Zeichnet Scores als Spalten von in SVD, die orthogonale Einheitsvektoren sind:U
> scr.svd = svd(CEN)$u %*% diag(svd(CEN)$d)
> U = svd(CEN)$u
> apply(U, 2, function(x) sum(x^2))
[1] 1 1 1
Während die prcomp()
Funktion in R die auf ihre Eigenwerte skalierten Scores zurückgibt:
> apply(scr, 2, function(x) var(x)) # pr.comp() scores scaled to evals
PC1 PC2 PC3
2.02142986 0.90743458 0.07113557
> evals #... here is the proof:
[1] 2.02142986 0.90743458 0.07113557
Daher können wir die Varianz durch Teilen durch die Eigenwerte auf skalieren :1
> scr_var_one = scr/sqrt(evals)[col(scr)] # to scale to var = 1
> apply(scr_var_one, 2, function(x) var(x)) # proved!
[1] 1 1 1
Da die Summe der Quadrate jedoch , müssen wir durch dividieren, weil:1n−1−−−−−√
var(scr_var_one)=1=∑n1scr_var_onen−1
> scr_sum_sqrs_one = scr_var_one / sqrt(nrow(scr) - 1) # We / by sqrt n - 1.
> apply(scr_sum_sqrs_one, 2, function(x) sum(x^2)) #... proving it...
PC1 PC2 PC3
1 1 1
Zu beachten ist, dass die Verwendung des Skalierungsfaktors später in geändert wird, wenn die Definition der Erklärung in der Tatsache zu liegen scheint, dassn−1−−−−−√n−−√lan
prcomp
verwendet : "Im Gegensatz zu Princomp werden Varianzen mit dem üblichen Divisor berechnet ".n−1n−1
Nachdem Sie alle if
Aussagen und andere Reinigungsmittel entfernt haben, gehen Sie biplot()
wie folgt vor:
X = as.matrix(iris[,1:3]) # The original dataset
CEN = scale(X, center = T, scale = T) # Centered and scaled
PCA = prcomp(CEN) # PCA analysis
par(mfrow = c(1,2)) # Splitting the plot in 2.
biplot(PCA) # In-built biplot() R func.
# Following getAnywhere(biplot.prcomp):
choices = 1:2 # Selecting first two PC's
scale = 1 # Default
scores= PCA$x # The scores
lam = PCA$sdev[choices] # Sqrt e-vals (lambda) 2 PC's
n = nrow(scores) # no. rows scores
lam = lam * sqrt(n) # See below.
# at this point the following is called...
# biplot.default(t(t(scores[,choices]) / lam),
# t(t(x$rotation[,choices]) * lam))
# Following from now on getAnywhere(biplot.default):
x = t(t(scores[,choices]) / lam) # scaled scores
# "Scores that you get out of prcomp are scaled to have variance equal to
# the eigenvalue. So dividing by the sq root of the eigenvalue (lam in
# biplot) will scale them to unit variance. But if you want unit sum of
# squares, instead of unit variance, you need to scale by sqrt(n)" (see comments).
# > colSums(x^2)
# PC1 PC2
# 0.9933333 0.9933333 # It turns out that the it's scaled to sqrt(n/(n-1)),
# ...rather than 1 (?) - 0.9933333=149/150
y = t(t(PCA$rotation[,choices]) * lam) # scaled eigenvecs (loadings)
n = nrow(x) # Same as dataset (150)
p = nrow(y) # Three var -> 3 rows
# Names for the plotting:
xlabs = 1L:n
xlabs = as.character(xlabs) # no. from 1 to 150
dimnames(x) = list(xlabs, dimnames(x)[[2L]]) # no's and PC1 / PC2
ylabs = dimnames(y)[[1L]] # Iris species
ylabs = as.character(ylabs)
dimnames(y) <- list(ylabs, dimnames(y)[[2L]]) # Species and PC1/PC2
# Function to get the range:
unsigned.range = function(x) c(-abs(min(x, na.rm = TRUE)),
abs(max(x, na.rm = TRUE)))
rangx1 = unsigned.range(x[, 1L]) # Range first col x
# -0.1418269 0.1731236
rangx2 = unsigned.range(x[, 2L]) # Range second col x
# -0.2330564 0.2255037
rangy1 = unsigned.range(y[, 1L]) # Range 1st scaled evec
# -6.288626 11.986589
rangy2 = unsigned.range(y[, 2L]) # Range 2nd scaled evec
# -10.4776155 0.8761695
(xlim = ylim = rangx1 = rangx2 = range(rangx1, rangx2))
# range(rangx1, rangx2) = -0.2330564 0.2255037
# And the critical value is the maximum of the ratios of ranges of
# scaled e-vectors / scaled scores:
(ratio = max(rangy1/rangx1, rangy2/rangx2))
# rangy1/rangx1 = 26.98328 53.15472
# rangy2/rangx2 = 44.957418 3.885388
# ratio = 53.15472
par(pty = "s") # Calling a square plot
# Plotting a box with x and y limits -0.2330564 0.2255037
# for the scaled scores:
plot(x, type = "n", xlim = xlim, ylim = ylim) # No points
# Filling in the points as no's and the PC1 and PC2 labels:
text(x, xlabs)
par(new = TRUE) # Avoids plotting what follows separately
# Setting now x and y limits for the arrows:
(xlim = xlim * ratio) # We multiply the original limits x ratio
# -16.13617 15.61324
(ylim = ylim * ratio) # ... for both the x and y axis
# -16.13617 15.61324
# The following doesn't change the plot intially...
plot(y, axes = FALSE, type = "n",
xlim = xlim,
ylim = ylim, xlab = "", ylab = "")
# ... but it does now by plotting the ticks and new limits...
# ... along the top margin (3) and the right margin (4)
axis(3); axis(4)
text(y, labels = ylabs, col = 2) # This just prints the species
arrow.len = 0.1 # Length of the arrows about to plot.
# The scaled e-vecs are further reduced to 80% of their value
arrows(0, 0, y[, 1L] * 0.8, y[, 2L] * 0.8,
length = arrow.len, col = 2)
die wie erwartet die biplot()
Ausgabe, wie sie direkt aufgerufen wird (rechtes Bild biplot(PCA)
unten), in all ihren unberührten ästhetischen Mängeln reproduziert (rechtes Bild unten) :
Sehenswürdigkeiten:
- Die Pfeile sind auf einer Skala aufgetragen, die sich auf das maximale Verhältnis zwischen dem skalierten Eigenvektor jeder der beiden Hauptkomponenten und ihren jeweiligen skalierten Bewertungen (der
ratio
) bezieht . AS @amoeba Kommentare:
Das Streudiagramm und das "Pfeildiagramm" sind so skaliert, dass die größte (im absoluten Wert) x- oder y-Pfeilkoordinate der Pfeile genau gleich der größten (im absoluten Wert) x- oder y-Koordinate der gestreuten Datenpunkte war
- Wie oben erwartet, können die Punkte direkt als Punktzahlen in der Matrix der SVD dargestellt werden:U