如何使用自举或蒙特卡罗方法确定重要的主成分?

机器算法验证 r 主成分分析 引导程序 蒙特卡洛
2022-01-19 14:59:48

我有兴趣确定从主成分分析 (PCA) 或经验正交函数 (EOF) 分析中得出的重要模式的数量。我对将这种方法应用于气候数据特别感兴趣。数据字段是一个 MxN 矩阵,其中 M 是时间维度(例如天),N 是空间维度(例如 lon/lat 位置)。我已经阅读了一种可能的引导方法来确定重要的 PC,但无法找到更详细的描述。到目前为止,我一直在应用 North 的经验法则(North等人,1982 年)来确定这个截止值,但我想知道是否有更稳健的方法可用。

举个例子:

###Generate data
x <- -10:10
y <- -10:10
grd <- expand.grid(x=x, y=y)

#3 spatial patterns
sp1 <- grd$x^3+grd$y^2
tmp1 <- matrix(sp1, length(x), length(y))
image(x,y,tmp1)

sp2 <- grd$x^2+grd$y^2
tmp2 <- matrix(sp2, length(x), length(y))
image(x,y,tmp2)

sp3 <- 10*grd$y
tmp3 <- matrix(sp3, length(x), length(y))
image(x,y,tmp3)


#3 respective temporal patterns
T <- 1:1000

tp1 <- scale(sin(seq(0,5*pi,,length(T))))
plot(tp1, t="l")

tp2 <- scale(sin(seq(0,3*pi,,length(T))) + cos(seq(1,6*pi,,length(T))))
plot(tp2, t="l")

tp3 <- scale(sin(seq(0,pi,,length(T))) - 0.2*cos(seq(1,10*pi,,length(T))))
plot(tp3, t="l")


#make data field - time series for each spatial grid (spatial pattern multiplied by temporal pattern plus error)
set.seed(1)
F <- as.matrix(tp1) %*% t(as.matrix(sp1)) + 
as.matrix(tp2) %*% t(as.matrix(sp2)) + 
as.matrix(tp3) %*% t(as.matrix(sp3)) +
matrix(rnorm(length(T)*dim(grd)[1], mean=0, sd=200), nrow=length(T), ncol=dim(grd)[1]) # error term

dim(F)
image(F)


###Empirical Orthogonal Function (EOF) Analysis 
#scale field
Fsc <- scale(F, center=TRUE, scale=FALSE)

#make covariance matrix
C <- cov(Fsc)
image(C)

#Eigen decomposition
E <- eigen(C)

#EOFs (U) and associated Lambda (L) 
U <- E$vectors
L <- E$values

#projection of data onto EOFs (U) to derive principle components (A)
A <- Fsc %*% U

dim(U)
dim(A)

#plot of top 10 Lambda
plot(L[1:10], log="y")

#plot of explained variance (explvar, %) by each EOF
explvar <- L/sum(L) * 100
plot(explvar[1:20], log="y")


#plot original patterns versus those identified by EOF
layout(matrix(1:12, nrow=4, ncol=3, byrow=TRUE), widths=c(1,1,1), heights=c(1,0.5,1,0.5))
layout.show(12)

par(mar=c(4,4,3,1))
image(tmp1, main="pattern 1")
image(tmp2, main="pattern 2")
image(tmp3, main="pattern 3")

par(mar=c(4,4,0,1)) 
plot(T, tp1, t="l", xlab="", ylab="")
plot(T, tp2, t="l", xlab="", ylab="")
plot(T, tp3, t="l", xlab="", ylab="")

par(mar=c(4,4,3,1))
image(matrix(U[,1], length(x), length(y)), main="eof 1") 
image(matrix(U[,2], length(x), length(y)), main="eof 2")
image(matrix(U[,3], length(x), length(y)), main="eof 3")

par(mar=c(4,4,0,1)) 
plot(T, A[,1], t="l", xlab="", ylab="")
plot(T, A[,2], t="l", xlab="", ylab="")
plot(T, A[,3], t="l", xlab="", ylab="")

在此处输入图像描述

而且,这是我用来确定 PC 重要性的方法。基本上,经验法则是相邻 Lambda 之间的差异必须大于它们的相关误差。

###Determine significant EOFs

#North's Rule of Thumb
Lambda_err <- sqrt(2/dim(F)[2])*L
upper.lim <- L+Lambda_err
lower.lim <- L-Lambda_err
NORTHok=0*L
for(i in seq(L)){
    Lambdas <- L
    Lambdas[i] <- NaN
    nearest <- which.min(abs(L[i]-Lambdas))
    if(nearest > i){
        if(lower.lim[i] > upper.lim[nearest]) NORTHok[i] <- 1
    }
    if(nearest < i){
        if(upper.lim[i] < lower.lim[nearest]) NORTHok[i] <- 1
    }
}
n_sig <- min(which(NORTHok==0))-1

plot(L[1:10],log="y", ylab="Lambda (dots) and error (vertical lines)", xlab="EOF")
segments(x0=seq(L), y0=L-Lambda_err, x1=seq(L), y1=L+Lambda_err)
abline(v=n_sig+0.5, col=2, lty=2)
text(x=n_sig, y=mean(L[1:10]), labels="North's Rule of Thumb", srt=90, col=2)

在此处输入图像描述

我发现 Björnsson 和 Venegas ( 1997 ) 关于显着性检验的章节部分很有帮助——它们指的是三类检验,其中主要的方差类型可能是我希望使用的。指的是一种蒙特卡罗方法,该方法对时间维度进行改组并在许多排列上重新计算 Lambda。von Storch 和 Zweiers (1999) 还提到了将 Lambda 光谱与参考“噪声”光谱进行比较的测试。在这两种情况下,我都不确定如何做到这一点,以及在给定由排列确定的置信区间的情况下如何进行显着性检验。

谢谢你的帮助。

参考文献:Björnsson, H. 和 Venegas, SA (1997)。“气候数据 EOF 和 SVD 分析手册”,麦吉尔大学,CCGCR 报告第 97-1 号,蒙特利尔,魁北克,52 页。http://andvari.vedur.is/%7Efolk/halldor/PICKUP/eof.pdf

GR North、TL Bell、RF Cahalan 和 FJ Moeng。(1982 年)。经验正交函数估计中的抽样误差。星期一。威亚。启 110:699-706。

von Storch, H, Zwiers, FW (1999)。气候研究中的统计分析。剑桥大学出版社。

1个回答

尽管这是我的问题,但我将尝试在这里稍微推进对话。自从我问这个问题已经 6 个月了,不幸的是没有给出完整的答案,我将尝试总结到目前为止我收集到的内容,看看是否有人可以详细说明剩余的问题。请原谅冗长的答案,但我没有其他办法......

首先,我将演示几种使用可能更好的合成数据集的方法。它来自 Beckers 和 Rixon ( 2003 ) 的一篇论文,该论文说明了使用一种算法对 gappy 数据进行 EOF。如果有人感兴趣,我已经在 R 中复制了该算法(链接)。

合成数据集:

#color palette
pal <- colorRampPalette(c("blue", "cyan", "yellow", "red"))

#Generate data
m=50
n=100
frac.gaps <- 0.5 # the fraction of data with NaNs
N.S.ratio <- 0.25 # the Noise to Signal ratio for adding noise to data

x <- (seq(m)*2*pi)/m
t <- (seq(n)*2*pi)/n


#True field
Xt <- 
 outer(sin(x), sin(t)) + 
 outer(sin(2.1*x), sin(2.1*t)) + 
 outer(sin(3.1*x), sin(3.1*t)) +
 outer(tanh(x), cos(t)) + 
 outer(tanh(2*x), cos(2.1*t)) + 
 outer(tanh(4*x), cos(0.1*t)) + 
 outer(tanh(2.4*x), cos(1.1*t)) + 
 tanh(outer(x, t, FUN="+")) + 
 tanh(outer(x, 2*t, FUN="+"))

Xt <- t(Xt)
image(Xt, col=pal(100))

#Noise field
set.seed(1)
RAND <- matrix(runif(length(Xt), min=-1, max=1), nrow=nrow(Xt), ncol=ncol(Xt))
R <- RAND * N.S.ratio * Xt

#True field + Noise field
Xp <- Xt + R
image(Xp, col=pal(100))

在此处输入图像描述

因此,真实数据字段Xt由 9 个信号组成,我在其中添加了一些噪声以创建观察字段Xp,这将在下面的示例中使用。EOF 是这样确定的:

EOF

#make covariance matrix
C <- t(Xp) %*% Xp #cov(Xp)
image(C)

#Eigen decomposition
E <- svd(C)

#EOFs (U) and associated Lambda (L) 
U <- E$u
L <- E$d

#projection of data onto EOFs (U) to derive principle components (A)
A <- Xp %*% U

按照我在原始示例中使用的示例,我将通过 North 的经验法则确定“重要的”EOF。

诺斯的经验法则

Lambda_err <- sqrt(2/dim(Xp)[2])*L
upper.lim <- L+Lambda_err
lower.lim <- L-Lambda_err
NORTHok=0*L
for(i in seq(L)){
    Lambdas <- L
    Lambdas[i] <- NaN
    nearest <- which.min(abs(L[i]-Lambdas))
    if(nearest > i){
        if(lower.lim[i] > upper.lim[nearest]) NORTHok[i] <- 1
    }
    if(nearest < i){
        if(upper.lim[i] < lower.lim[nearest]) NORTHok[i] <- 1
    }
}
n_sig <- min(which(NORTHok==0))-1
n_sig

plot(L[1:20],log="y", ylab="Lambda (dots) and error (vertical lines)", xlab="EOF")
segments(x0=seq(L), y0=L-Lambda_err, x1=seq(L), y1=L+Lambda_err)
abline(v=n_sig+0.5, col=2, lty=2)
text(x=n_sig, y=mean(L[1:10]), labels="North's Rule of Thumb", srt=90, col=2)

在此处输入图像描述

由于 2:4 的 Lambda 值在幅度上彼此非常接近,因此根据经验法则,这些被认为是微不足道的 - 即它们各自的 EOF 模式可能会重叠和混合,因为它们的幅度相似。这是不幸的,因为我们知道现场实际存在 9 个信号。

一种更主观的方法是查看对数转换的 Lambda 值(“Scree 图”),然后将回归拟合到尾随值。然后可以直观地确定 lambda 值位于该线上方的哪个级别:

屏幕图

ntrail <- 35
tail(L, ntrail)
fit <- lm(log(tail(L, ntrail)) ~ seq(length(L)-ntrail+1, length(L)))
plot(log(L))
abline(fit, col=2)

在此处输入图像描述

因此,5 个领先的 EOF 位于这条线之上。Xp当没有添加额外的噪声并且结果显示所有 9 个原始信号时,我已经尝试了这个示例。因此,EOF 6:9 的不显着性是因为它们的幅度低于现场噪声。

一种更客观的方法是 Overland 和 Preisendorfer (1982) 的“规则 N”标准。包中有一个实现wq,我在下面显示。

规则 N

library(wq)
eofNum(Xp, distr = "normal", reps = 99)

RN <- ruleN(nrow(Xp), ncol(Xp), type = "normal", reps = 99)
RN
eigs <- svd(cov(Xp))$d
plot(eigs, log="y")
lines(RN, col=2, lty=2)

在此处输入图像描述

规则 N 确定了 4 个重要的 EOF。就个人而言,我需要更好地理解这种方法;为什么可以根据不使用与中相同分布的随机场来衡量误差水平Xp这种方法的一种变体是对数据重新采样,Xp以便每列随机重新排列。这样,我们保证随机场的总方差与 相同Xp通过多次重采样,我们可以计算分解的基线误差。

带随机场的蒙特卡洛(即 Null 模型比较)

iter <- 499
LAMBDA <- matrix(NaN, ncol=iter, nrow=dim(Xp)[2])

set.seed(1)
for(i in seq(iter)){
    #i=1

    #random reorganize dimensions of scaled field
    Xp.tmp <- NaN*Xp
    for(j in seq(dim(Xp.tmp)[2])){
        #j=1
        Xp.tmp[,j] <- Xp[,j][sample(nrow(Xp))]
    }

    #make covariance matrix
    C.tmp <- t(Xp.tmp) %*% Xp.tmp #cov(Xp.tmp)

    #SVD decomposition
    E.tmp <- svd(C.tmp)

    #record Lambda (L) 
    LAMBDA[,i] <- E.tmp$d

    print(paste(round(i/iter*100), "%", " completed", sep=""))
}

boxplot(t(LAMBDA), log="y", col=8, border=2, outpch="")
points(L)

在此处输入图像描述

同样,4 个 EOF 高于随机场的分布。我对这种方法和规则 N 的担心是,它们并没有真正解决 Lambda 值的置信区间;例如,较高的第一个 Lambda 值将自动导致较低的方差量,由尾随的值来解释。因此,从随机场计算的 Lambda 将始终具有较小的斜率,并且可能导致选择的有效 EOF 太少。[注意:该eofNum()函数假定 EOF 是根据相关矩阵计算的。如果使用例如协方差矩阵(居中但未缩放的数据),此数字可能会有所不同。]

最后,@tomasz74 提到了 Babamoradi 等人的论文。(2013),我对此进行了简要介绍。它非常有趣,但似乎更专注于计算 EOF 负载和系数的 CI,而不是 Lambda。尽管如此,我相信可以采用相同的方法来评估 Lambda 误差。通过对行重新采样直到生成新字段,对数据字段进行引导重新采样。同一行可以多次重采样,这是一种非参数方法,不需要对数据的分布做出假设。

Lambda 值的引导程序

B <- 40 * nrow(Xp)
LAMBDA <- matrix(NaN, nrow=length(L), ncol=B)
for(b in seq(B)){
    samp.b <- NaN*seq(nrow(Xp))
    for(i in seq(nrow(Xp))){
        samp.b[i] <- sample(nrow(Xp), 1)
    }
    Xp.b  <- Xp[samp.b,]
    C.b  <- t(Xp.b) %*% Xp.b 
    E.b  <- svd(C.b)
    LAMBDA[,b] <- E.b$d
    print(paste(round(b/B*100), "%", " completed", sep=""))
}
boxplot(t(LAMBDA), log="y", col=8, outpch="", ylab="Lambda [log-scale]")
points(L, col=4)
legend("topright", legend=c("Original"), pch=1, col=4)

在此处输入图像描述

虽然这可能比 North 计算 Lambda 值误差的经验法则更可靠,但我现在相信 EOF 重要性的问题归结为对这意味着什么的不同意见。对于 North 的经验法则和 bootstrap 方法,重要性似乎更多地取决于 teere 是否是 Lambda 值之间的重叠。如果有,那么这些 EOF 可能会混合在它们的信号中,而不是代表“真实”模式。另一方面,这两个 EOF 可能描述了大量的方差(与随机场的分解相比 - 例如规则 N)。因此,如果有人对滤除噪声(即通过 EOF 截断)感兴趣,那么规则 N 就足够了。如果有人对确定数据集中的真实模式感兴趣,那么更严格的 Lambda 重叠标准可能会更稳健。

同样,我不是这些问题的专家,所以我仍然希望有更多经验的人可以补充这个解释。

参考:

Beckers、Jean-Marie 和 M. Rixen。“来自不完整海洋学数据集的 EOF 计算和数据填充。” 大气和海洋技术杂志 20.12 (2003): 1839-1856。

Overland, J. 和 R. Preisendorfer,适用于旋风气候学的主要成分的显着性检验,周一。威亚。修订版,110,1-4,1982。