尽管 OP 已经超过一年没有活跃了,但我仍然决定发表一个回答。在统计学中,我们经常需要在线性回归背景下使用投影矩阵,因此我会使用
X
而不是
S
。其中,
X
是模型矩阵,
y
是响应向量,而
H = X(X'X)^{-1}X'
是帽子 / 投影矩阵,因此
Hy
给出预测值。
这个答案假设普通最小二乘的情况。对于加权最小二乘,请参见从 QR 分解获取加权最小二乘回归的帽子矩阵。
概述
solve
基于一般方阵的 LU 分解。对于对称的 X'X
(应该通过 crossprod(X)
计算,而不是在 R 中使用 t(X) %*% X
,请参阅 ?crossprod
以了解更多信息),我们可以使用基于 Choleksy 分解的 chol2inv
。
然而,三角分解比 QR 分解不太稳定。这不难理解。如果 X
的条件数为 kappa
,那么 X'X
的条件数将为 kappa ^ 2
。这可能会导致很大的数值困难。您收到的错误消息如下:
这只是在说明:kappa ^ 2
约为e-28
,远小于机器精度约e-16
。使用容差 tol = .Machine$double.eps
,X'X
将被视为秩不足,因此 LU 和 Cholesky 分解会崩溃。
通常情况下,我们会在这种情况下切换到 SVD 或 QR 方法,但带轴点的 Cholesky 分解是另一种选择。
- SVD 是最稳定的方法,但太昂贵了;
- QR 具有令人满意的稳定性,计算成本适中,并且在实践中常用;
- 带轴点的 Cholesky 快速,稳定性可接受。 对于大矩阵,它更受欢迎。
接下来,我将解释这三种方法。
使用 QR 分解
![QR factorization](https://istack.dev59.com/gNAa2.webp)
注意,投影矩阵与置换无关,即执行 QR 分解时是否使用轴点无关紧要。
在 R 中,qr.default
可以调用 LINPACK 例程 DQRDC
进行非轴点 QR 分解,并使用 LAPACK 例程 DGEQP3
进行块带轴点 QR 分解。 让我们生成一个玩具矩阵并测试两个选项:
set.seed(0); X <- matrix(rnorm(50), 10, 5)
qr_linpack <- qr.default(X)
qr_lapack <- qr.default(X, LAPACK = TRUE)
str(qr_linpack)
str(qr_lapack)
注意两个对象的$pivot
是不同的。
现在,我们定义一个包装函数来计算QQ'
:
f <- function (QR) {
Q <- qr.qy(QR, diag(1, nrow = nrow(QR$qr), ncol = QR$rank))
tcrossprod(Q)
}
我们将看到,
qr_linpack
和
qr_lapack
会给出相同的投影矩阵:
H1 <- f(qr_linpack)
H2 <- f(qr_lapack)
mean(abs(H1 - H2))
使用奇异值分解
![SVD](https://istack.dev59.com/g8Hjd.webp)
在 R 中,svd
函数可以计算矩阵的奇异值分解。我们仍然使用上面的示例矩阵 X
:
SVD <- svd(X)
str(SVD)
H3 <- tcrossprod(SVD$u)
mean(abs(H1 - H3))
我们再次得到相同的投影矩阵。
使用枢轴Cholesky分解
![Pivoted Cholesky factorization](https://istack.dev59.com/L8pGJ.webp)
为了演示,我们仍然使用上面的示例X
。
L <- t(suppressWarnings(chol(crossprod(X), pivot = TRUE)))
str(L)
r <- attr(L, "rank")
piv <- attr(L, "pivot")
Qt <- forwardsolve(L, t(X[, piv]), r)
H4 <- crossprod(Qt)
mean(abs(H1 - H4))
再次,我们得到了相同的投影矩阵。