Після чудового допису Дж. Лонга в цій темі, я шукав простий приклад, і R-код необхідний для створення PCA, а потім повернутися до вихідних даних. Це дало мені деяку геометричну інтуїцію з перших рук, і я хочу поділитися тим, що я отримав. Набір даних та код можна безпосередньо скопіювати та вставити у форму G Gitub .
Я використовував набір даних , який я знайшов в Інтернеті на напівпровідниках тут , і я підрізав його тільки два виміри - «атомний номер» і «точку плавлення» - для полегшення прокладки.
Як застереження, ідея є чисто ілюстративною обчислювальним процесом: PCA використовується для зменшення більш ніж двох змінних до кількох похідних основних компонентів або для ідентифікації колінеарності також у випадку безлічі ознак. Таким чином, воно не знайде особливого застосування у випадку двох змінних, і не було б необхідності обчислювати власні вектори матриць кореляції, як вказував @amoeba.
Далі я обрізав спостереження з 44 до 15, щоб полегшити завдання відстеження окремих точок. Кінцевим результатом став каркас даних скелета ( dat1
):
compounds atomic.no melting.point
AIN 10 498.0
AIP 14 625.0
AIAs 23 1011.5
... ... ...
Стовпчик "сполуки" вказує на хімічну конституцію напівпровідника і відіграє роль назви рядків.
Це можна відтворити так (готово скопіювати та вставити на консоль 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]
Потім дані були масштабовані:
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)`
Наступні кроки лінійної алгебри:
C <- cov(X) # Covariance matrix (centered data)
⎡⎣⎢at_nomelt_pat_no10.296melt_p0.2961⎤⎦⎥
Функція кореляції cor(dat1)
дає той самий вихід на немасштабовані дані, як функція cov(X)
для масштабованих даних.
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%
Ми включимо обидва власні вектори, враховуючи невеликий розмір прикладу набору даних про іграшки, розуміючи, що виключення одного з власних векторів призведе до зменшення розмірності - ідея, що стоїть за PCA.
Оцінка матриця була визначена в якості матриці множення масштабованих даних ( X
) з допомогою матриці власних векторів (або «вирощений») :
score_matrix <- X %*% e_vectors
# Identical to the often found operation: t(t(e_vectors) %*% t(X))
Концепція тягне за собою лінійну комбінацію кожного запису (рядок / предмет / спостереження / надпровідник в даному випадку) центрированних (і в цьому випадку масштабованих) даних, зважених рядками кожного власного вектора , так що в кожному з кінцевих стовпців матриця балів, ми знайдемо внесок з кожної змінної (стовпця) даних (цілого X
), АЛЕ тільки відповідний власний вектор братиме участь у обчисленні (тобто перший власний вектор буде внести (основний компонент 1) та до , як у: ПК[0.7,0.7]T[ 0,7 , - 0,7 ] Т ПКPC1[0.7,−0.7]TPC2
Тому кожен власний вектор впливатиме на кожну змінну по-різному, і це буде відображено в "завантаженнях" PCA. У нашому випадку від'ємний знак у другій складовій другого власного вектора змінить знак значень точки плавлення у лінійних комбінаціях, що виробляють PC2, тоді як ефект першого власного вектора буде стабільно позитивним: [0.7,−0.7]
Власні вектори масштабуються до :1
> apply(e_vectors, 2, function(x) sum(x^2))
PC1 PC2
1 1
тоді як ( навантаження ) - це власні вектори, масштабовані власними значеннями (незважаючи на заплутану термінологію у вбудованих функціях R, показаних нижче). Отже, навантаження можна обчислити так:
> 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
Цікаво відзначити, що обертається хмара даних (графік оцінки) матиме дисперсію вздовж кожного компонента (ПК), рівну власним значенням:
> apply(score_matrix, 2, function(x) var(x))
PC1 PC2
53829.7896 110.8414
> lambda
[1] 53829.7896 110.8414
Використовуючи вбудовані функції, результати можна повторити:
# 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
Альтернативно, метод руйнування значення сингулярного значення ( ) може бути застосований до ручного обчислення PCA; насправді це метод, який використовується в . Етапи можна прописати як: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
Результат показаний нижче, по-перше, відстані від окремих точок до першого власного вектора, а на другому ділянці - ортогональні відстані до другого власного вектора:
Якби замість цього ми побудували значення матриці оцінок (PC1 та PC2) - більше не "плавлення.точка" та "atomic.no", а дійсно зміна бази координат точок з власними векторами як основою, ці відстані були б збереглася, але природно стала б перпендикулярною до осі xy:
Тепер фокусом було відновлення вихідних даних . Точки були перетворені простим множенням матриць власними векторами. Тепер дані повертаються назад шляхом множення на обернену матрицю власних векторів з результуючою помітною зміною місця розташування точок даних. Наприклад, помітьте зміну рожевої крапки "GaN" у лівому верхньому квадранті (чорний круг у лівій ділянці, внизу), повернувшись до початкового положення у лівому нижньому квадранті (чорний круг у правій ділянці, внизу).
Тепер у нас, нарешті, були відновлені початкові дані в цій "де-поверненій" матриці:
Крім зміни координат обертання даних у PCA, результати слід інтерпретувати, і цей процес має на меті залучити a biplot
, на якому точки даних побудовані щодо нових координат власного вектора, а вихідні змінні тепер накладаються як вектори. Цікаво відзначити еквівалентність у розташуванні точок між графіками у другому ряду графіків обертання зверху ("Оцінки з осі xy = Власні вектори") (ліворуч від поданих нижче графіків) та biplot
(з право):
Накладення оригінальних змінних у вигляді червоних стрілок пропонує шлях до інтерпретації PC1
як вектора у напрямку (або з позитивною кореляцією) з обома atomic no
та melting point
; і PC2
як компонент разом зі збільшеннями значень, atomic no
але негативно корельованих із melting point
, що відповідають значенням власних векторів:
PCA$rotation
PC1 PC2
atomic.no 0.7071068 0.7071068
melting.point 0.7071068 -0.7071068
Цей інтерактивний підручник Віктора Пауелла дає негайний відгук щодо змін власних векторів у міру зміни хмари даних.