Dopo l'eccellente post di JD Long in questo thread, ho cercato un semplice esempio e il codice R necessario per produrre il PCA e poi tornare ai dati originali. Mi ha dato un po 'di intuizione geometrica di prima mano e voglio condividere ciò che ho ottenuto. Il set di dati e il codice possono essere direttamente copiati e incollati in R form Github .
Ho usato un set di dati che ho trovato online sui semiconduttori qui , e l'ho ridotto a due dimensioni - "numero atomico" e "punto di fusione" - per facilitare la stampa.
Come avvertimento, l'idea è puramente illustrativa del processo computazionale: la PCA viene utilizzata per ridurre più di due variabili a pochi componenti principali derivati, o per identificare la collinearità anche nel caso di più funzionalità. Quindi non troverebbe molta applicazione nel caso di due variabili, né sarebbe necessario calcolare gli autovettori delle matrici di correlazione come sottolineato da @amoeba.
Inoltre, ho troncato le osservazioni da 44 a 15 per facilitare il compito di tracciare i singoli punti. Il risultato finale è stato un frame di dati scheletro ( dat1
):
compounds atomic.no melting.point
AIN 10 498.0
AIP 14 625.0
AIAs 23 1011.5
... ... ...
La colonna "composti" indica la costituzione chimica del semiconduttore e svolge il ruolo di nome della riga.
Questo può essere riprodotto come segue (pronto per copiare e incollare sulla console 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]
I dati sono stati quindi ridimensionati:
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)`
Seguirono i passi lineari dell'algebra:
C <- cov(X) # Covariance matrix (centered data)
⎡⎣⎢at_nomelt_pat_no10.296melt_p0.2961⎤⎦⎥
La funzione di correlazione cor(dat1)
fornisce lo stesso output sui dati non ridimensionati della funzione cov(X)
sui dati ridimensionati.
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⎤⎦⎥
Poiché il primo autovettore inizialmente ritorna come scegliamo di cambiarlo in per renderlo coerente con le formule integrate attraverso:∼[−0.7,−0.7][0.7,0.7]
e_vectors[,1] = - e_vectors[,1]; colnames(e_vectors) <- c("PC1","PC2")
Gli autovalori risultanti erano e . In condizioni meno minimaliste, questo risultato avrebbe aiutato a decidere quali autovettori includere (autovalori più grandi). Ad esempio, il contributo relativo del primo autovalore è : significa che rappresenta della variabilità nei dati. La variabilità nella direzione del secondo autovettore è del . Questo è in genere mostrato su un diagramma a ghiaia che rappresenta il valore degli autovalori:1.29642170.703578364.8%eigen(C)$values[1]/sum(eigen(C)$values) * 100
∼65%35.2%
Includeremo entrambi gli autovettori data la piccola dimensione di questo esempio di set di dati giocattolo, comprendendo che l'esclusione di uno degli autovettori comporterebbe una riduzione della dimensionalità - l'idea alla base di PCA.
La matrice del punteggio è stata determinata come moltiplicazione della matrice dei dati in scala ( X
) per la matrice degli autovettori (o "rotazioni") :
score_matrix <- X %*% e_vectors
# Identical to the often found operation: t(t(e_vectors) %*% t(X))
Il concetto implica una combinazione lineare di ciascuna voce (riga / soggetto / osservazione / superconduttore in questo caso) dei dati centrati (e in questo caso ridimensionati) ponderati dalle righe di ciascun autovettore , in modo che in ciascuna delle colonne finali del matrice punteggio, troveremo un contributo di ciascuna variabile (colonna) dei dati (l'intero X
), ma solo il corrispondente autovettore avrà partecipato calcolo (cioè il primo autovettore sarà contribuire a (componente principale 1) e a , come in: PC[0.7,0.7]T[ 0,7 , - 0,7 ] T PCPC1[0.7,−0.7]TPC2
Pertanto ogni autovettore influenzerà ogni variabile in modo diverso, e questo si rifletterà nei "caricamenti" del PCA. Nel nostro caso, il segno negativo nel secondo componente del secondo autovettore cambierà il segno dei valori del punto di fusione nelle combinazioni lineari che producono PC2, mentre l'effetto del primo autovettore sarà costantemente positivo: [0.7,−0.7]
Gli autovettori sono ridimensionati su :1
> apply(e_vectors, 2, function(x) sum(x^2))
PC1 PC2
1 1
mentre i ( caricamenti ) sono gli autovettori scalati in base agli autovalori (nonostante la terminologia confusa nelle funzioni R integrate visualizzate di seguito). Di conseguenza, i carichi possono essere calcolati come:
> 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
È interessante notare che la nuvola di dati ruotata (il diagramma del punteggio) avrà una varianza lungo ciascun componente (PC) uguale agli autovalori:
> apply(score_matrix, 2, function(x) var(x))
PC1 PC2
53829.7896 110.8414
> lambda
[1] 53829.7896 110.8414
Utilizzando le funzioni integrate i risultati possono essere replicati:
# 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
In alternativa, è possibile applicare il metodo di decomposizione del valore singolare ( ) per calcolare manualmente il PCA; in effetti, questo è il metodo utilizzato in . I passaggi possono essere indicati come: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
Il risultato è mostrato di seguito, con il primo, le distanze dai singoli punti al primo autovettore, e su un secondo diagramma, le distanze ortogonali al secondo autovettore:
Se invece tracciassimo i valori della matrice dei punteggi (PC1 e PC2) - non più "melting.point" e "atomic.no", ma un cambiamento di base delle coordinate del punto con gli autovettori come base, queste distanze sarebbero conservato, ma diventerebbe naturalmente perpendicolare all'asse xy:
Il trucco era ora di recuperare i dati originali . I punti erano stati trasformati attraverso una semplice moltiplicazione di matrice da parte degli autovettori. Ora i dati sono stati ruotati indietro moltiplicando per l' inverso della matrice di autovettori con un conseguente marcato cambiamento nella posizione dei punti dati. Ad esempio, notare la modifica del punto rosa "GaN" nel quadrante superiore sinistro (cerchio nero nel diagramma sinistro, in basso), tornando alla sua posizione iniziale nel quadrante inferiore sinistro (cerchio nero nel diagramma destro, in basso).
Ora abbiamo finalmente ripristinato i dati originali in questa matrice "de-ruotata":
Oltre al cambiamento delle coordinate di rotazione dei dati in PCA, i risultati devono essere interpretati e questo processo tende a coinvolgere un biplot
, su cui sono tracciati i punti di dati rispetto alle nuove coordinate di autovettore e le variabili originali sono ora sovrapposte come vettori. È interessante notare l'equivalenza nella posizione dei punti tra i grafici nella seconda riga dei grafici di rotazione sopra ("Punteggi con asse xy = autovettori") (a sinistra nei grafici che seguono), e il biplot
(a giusto):
La sovrapposizione delle variabili originali come frecce rosse offre un percorso per l'interpretazione di PC1
un vettore nella direzione (o con una correlazione positiva) con entrambi atomic no
e melting point
; e di PC2
come componente lungo valori crescenti atomic no
ma negativamente correlati melting point
, coerenti con i valori degli autovettori:
PCA$rotation
PC1 PC2
atomic.no 0.7071068 0.7071068
melting.point 0.7071068 -0.7071068
Questo tutorial interattivo di Victor Powell fornisce un feedback immediato sulle modifiche degli autovettori quando il cloud di dati viene modificato.