Стрілки базових змінних у біклоті 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=1VS1

а1=VS1=VS1cos(ϕ)(1)=год×1×cos(ϕ)

V=х2

Вар(V)=х2н-1=Vн-1V=год=вар(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)

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

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

  • Стрілки можуть бути відтворені у вигляді співвідношення вихідних змінних з балами, сформованими за першими двома основними компонентами.
  • VS

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

або в коді 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.