Чи не повинен SVD взагалі нічого пояснювати для випадкової матриці? Що я роблю неправильно?


13

Якщо я будую 2-D матрицю, повністю складається з випадкових даних, я б очікував, що компоненти PCA та SVD по суті нічого не пояснюють.

Натомість, здається, що перший стовпець SVD пояснює 75% даних. Як це можливо? Що я роблю неправильно?

Ось сюжет:

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

Ось код R:

set.seed(1)
rm(list=ls())
m <- matrix(runif(10000,min=0,max=25), nrow=100,ncol=100)
svd1 <- svd(m, LINPACK=T)
par(mfrow=c(1,4))
image(t(m)[,nrow(m):1])
plot(svd1$d,cex.lab=2, xlab="SVD Column",ylab="Singluar Value",pch=19)

percentVarianceExplained = svd1$d^2/sum(svd1$d^2) * 100
plot(percentVarianceExplained,ylim=c(0,100),cex.lab=2, xlab="SVD Column",ylab="Percent of variance explained",pch=19)

cumulativeVarianceExplained = cumsum(svd1$d^2/sum(svd1$d^2)) * 100
plot(cumulativeVarianceExplained,ylim=c(0,100),cex.lab=2, xlab="SVD column",ylab="Cumulative percent of variance explained",pch=19)

Оновлення

Дякую @Aaron Як ви зазначали, виправлення полягало в тому, щоб додати масштабування до матриці, щоб числа були по центру 0 (тобто середнє значення дорівнює 0).

m <- scale(m, scale=FALSE)

Ось виправлене зображення, показане для матриці зі випадковими даними, перший стовпець SVD близький до 0, як очікувалося.

Виправлене зображення


4
[0,1]100R100Rnn1/3n/3(n1)/121/12(n/3(n1)/12)/(n/3)=3/4+1/(4n)n=10075.25

Відповіді:


11

Перший ПК пояснює, що змінні не зосереджені навколо нуля. Спочатку масштабування або центрування випадкових змінних навколо нуля матиме результат, який ви очікуєте. Наприклад, будь-яке з цих:

m <- matrix(runif(10000,min=0,max=25), nrow=100,ncol=100)
m <- scale(m, scale=FALSE)

m <- matrix(runif(10000,min=-25,max=25), nrow=100,ncol=100)

3
Ви піднімаєте хорошу точку, але я думаю, що це розповідає лише частину історії. Справді, я б здогадався, що ОП спробує кожне з них і все-таки здивується результату. Справа в тому, що, оскільки одиничні значення впорядковані у висновку, вони не з'являться (і справді не є) рівномірно розподіленими, як можна було б наївно очікувати від "випадкових" даних. Розподіл Марченко-Пастур керує їх поведінкою в даному випадку.
кардинал

@Aaron Дякую, ви були абсолютно праві. Я додав графік виправленого результату вище, щоб показати, наскільки гарний результат.
Контанго

1
@cardinal Дякуємо за ваш коментар, ви абсолютно праві (див. графік, виданий виправленим кодом, вище). Я вважаю, що значення SVD ставали б менш рівномірно розподіленими, коли матриця стає меншою, оскільки менша матриця матиме більше шансів мати шаблони, які не будуть розбиті за законом великих чисел.
Контанго

3

Я додам більш наочну відповідь на ваше запитання, використовуючи порівняння з нульовою моделлю. Процедура випадковим чином переміщує дані в кожному стовпці, щоб зберегти загальну дисперсію, тоді як коваріація між змінними (стовпцями) втрачається. Це виконується кілька разів, і отриманий розподіл сингулярних значень у рандомізованій матриці порівнюється з вихідними значеннями.

Я використовую prcompзамість svdрозкладу матриці, але результати схожі:

set.seed(1)
m <- matrix(runif(10000,min=0,max=25), nrow=100,ncol=100)

S <- svd(scale(m, center = TRUE, scale=FALSE))
P <- prcomp(m, center = TRUE, scale=FALSE)
plot(S$d, P$sdev) # linearly related

Порівняння нульової моделі виконується на центрованій матриці нижче:

library(sinkr) # https://github.com/marchtaylor/sinkr

# centred data
Pnull <- prcompNull(m, center = TRUE, scale=FALSE, nperm = 100)
Pnull$n.sig
boxplot(Pnull$Lambda[,1:20], ylim=range(Pnull$Lambda[,1:20], Pnull$Lambda.orig[1:20]), outline=FALSE, col=8, border="grey50", log="y", main=paste("m (center=FALSE); n sig. =", Pnull$n.sig))
lines(apply(Pnull$Lambda, 2, FUN=quantile, probs=0.95))
points(Pnull$Lambda.orig[1:20], pch=16)

Далі наведено блокплот перестановленої матриці з 95-відсотковим квантилем кожної окремої величини, показаної суцільною лінією. Вихідними значеннями PCA mє крапки. всі вони лежать нижче 95% лінії. Таким чином, їх амплітуда не відрізняється від випадкового шуму.

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

Таку саму процедуру можна виконати і в нецентризованій версії mз однаковим результатом - Немає значних сингулярних значень:

# centred data
Pnull <- prcompNull(m, center = FALSE, scale=FALSE, nperm = 100)
Pnull$n.sig
boxplot(Pnull$Lambda[,1:20], ylim=range(Pnull$Lambda[,1:20], Pnull$Lambda.orig[1:20]), outline=FALSE, col=8, border="grey50", log="y", main=paste("m (center=TRUE); n sig. =", Pnull$n.sig))
lines(apply(Pnull$Lambda, 2, FUN=quantile, probs=0.95))
points(Pnull$Lambda.orig[1:20], pch=16)

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

Для порівняння розглянемо набір даних з невипадковим набором даних: iris

# iris dataset example
m <- iris[,1:4]
Pnull <- prcompNull(m, center = TRUE, scale=FALSE, nperm = 100)
Pnull$n.sig
boxplot(Pnull$Lambda, ylim=range(Pnull$Lambda, Pnull$Lambda.orig), outline=FALSE, col=8, border="grey50", log="y", main=paste("m (center=FALSE); n sig. =", Pnull$n.sig))
lines(apply(Pnull$Lambda, 2, FUN=quantile, probs=0.95))
points(Pnull$Lambda.orig[1:20], pch=16)

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

Тут перше значення однини є суттєвим і пояснює понад 92% від загальної дисперсії:

P <- prcomp(m, center = TRUE)
P$sdev^2 / sum(P$sdev^2)
# [1] 0.924618723 0.053066483 0.017102610 0.005212184

+1. Приклад набору даних Iris цікавий тим, що при перегляді перших двох ПК (наприклад, у вашій власній публікації тут stats.stackexchange.com/a/88092 ) цілком зрозуміло, що другий несе певний сигнал. Тест на перестановку (він же перетасовування) вказує на те, що лише 1-й є "значущим". Зрозуміло, що перетасування має тенденцію до недооцінки кількості ПК: велика дисперсія першого реального ПК буде «розкинута» по перетасованих ПК та підвищить їх усі, починаючи з другого. Можна розробити більш чутливі тести, які враховують це, але це робиться рідко.
амеба каже, що повернеться до Моніки

@amoeba - Відмінний коментар. Я вже деякий час замислююся над ефектом "розповсюдження". Я вважаю, що тест перехресної перевірки може бути одним з найбільш чутливих, на які ви посилаєтесь (наприклад, ваша відповідь тут )? Було б чудово, якби ви могли надати приклад / довідку.
Марк у коробці

Зазвичай я вважаю за краще використовувати перехресну перевірку (грунтуючись на помилці відновлення, відповідно до моєї відповіді тут ), але я фактично не впевнений у тому, чи не якимось чином він страждає від того ж виду нечутливості чи ні. Можливо, має сенс спробувати це на наборі даних Iris. Щодо підходів, що базуються на перетасуванні, я не знаю жодної посилання на облік цього "розповсюдження", я просто знаю деяких людей, які працювали над цим нещодавно. Я думаю, що вони хотіли це скоро написати. Ідея полягає у впровадженні деяких факторів зниження масштабів для дисперсій більш високих перетасованих ПК.
Амеба каже: Відновити Моніку

@amoeba - Дякую за це посилання. Це мені багато що пояснює. Мені було особливо цікаво побачити, що перехресне підтвердження в PCA використовує методи, які можуть працювати на наборах даних із відсутніми значеннями. Я зробив декілька спроб щодо цього підходу, і (як ви заявляєте) підхід до перетасування нульової моделі дійсно має тенденцію до недооцінки кількості значних ПК. Однак для набору даних райдужної оболонки я послідовно повертаю один ПК для помилки відновлення. Цікаво враховуючи те, що ви згадали про сюжет. Можливо, якби ми оцінювали помилку на основі прогнозів видів, результати можуть бути різними.
Марк у коробці

З цікавості я спробував це на даних райдужки. Насправді я отримую два значні ПК із методом перехресної перевірки. Я оновив пов’язане повідомлення, будь ласка, дивіться його.
Амеба каже: Відновити Моніку
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.