Después de la excelente publicación de JD Long en este hilo, busqué un ejemplo simple y el código R necesario para producir el PCA y luego volver a los datos originales. Me dio algo de intuición geométrica de primera mano, y quiero compartir lo que obtuve. El conjunto de datos y el código pueden copiarse directamente y pegarse en R desde Github .
Utilicé un conjunto de datos que encontré en línea en semiconductores aquí , y lo recorté a solo dos dimensiones - "número atómico" y "punto de fusión" - para facilitar el trazado.
Como advertencia, la idea es puramente ilustrativa del proceso computacional: PCA se utiliza para reducir más de dos variables a unos pocos componentes principales derivados, o para identificar colinealidad también en el caso de múltiples características. Por lo tanto, no encontraría mucha aplicación en el caso de dos variables, ni sería necesario calcular los vectores propios de las matrices de correlación como señaló @amoeba.
Además, trunqué las observaciones del 44 al 15 para facilitar la tarea de rastrear puntos individuales. El resultado final fue un marco de datos esqueleto ( dat1
):
compounds atomic.no melting.point
AIN 10 498.0
AIP 14 625.0
AIAs 23 1011.5
... ... ...
La columna de "compuestos" indica la constitución química del semiconductor y desempeña el papel de nombre de fila.
Esto se puede reproducir de la siguiente manera (listo para copiar y pegar en la consola R):
dat <- read.csv(url("http://rinterested.github.io/datasets/semiconductors"))
colnames(dat)[2] <- "atomic.no"
dat1 <- subset(dat[1:15,1:3])
row.names(dat1) <- dat1$compounds
dat1 <- dat1[,-1]
Los datos fueron luego escalados:
X <- apply(dat1, 2, function(x) (x - mean(x)) / sd(x))
# This centers data points around the mean and standardizes by dividing by SD.
# It is the equivalent to `X <- scale(dat1, center = T, scale = T)`
Los pasos de álgebra lineal siguieron:
C <- cov(X) # Covariance matrix (centered data)
⎡⎣⎢at_nomelt_pat_no10.296melt_p0.2961⎤⎦⎥
La función de correlación cor(dat1)
proporciona el mismo resultado en los datos no escalados que la función cov(X)
en los datos escalados.
lambda <- eigen(C)$values # Eigenvalues
lambda_matrix <- diag(2)*eigen(C)$values # Eigenvalues matrix
⎡⎣⎢λPC11.2964220λPC200.7035783⎤⎦⎥
e_vectors <- eigen(C)$vectors # Eigenvectors
12√⎡⎣⎢PC111PC21−1⎤⎦⎥
∼[−0.7,−0.7][0.7,0.7]
e_vectors[,1] = - e_vectors[,1]; colnames(e_vectors) <- c("PC1","PC2")
1.29642170.703578364.8%eigen(C)$values[1]/sum(eigen(C)$values) * 100
∼65%35.2%
Incluiremos ambos vectores propios dado el pequeño tamaño de este ejemplo de conjunto de datos de juguetes, entendiendo que excluir uno de los vectores propios daría como resultado una reducción de la dimensionalidad, la idea detrás de PCA.
La matriz de puntuación se determinó como la multiplicación matricial de los datos escalados ( X
) por la matriz de vectores propios (o "rotaciones") :
score_matrix <- X %*% e_vectors
# Identical to the often found operation: t(t(e_vectors) %*% t(X))
X
[0.7,0.7]TPC1[0.7,−0.7]TPC2
[0.7,−0.7]
1
> apply(e_vectors, 2, function(x) sum(x^2))
PC1 PC2
1 1
mientras que las ( cargas ) son los vectores propios escalados por los valores propios (a pesar de la terminología confusa en las funciones R incorporadas que se muestran a continuación). En consecuencia, las cargas se pueden calcular como:
> e_vectors %*% lambda_matrix
[,1] [,2]
[1,] 0.9167086 0.497505
[2,] 0.9167086 -0.497505
> prcomp(X)$rotation %*% diag(princomp(covmat = C)$sd^2)
[,1] [,2]
atomic.no 0.9167086 0.497505
melting.point 0.9167086 -0.497505
Es interesante notar que la nube de datos rotados (la gráfica de puntaje) tendrá una varianza a lo largo de cada componente (PC) igual a los valores propios:
> apply(score_matrix, 2, function(x) var(x))
PC1 PC2
53829.7896 110.8414
> lambda
[1] 53829.7896 110.8414
Utilizando las funciones integradas, los resultados se pueden replicar:
# For the SCORE MATRIX:
prcomp(X)$x
# or...
princomp(X)$scores # The signs of the PC 1 column will be reversed.
# and for EIGENVECTOR MATRIX:
prcomp(X)$rotation
# or...
princomp(X)$loadings
# and for EIGENVALUES:
prcomp(X)$sdev^2
# or...
princomp(covmat = C)$sd^2
UΣVTprcomp()
svd_scaled_dat <-svd(scale(dat1))
eigen_vectors <- svd_scaled_dat$v
eigen_values <- (svd_scaled_dat$d/sqrt(nrow(dat1) - 1))^2
scores<-scale(dat1) %*% eigen_vectors
El resultado se muestra a continuación, con primero, las distancias desde los puntos individuales al primer vector propio, y en un segundo gráfico, las distancias ortogonales al segundo vector propio:
Si, en cambio, graficamos los valores de la matriz de puntaje (PC1 y PC2), ya no "melting.point" y "atomic.no", sino realmente un cambio de base de las coordenadas de puntos con los vectores propios como base, estas distancias serían conservado, pero naturalmente se volvería perpendicular al eje xy:
El truco ahora era recuperar los datos originales . Los puntos se habían transformado mediante una simple multiplicación matricial por los vectores propios. Ahora los datos se volvieron a multiplicar por la inversa de la matriz de vectores propios con un cambio marcado resultante en la ubicación de los puntos de datos. Por ejemplo, observe el cambio en el punto rosado "GaN" en el cuadrante superior izquierdo (círculo negro en el diagrama izquierdo, abajo), volviendo a su posición inicial en el cuadrante inferior izquierdo (círculo negro en el diagrama derecho, abajo).
Ahora finalmente tuvimos los datos originales restaurados en esta matriz "des-rotada":
Más allá del cambio de coordenadas de rotación de los datos en PCA, los resultados deben ser interpretados, y este proceso tiende a involucrar a biplot
, en el cual los puntos de datos se trazan con respecto a las nuevas coordenadas del vector propio, y las variables originales ahora se superponen como vectores Es interesante observar la equivalencia en la posición de los puntos entre las parcelas en la segunda fila de gráficos de rotación anteriores ("Puntajes con eje xy = vectores propios") (a la izquierda en las parcelas que siguen), y el biplot
(a la derecho):
La superposición de las variables originales como flechas rojas ofrece un camino hacia la interpretación de PC1
un vector en la dirección (o con una correlación positiva) con ambos atomic no
y melting point
; y PC2
como un componente a lo largo de valores crecientes de atomic no
pero negativamente correlacionados con melting point
, consistentes con los valores de los vectores propios:
PCA$rotation
PC1 PC2
atomic.no 0.7071068 0.7071068
melting.point 0.7071068 -0.7071068
Este tutorial interactivo de Victor Powell brinda comentarios inmediatos sobre los cambios en los vectores propios a medida que se modifica la nube de datos.