Я работаю в R с помощью превосходного учебника по PCA Линдси и Смита, и застреваю на последнем этапе. Сценарий R, приведенный ниже, выводит нас на этап (на стр.19), на котором исходные данные восстанавливаются из (в данном случае, единственного) основного компонента, который должен давать прямую линию вдоль оси PCA1 (учитывая, что данные имеет только 2 измерения, второе из которых намеренно отбрасывается).
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)
Это насколько я знаю, и пока все в порядке. Но я не могу понять, как данные получены для окончательного графика - дисперсия, относящаяся к PCA 1 - которую Смит готовит как:
Это то, что я пробовал (что игнорирует добавление оригинальных средств):
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)
.. и получил ошибочный
.. потому что я как-то потерял измерение данных в умножении матриц. Я был бы очень благодарен за идею, что здесь происходит не так.
* Редактировать *
Интересно, это правильная формула:
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')
Но я немного сбит с толку, если это так, потому что (а) я понимаю, что rowVectorFeature
необходимо уменьшить до желаемой размерности (собственный вектор для PCA1), и (b) он не совпадает с абзацем PCA1:
Любые мнения высоко ценятся.
s1
уклон был вычислен с ошибкой (должно быть , а не ), поэтому красная линия не идеально согласуется с данными на первом рисунке и с реконструкцией на последнем.Ответы:
Вы были очень близко к этому и попали в ловушку тонкой проблемы при работе с матрицами в R. Я работал с вашим
final_data
и получил правильные результаты независимо. Затем я присмотрелся к вашему коду. Короче говоря, где вы написалиты был бы в порядке, если бы написал
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
Написать
затем, чтобы вернуть ваши данные в исходную базу вам нужно
Вы можете обнулить части ваших данных, которые проецируются вдоль второго компонента, используя
и вы можете преобразовать, как раньше
Нанесение их на один и тот же график вместе с зеленой линией главного компонента показывает, как работает аппроксимация.
Давайте вернемся к тому, что у вас было. Эта линия была в порядке
feat_vec %*% row_data_adj
Тогда у вас было
Это нормально: вы просто обнуляете части своих данных, которые проецируются вдоль второго компонента. Где это идет не так
t(feat_vec[1,]) %*% t(trans_data)
источник
Я думаю, что у вас есть правильная идея, но вы наткнулись на неприятную особенность R. Вот снова соответствующий фрагмент кода, как вы это сформулировали:
По существу
final_data
содержит координаты исходных точек относительно системы координат, определяемой собственными векторами ковариационной матрицы. Поэтому для восстановления исходных точек необходимо умножить каждый собственный вектор на соответствующую преобразованную координату, напримеркоторый дал бы исходные координаты первой точки. В своем вопросе вы установите второй компонент правильно к нулю
trans_data[,2] = 0
. Если вы тогда (как вы уже редактировали) рассчитатьВы рассчитываете формулу (1) для всех точек одновременно. Ваш первый подход
вычисляет что-то другое и работает только потому, что R автоматически удаляет атрибут измерения
feat_vec[1,]
, поэтому он больше не является вектором строки, а рассматривается как вектор столбца. Последующее преобразование снова превращает его в вектор строки, и по этой причине, по крайней мере, вычисление не приводит к ошибке, но если вы пройдете математику, вы увидите, что это нечто иное, чем (1). В целом, при умножении матриц хорошей идеей является подавление отбрасывания атрибута измерения, которое может быть достигнутоdrop
параметром, напримерfeat_vec[1,,drop=FALSE]
.источник
drop=F
аргументе.Изучив это упражнение, вы можете попробовать более простые способы в R. Существуют две популярные функции для выполнения PCA:
princomp
иprcomp
.princomp
Функция делает собственное значение разложения , как это делалось в тренировках.prcomp
Функция использует разложение по сингулярным значениям. Оба метода будут давать одинаковые результаты почти все время: этот ответ объясняет различия в R, тогда как этот ответ объясняет математику . (Спасибо TooTone за комментарии, теперь интегрированные в этот пост.)Здесь мы используем оба, чтобы воспроизвести упражнение в R. Сначала используем
princomp
:Второе использование
prcomp
:Очевидно, что знаки перевернуты, но объяснение вариации эквивалентно.
источник