Стрелки базовых переменных в биплоте PCA в R


11

Рискуя сделать вопрос специфичным для программного обеспечения и из-за его повсеместности и особенностей, я хочу спросить о функции biplot()в R и, более конкретно, о вычислении и построении по умолчанию ее наложенных красных стрелок, соответствующих к базовым переменным.


[Чтобы разобраться в некоторых комментариях, изначально опубликованные сюжеты имели скудную проблему с редким интересом, а теперь стерты.]


Я не могу понять, как ты на самом деле получил свои зеленые стрелки. Они не верны. Тот факт, что длина зеленого цвета составляет ок. в два раза длиннее зеленого s.width позволяет предположить, что вы строили векторы, относящиеся к не стандартизированным переменным. Это не может произойти на биплоте PCA на основе корреляций.
ttnphns

Красные стрелки кажутся правильными. Смотрите: они одинаковой длины и симметричны относительно PC2. Это единственно возможная позиция, когда вы делаете PCA только с двумя переменными и основаны на корреляциях (то есть стандартизированных переменных). В PCA, основанном на корреляциях, нагрузки (скоординированные стрелками) являются корреляциями между ПК и переменными. В вашем примере, загружения (вары на ПК): .74752, .66424; -.74752, .66424.
ttnphns

@ttnphns Да, красные стрелки - это то, что я пытаюсь воспроизвести (они правильные), и они нанесены на R с biplot(name_of_the_PCA)вызовом, который в данном случае является biplot(PCA). Я центрировал и масштабировал данные.
Антони Пареллада

Итак, какой у вас вопрос? Как рассчитать координаты для красных стрелок? Это должны быть загрузки PCA . Иногда собственные векторы строятся на графике (ваша команда R, вероятно, сделала это?), Однако, согласованный, значимый способ - это график загрузки .
ttnphns

@ttnphns Построение собственных векторов (я полагаю, это то же самое, что и нагрузки) дает мне правильную ориентацию (спасибо), но не такую ​​же величину, как у красных стрелок (я вставляю изображение в OP).
Антони Пареллада

Ответы:


19

Подумайте об отмене голосования в постах @ amoeba и @ttnphns . Спасибо вам обоим за вашу помощь и идеи.


Следующее опирается на набор данных Iris в R и, в частности, на первые три переменные (столбцы):Sepal.Length, Sepal.Width, Petal.Length .

Biplot сочетает в себе загрузочный участок (нестандартизованные собственные векторы) - в бетоне, первые две нагрузок , и оценку участок (повернуты и расширенные точки данных , построенные по отношению к основным компонентам). Используя тот же набор данных, @amoeba описывает 9 возможных комбинаций биплота PCA на основе 3 возможных нормализаций оценочного графика первого и второго основных компонентов и 3 нормализаций загрузочного графика (стрелки) исходных переменных. Чтобы увидеть, как R обрабатывает эти возможные комбинации, интересно взглянуть на biplot()метод:


Сначала линейная алгебра готова скопировать и вставить:

X = as.matrix(iris[,1:3])             # Three first variables of Iris dataset
CEN = scale(X, center = T, scale = T) # Centering and scaling the data
PCA = prcomp(CEN)

# EIGENVECTORS:
(evecs.ei = eigen(cor(CEN))$vectors)       # Using eigen() method
(evecs.svd = svd(CEN)$v)                   # PCA with SVD...
(evecs = prcomp(CEN)$rotation)             # Confirming with prcomp()

# EIGENVALUES:
(evals.ei = eigen(cor(CEN))$values)        # Using the eigen() method
(evals.svd = svd(CEN)$d^2/(nrow(X) - 1))   # and SVD: sing.values^2/n - 1
(evals = prcomp(CEN)$sdev^2)               # with prcomp() (needs squaring)

# SCORES:
scr.svd = svd(CEN)$u %*% diag(svd(CEN)$d)  # with SVD
scr = prcomp(CEN)$x                        # with prcomp()
scr.mm = CEN %*% prcomp(CEN)$rotation      # "Manually" [data] [eigvecs]

# LOADINGS:

loaded = evecs %*% diag(prcomp(CEN)$sdev)  # [E-vectors] [sqrt(E-values)]

1. Воспроизведение участка загрузки (стрелки):

Здесь очень помогает геометрическая интерпретация этого поста @ttnphns . Обозначения диаграммы в посте были сохранены: обозначает переменную в предметном пространстве . ч ' представляет собой соответствующий стрелок в конечном счете , нанесено; и координаты a 1 и a 2 являются компонентом, загружающим переменную V относительно ПК 1 и ПК 2 :ВSepal L.час'a1a2ВПК1ПК2


введите описание изображения здесь


Компонент переменной по Sepal L.отношению к будет тогда:ПК1

a1знак равночассоз(φ)

ПК1S1

| |S1| |знак равноΣ1Nмножество12знак равно1ВS1

a1знак равноВS1знак равно| |В| || |S1| |соз(φ)(1)знак равночас×1×соз(φ)

| |В| |знак равноΣИкс2

Var(В)знак равноΣИкс2N-1знак равно| |В| |N-1| |В| |знак равночасзнак равновар(В)N-1,

Точно так же,

| |S1| |знак равно1знак равновар (S1)N-1,

(1)

a1знак равночас×1×соз(φ)знак равновар(В)вар(S1)соз(θ)(N-1)

соз(φ)рN-1

Дублируя и перекрывая синим цветом красные стрелки biplot()

par(mfrow = c(1,2)); par(mar=c(1.2,1.2,1.2,1.2))

biplot(PCA, cex = 0.6, cex.axis = .6, ann = F, tck=-0.01) # R biplot
# R biplot with overlapping (reproduced) arrows in blue completely covering red arrows:
biplot(PCA, cex = 0.6, cex.axis = .6, ann = F, tck=-0.01) 
arrows(0, 0,
       cor(X[,1], scr[,1]) * 0.8 * sqrt(nrow(X) - 1), 
       cor(X[,1], scr[,2]) * 0.8 * sqrt(nrow(X) - 1), 
       lwd = 1, angle = 30, length = 0.1, col = 4)
arrows(0, 0,
       cor(X[,2], scr[,1]) * 0.8 * sqrt(nrow(X) - 1), 
       cor(X[,2], scr[,2]) * 0.8 * sqrt(nrow(X) - 1), 
       lwd = 1, angle = 30, length = 0.1, col = 4)
arrows(0, 0,
       cor(X[,3], scr[,1]) * 0.8 * sqrt(nrow(X) - 1), 
       cor(X[,3], scr[,2]) * 0.8 * sqrt(nrow(X) - 1), 
       lwd = 1, angle = 30, length = 0.1, col = 4)

введите описание изображения здесь

Точки интереса:

  • Стрелки могут быть воспроизведены как корреляция исходных переменных с оценками, сгенерированными первыми двумя основными компонентами.
  • В*S

введите описание изображения здесь

или в коде R:

    biplot(PCA, cex = 0.6, cex.axis = .6, ann = F, tck=-0.01) # R biplot
    # R biplot with overlapping arrows in blue completely covering red arrows:
    biplot(PCA, cex = 0.6, cex.axis = .6, ann = F, tck=-0.01) 
    arrows(0, 0,
       (svd(CEN)$v %*% diag(svd(CEN)$d))[1,1] * 0.8, 
       (svd(CEN)$v %*% diag(svd(CEN)$d))[1,2] * 0.8, 
       lwd = 1, angle = 30, length = 0.1, col = 4)
    arrows(0, 0,
       (svd(CEN)$v %*% diag(svd(CEN)$d))[2,1] * 0.8, 
       (svd(CEN)$v %*% diag(svd(CEN)$d))[2,2] * 0.8, 
       lwd = 1, angle = 30, length = 0.1, col = 4)
    arrows(0, 0,
       (svd(CEN)$v %*% diag(svd(CEN)$d))[3,1] * 0.8, 
       (svd(CEN)$v %*% diag(svd(CEN)$d))[3,2] * 0.8, 
       lwd = 1, angle = 30, length = 0.1, col = 4)

или даже пока ...

    biplot(PCA, cex = 0.6, cex.axis = .6, ann = F, tck=-0.01) # R biplot
    # R biplot with overlapping (reproduced) arrows in blue completely covering red arrows:
    biplot(PCA, cex = 0.6, cex.axis = .6, ann = F, tck=-0.01) 
    arrows(0, 0,
       (loaded)[1,1] * 0.8 * sqrt(nrow(X) - 1), 
       (loaded)[1,2] * 0.8 * sqrt(nrow(X) - 1), 
       lwd = 1, angle = 30, length = 0.1, col = 4)
    arrows(0, 0,
       (loaded)[2,1] * 0.8 * sqrt(nrow(X) - 1), 
       (loaded)[2,2] * 0.8 * sqrt(nrow(X) - 1), 
       lwd = 1, angle = 30, length = 0.1, col = 4)
    arrows(0, 0,
       (loaded)[3,1] * 0.8 * sqrt(nrow(X) - 1), 
       (loaded)[3,2] * 0.8 * sqrt(nrow(X) - 1), 
       lwd = 1, angle = 30, length = 0.1, col = 4)

связывая с геометрическим объяснением загрузок @ttnphns , или этот другой информативный пост также @ttnphns .

  • Существует коэффициент масштабирования sqrt(nrow(X) - 1), который остается загадкой.

  • 0.8

Кроме того, следует сказать, что стрелки построены так, что центр текстовой метки находится там, где он должен быть! Стрелки затем умножаются на 0.80.8 перед построением графика, то есть все стрелки короче, чем они должны быть, предположительно, для предотвращения наложения с текстовой меткой (см. Код для biplot.default). Я нахожу это чрезвычайно запутанным. - амеба 19 марта 15 в 10:06


2. Построение графика biplot()результатов (и стрелок одновременно):

UU

введите описание изображения здесь

В конструкции биплота используются две разные шкалы на нижней и верхней горизонтальных осях:

введите описание изображения здесь

Однако относительный масштаб не сразу очевиден, требуя углубления в функции и методы:

biplot()U

> scr.svd = svd(CEN)$u %*% diag(svd(CEN)$d) 
> U = svd(CEN)$u
> apply(U, 2, function(x) sum(x^2))
[1] 1 1 1

Принимая во внимание, что prcomp()функция в R возвращает баллы, масштабированные до их собственных значений:

> apply(scr, 2, function(x) var(x))         # pr.comp() scores scaled to evals
       PC1        PC2        PC3 
2.02142986 0.90743458 0.07113557 
> evals                                     #... here is the proof:
[1] 2.02142986 0.90743458 0.07113557

1

> scr_var_one = scr/sqrt(evals)[col(scr)]  # to scale to var = 1
> apply(scr_var_one, 2, function(x) var(x)) # proved!
[1] 1 1 1

1N-1

вар(scr_var_one)знак равно1знак равноΣ1Nscr_var_oneN-1
> scr_sum_sqrs_one = scr_var_one / sqrt(nrow(scr) - 1) # We / by sqrt n - 1.
> apply(scr_sum_sqrs_one, 2, function(x) sum(x^2))     #... proving it...
PC1 PC2 PC3 
  1   1   1

N-1Nlan

prcompN-1N-1


После снятия с них всех ifзаявлений и других бытовых чисток biplot()поступает следующим образом:

X   = as.matrix(iris[,1:3])                    # The original dataset
CEN = scale(X, center = T, scale = T)          # Centered and scaled
PCA = prcomp(CEN)                              # PCA analysis

par(mfrow = c(1,2))                            # Splitting the plot in 2.
biplot(PCA)                                    # In-built biplot() R func.

# Following getAnywhere(biplot.prcomp):

choices = 1:2                                  # Selecting first two PC's
scale = 1                                      # Default
scores= PCA$x                                  # The scores
lam = PCA$sdev[choices]                        # Sqrt e-vals (lambda) 2 PC's
n = nrow(scores)                               # no. rows scores
lam = lam * sqrt(n)                            # See below.

# at this point the following is called...
# biplot.default(t(t(scores[,choices])      /  lam), 
#                t(t(x$rotation[,choices]) *   lam))

# Following from now on getAnywhere(biplot.default):

x = t(t(scores[,choices])       / lam)         # scaled scores
# "Scores that you get out of prcomp are scaled to have variance equal to      
#  the eigenvalue. So dividing by the sq root of the eigenvalue (lam in 
#  biplot) will scale them to unit variance. But if you want unit sum of 
#  squares, instead of unit variance, you need to scale by sqrt(n)" (see comments).
# > colSums(x^2)
# PC1       PC2 
# 0.9933333 0.9933333    # It turns out that the it's scaled to sqrt(n/(n-1)), 
# ...rather than 1 (?) - 0.9933333=149/150

y = t(t(PCA$rotation[,choices]) * lam)         # scaled eigenvecs (loadings)


n = nrow(x)                                    # Same as dataset (150)
p = nrow(y)                                    # Three var -> 3 rows

# Names for the plotting:

xlabs = 1L:n
xlabs = as.character(xlabs)                    # no. from 1 to 150 
dimnames(x) = list(xlabs, dimnames(x)[[2L]])   # no's and PC1 / PC2

ylabs = dimnames(y)[[1L]]                      # Iris species
ylabs = as.character(ylabs)
dimnames(y) <- list(ylabs, dimnames(y)[[2L]])  # Species and PC1/PC2

# Function to get the range:
unsigned.range = function(x) c(-abs(min(x, na.rm = TRUE)), 
                                abs(max(x, na.rm = TRUE)))
rangx1 = unsigned.range(x[, 1L])               # Range first col x
# -0.1418269  0.1731236
rangx2 = unsigned.range(x[, 2L])               # Range second col x
# -0.2330564  0.2255037
rangy1 = unsigned.range(y[, 1L])               # Range 1st scaled evec
# -6.288626   11.986589
rangy2 = unsigned.range(y[, 2L])               # Range 2nd scaled evec
# -10.4776155   0.8761695

(xlim = ylim = rangx1 = rangx2 = range(rangx1, rangx2))
# range(rangx1, rangx2) = -0.2330564  0.2255037

# And the critical value is the maximum of the ratios of ranges of 
# scaled e-vectors / scaled scores:

(ratio = max(rangy1/rangx1, rangy2/rangx2)) 
# rangy1/rangx1   =   26.98328    53.15472
# rangy2/rangx2   =   44.957418   3.885388
# ratio           =   53.15472

par(pty = "s")                                 # Calling a square plot

# Plotting a box with x and y limits -0.2330564  0.2255037
# for the scaled scores:

plot(x, type = "n", xlim = xlim, ylim = ylim)  # No points
# Filling in the points as no's and the PC1 and PC2 labels:
text(x, xlabs) 
par(new = TRUE)                                # Avoids plotting what follows separately

# Setting now x and y limits for the arrows:

(xlim = xlim * ratio)  # We multiply the original limits x ratio
# -16.13617  15.61324
(ylim = ylim * ratio)  # ... for both the x and y axis
# -16.13617  15.61324

# The following doesn't change the plot intially...
plot(y, axes = FALSE, type = "n", 
     xlim = xlim, 
     ylim = ylim, xlab = "", ylab = "")

# ... but it does now by plotting the ticks and new limits...
# ... along the top margin (3) and the right margin (4)
axis(3); axis(4)
text(y, labels = ylabs, col = 2)  # This just prints the species

arrow.len = 0.1                   # Length of the arrows about to plot.

# The scaled e-vecs are further reduced to 80% of their value
arrows(0, 0, y[, 1L] * 0.8, y[, 2L] * 0.8, 
       length = arrow.len, col = 2)

который, как и ожидалось, воспроизводит (правое изображение внизу) biplot()вывод, вызванный напрямую biplot(PCA)(левый график ниже) во всех его нетронутых эстетических недостатках:

введите описание изображения здесь

Точки интереса:

  • Стрелки построены в масштабе, связанном с максимальным отношением между масштабированным собственным вектором каждого из двух основных компонентов и их соответствующими масштабированными показателями (the ratio). AS @amoeba комментирует:

график рассеяния и «график стрелки» масштабируются таким образом, чтобы наибольшая (по абсолютной величине) координата стрелки x или y стрелки была точно равна наибольшей (по абсолютной величине) координате x или y точек рассеянных данных

  • U


1
+1, хорошая учеба. Я добавил метку Rк вашему вопросу, потому что запутанный вопрос (а именно коэффициент масштабирования) оказался частично R-специфичным. В общем, вы сами могли видеть, что биплот PCA представляет собой наложенную диаграмму рассеяния оценок компонентов (координат строк) и коэффициентов направления компонентов (координат столбцов), и поскольку к каждому из них могут применяться различные величины стандартизации по «инерции» (дисперсия) из того, поэтому могут возникнуть различные взгляды на биплот. Для добавления: наиболее типично (больше смысла), нагрузки отображаются в виде координат столбца (стрелки).
ttnphns

1
(продолжение) Смотрите мой обзор биплота, который объясняет, другими словами, то, что вы показали в своем хорошем ответе.
ttnphns

2
+1 спасибо за написание учебников и с воспроизводимым кодом для нас!
Haitao Du

Энтони, ты нарисовал (как от руки) или нарисовал (снабжен данными) свою картинку? Какое программное обеспечение вы использовали? Это выглядит мило.
ttnphns

@ttnphns Спасибо! Вот ссылка на него . Мне было интересно, если бы вы могли улучшить его, и график загрузки и ПК лучше, более дидактически. Не стесняйтесь менять (это удивительно удобная программа), и если вы это сделаете, пожалуйста, поделитесь.
Антони Пареллада
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.