Стрілки базових змінних у біклоті PCA в R


11

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


[Щоб мати сенс у деяких коментарях, спочатку розміщені сюжети мали надзвичайну проблему з дефіцитним інтересом, і зараз вони стираються.]


Я не можу зрозуміти, як ти насправді отримав свої зелені стрілки. Вони невірні. Справа в тому, що довжина зеленого кольору - це прибл. вдвічі довший, ніж зелений колір ширини, дозволяє підозрювати, що ви побудували вектори, що стосуються змінних, не стандартизованих. Це не може статися на біплоті PCA на основі кореляцій.
— ttnphns

Червоні стрілки здаються правильними. Дивіться: вони однакової довжини і симетричні щодо PC2. Це єдино можлива позиція, коли ви робите PCA лише з двома змінними та засновані на кореляціях (тобто стандартизованих змінних). У PCA на основі кореляцій навантаження (узгоджені стрілки) - це кореляції між ПК та змінними. У вашому прикладі, завантаження (вари на ПК): .74752, .66424; -.74752, .66424.
— ttnphns

@ttnphns Так, червоні стрілки - це те, що я намагаюся відтворити (вони є правильними), і вони побудовані в R із biplot(name_of_the_PCA)викликом, який у цьому випадку є biplot(PCA). Я зосереджував і масштабував дані.
— Антоні Пареллада

Отже, яке ваше питання? Як обчислити координати для червоних стрілок? Вони повинні бути навантаженнями PCA . Іноді створюються власні вектори (ваша команда R, ймовірно, це зробила ??), однак, консенсусний, змістовний спосіб полягає у побудові графіку завантажень .
— ttnphns

@ttnphns Складання власних векторів (я припускаю, що це те саме, що і навантаження) дає мені правильну орієнтацію (дякую), але не такої ж величини, як червоні стрілки (я вставляю зображення в ОП).
— Антоні Пареллада

Відповіді:


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 . Позначення діаграми в дописі збережено: означає змінну в предметному просторі . h ' - відповідна стрілка в кінцевому підсумку; а координати 1 і 2 є складовими навантаженнями змінної V по відношенню до ПК 1 і ПК 2 :VSepal L.год'а1а2VПК1ПК2


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


Складовою змінної Sepal L.стосовно буде:ПК1

а1=год⋅cos⁡(ϕ)

ПК1S1

‖S1‖=∑1нбалів12=1V⋅S1

а1=V⋅S1=‖V‖‖S1‖cos⁡(ϕ)(1)=год×1×⋅cos⁡(ϕ)

‖V‖=∑х2

Вар(V)=∑х2н-1=‖V‖н-1⟹‖V‖=год=вар(V)н-1.

Так само,

‖S1‖=1=вар (S1)н-1.

(1)

а1=год×1×⋅cos⁡(ϕ)=вар(V)вар(S1)cos⁡(θ)(н-1)

cos⁡(ϕ)rн-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)

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

Цікаві місця:

  • Стрілки можуть бути відтворені у вигляді співвідношення вихідних змінних з балами, сформованими за першими двома основними компонентами.
  • V∗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

1н-1

вар(scr_var_one)=1=∑1нscr_var_oneн-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

н-1нlan

prcompн-1н-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 коментарі:

графік розсіювання та "графік стрілки" масштабуються таким чином, що найбільша (в абсолютній величині) х або у координатна стрілка стрілок була точно рівною найбільшій (в абсолютній величині) х або у координаті розсіяних точок даних

  • U


1
+1, Гарне навчання. Я додав тег Rдо вашого запитання, оскільки заплутаний матеріал (а саме коефіцієнт масштабування) виявився частково R-специфічним. Загалом, ви могли переконатися, що біплот PCA - це накладений розсип балів компонентів (координати рядків) та коефіцієнтів напрямку компонентів (координати стовпців), і оскільки різні кількості стандартизації за "інерцією" (дисперсією) можуть застосовуватися до кожного теж може виникнути різні погляди біплота. Щоб додати: найчастіше (більше сенсу), завантаження відображаються як координати стовпців (стрілки).
— ttnphns

1
(продовження) Дивіться мій огляд біплоту, який різними словами пояснює те, що ви показали у своїй гарній відповіді.
— ttnphns

2
+ 1 подяка за написання навчальних посібників та з відтворюваним кодом для нас!
— Хайтао Ду

Антоні, ти малював (як вручну) чи ви задумував (подали в дані) свою картинку? Яке програмне забезпечення ви використовували? Це виглядає приємно.
— ttnphns

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