对于随机矩阵,SVD 不应该什么都不解释吗?我究竟做错了什么?

机器算法验证 r 主成分分析 svd
2022-03-18 20:24:21

如果我构建一个完全由随机数据组成的二维矩阵,我希望 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,正如预期的那样。

校正后的图像

2个回答

第一台 PC 解释变量不以零为中心。首先缩放或将随机变量集中在零附近将产生您期望的结果。例如,以下任一:

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)

我将通过使用空模型比较为您的问题添加更直观的答案。该过程随机打乱每列中的数据以保留整体方差,同时丢失变量(列)之间的协方差。这会执行多次,并将随机矩阵中奇异值的结果分布与原始值进行比较。

我使用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