Pracuję w R poprzez doskonały samouczek PCA autorstwa Lindsay I Smith i utknąłem w ostatnim etapie. Poniższy skrypt R przenosi nas do etapu (na str. 19), na którym odtwarzane są oryginalne dane z (w tym przypadku pojedynczego) głównego elementu, który powinien dać wykres linii prostej wzdłuż osi PCA1 (biorąc pod uwagę, że dane ma tylko 2 wymiary, z których drugi jest celowo upuszczany).
d = data.frame(x=c(2.5,0.5,2.2,1.9,3.1,2.3,2.0,1.0,1.5,1.1),
y=c(2.4,0.7,2.9,2.2,3.0,2.7,1.6,1.1,1.6,0.9))
# mean-adjusted values
d$x_adj = d$x - mean(d$x)
d$y_adj = d$y - mean(d$y)
# calculate covariance matrix and eigenvectors/values
(cm = cov(d[,1:2]))
#### outputs #############
# x y
# x 0.6165556 0.6154444
# y 0.6154444 0.7165556
##########################
(e = eigen(cm))
##### outputs ##############
# $values
# [1] 1.2840277 0.0490834
#
# $vectors
# [,1] [,2]
# [1,] 0.6778734 -0.7351787
# [2,] 0.7351787 0.6778734
###########################
# principal component vector slopes
s1 = e$vectors[1,1] / e$vectors[2,1] # PC1
s2 = e$vectors[1,2] / e$vectors[2,2] # PC2
plot(d$x_adj, d$y_adj, asp=T, pch=16, xlab='x', ylab='y')
abline(a=0, b=s1, col='red')
abline(a=0, b=s2)
# PCA data = rowFeatureVector (transposed eigenvectors) * RowDataAdjust (mean adjusted, also transposed)
feat_vec = t(e$vectors)
row_data_adj = t(d[,3:4])
final_data = data.frame(t(feat_vec %*% row_data_adj)) # ?matmult for details
names(final_data) = c('x','y')
#### outputs ###############
# final_data
# x y
# 1 0.82797019 -0.17511531
# 2 -1.77758033 0.14285723
# 3 0.99219749 0.38437499
# 4 0.27421042 0.13041721
# 5 1.67580142 -0.20949846
# 6 0.91294910 0.17528244
# 7 -0.09910944 -0.34982470
# 8 -1.14457216 0.04641726
# 9 -0.43804614 0.01776463
# 10 -1.22382056 -0.16267529
############################
# final_data[[1]] = -final_data[[1]] # for some reason the x-axis data is negative the tutorial's result
plot(final_data, asp=T, xlab='PCA 1', ylab='PCA 2', pch=16)
To jest tak daleko, jak mam, i wszystko w porządku do tej pory. Ale nie mogę zrozumieć, w jaki sposób uzyskuje się dane dla ostatecznego wykresu - wariancji przypisywanej PCA 1 - którą Smith wykreśla jako:
Oto, co próbowałem (co ignoruje dodawanie oryginalnych środków):
trans_data = final_data
trans_data[,2] = 0
row_orig_data = t(t(feat_vec[1,]) %*% t(trans_data))
plot(row_orig_data, asp=T, pch=16)
.. i otrzymałem błąd:
.. ponieważ jakoś straciłem wymiar danych podczas mnożenia macierzy. Byłbym bardzo wdzięczny za pomysł, co się tutaj dzieje.
* Edytować *
Zastanawiam się, czy jest to właściwa formuła:
row_orig_data = t(t(feat_vec) %*% t(trans_data))
plot(row_orig_data, asp=T, pch=16, cex=.5)
abline(a=0, b=s1, col='red')
Ale jestem trochę zdezorientowany, jeśli tak, ponieważ (a) rozumiem rowVectorFeature
potrzebę zredukowania do pożądanej wymiarowości (wektor własny dla PCA1) i (b) nie pokrywa się z linią PCA1:
Wszelkie opinie bardzo cenione.
s1
nachylenie zostało obliczone z pomyłką (powinno być , a nie ), dlatego czerwona linia nie jest idealnie dopasowane do danych na pierwszej figurze i rekonstrukcji na ostatniej. x / yOdpowiedzi:
Byłeś bardzo, bardzo blisko i zostałeś złapany przez subtelny problem w pracy z macierzami w R. Pracowałem od ciebie
final_data
i uzyskałem prawidłowe wyniki niezależnie. Potem przyjrzałem się twojemu kodowi. Krótko mówiąc, gdzie napisałeśbyłbyś w porządku, gdybyś napisał
trans_data
t(feat_vec[1,])
row_orig_data = t(as.matrix(feat_vec[1,],ncol=1,nrow=2) %*% t(trans_data))
non-conformable arguments
final_data
row_orig_data
t(t(p) %*% t(q)) = q %*% t
pisać
następnie, aby odzyskać swoje dane w pierwotnej podstawie, potrzebujesz
Za pomocą można wyzerować części danych, które są rzutowane wzdłuż drugiego komponentu
i możesz następnie przekształcić jak poprzednio
Umieszczenie ich na tym samym wykresie wraz z główną linią składową na zielono pokazuje, jak działało przybliżenie.
Wróćmy do tego, co miałeś. Ta linia była w porządku
feat_vec %*% row_data_adj
Więc miałeś
Jest w porządku: zerujesz tylko te części danych, które są rzutowane wzdłuż drugiego komponentu. Gdzie idzie źle
t(feat_vec[1,]) %*% t(trans_data)
źródło
Myślę, że masz dobry pomysł, ale natknąłeś się na nieprzyjemną cechę R. Ponownie odpowiedni fragment kodu, jak już powiedziałeś:
Zasadniczo
final_data
zawiera współrzędne pierwotnych punktów w odniesieniu do układu współrzędnych określonego przez wektory własne macierzy kowariancji. Aby zrekonstruować oryginalne punkty, należy zatem pomnożyć każdy wektor własny z powiązaną transformowaną współrzędną, npco dałoby oryginalne współrzędne pierwszego punktu. W swoim pytaniu drugi składnik prawidłowo ustawić na zero
trans_data[,2] = 0
. Jeśli następnie (jak już edytowałeś) obliczobliczasz wzór (1) dla wszystkich punktów jednocześnie. Twoje pierwsze podejście
oblicza coś innego i działa tylko dlatego, że R automatycznie upuszcza atrybut wymiaru
feat_vec[1,]
, więc nie jest to już wektor wiersza, ale traktowany jak wektor kolumny. Kolejna transpozycja sprawia, że znów jest to wektor wiersza i dlatego przynajmniej obliczenia nie powodują błędu, ale jeśli przejdziesz przez matematykę, zobaczysz, że jest to coś innego niż (1). Zasadniczo dobrym pomysłem w przypadku mnożenia macierzy jest tłumienie upuszczania atrybutu wymiaru, który można osiągnąć za pomocądrop
parametru, npfeat_vec[1,,drop=FALSE]
.źródło
drop=F
argumentu.Po zapoznaniu się z tym ćwiczeniem możesz wypróbować łatwiejsze sposoby w R. Istnieją dwie popularne funkcje do wykonywania PCA:
princomp
iprcomp
. Taprincomp
funkcja rozkłada wartość własną jak w ćwiczeniu.prcomp
Wykorzystuje rozkładu wartości pojedynczej. Obie metody dają te same wyniki prawie przez cały czas: ta odpowiedź wyjaśnia różnice w R, podczas gdy ta odpowiedź wyjaśnia matematykę . (Dzięki TooTone za komentarze teraz zintegrowane z tym postem).Tutaj używamy obu do odtworzenia ćwiczenia w R. Po pierwsze
princomp
:Drugie użycie
prcomp
:Oczywiście znaki są odwrócone, ale wyjaśnienie zmienności jest równoważne.
źródło