OP は 1 年以上アクティブではありませんが、回答を投稿することにしました。X
の代わりにを使用します。統計ではS
、線形回帰のコンテキストで射影行列が必要になることがよくあります。X
y
H = X(X'X)^{-1}X'
Hy
この回答は、通常の最小二乗法のコンテキストを前提としています。加重最小二乗法については、加重最小二乗回帰の QR 分解からハット行列を取得する を参照してください。
概要
solve
一般的な正方行列の LU 分解に基づいています。対称である ( Rではなく でX'X
計算する必要があります。詳しくは、こちらを参照してください) 。crossprod(X)
t(X) %*% X
?crossprod
chol2inv
ただし、三角分解は因数分解よりも不安定ですQR
。これを理解するのは難しくありません。X
条件付き番号 がある場合kappa
、条件付き番号X'X
がありますkappa ^ 2
。これは、大きな数値上の困難を引き起こす可能性があります。表示されるエラー メッセージ:
# system is computationally singular: reciprocal condition number = 2.26005e-28
これを伝えているだけです。kappa ^ 2
約e-28
、機械精度よりかなり小さい程度e-16
です。許容範囲tol = .Machine$double.eps
でX'X
は、ランク不足と見なされるため、LU とコレスキー分解は失敗します。
通常、この状況では SVD または QR に切り替えますが、ピボットされたコレスキー分解も別の選択肢です。
- SVD は最も安定した方法ですが、コストがかかりすぎます。
- QR は、中程度の計算コストで十分に安定しており、実際に一般的に使用されています。
- Pivoted Cholesky は高速で、許容できる安定性があります。大きな行列の場合、これが優先されます。
以下では、3 つの方法すべてについて説明します。
QR 因数分解の使用

射影行列は順列に依存しないことに注意してください。つまり、ピボットを使用して QR 分解を実行するかどうかは問題ではありません。
R では、非ピボット QR 分解用のqr.default
LINPACK ルーチンと、ブロック ピボット付き QR 分解用の LAPACK ルーチンを呼び出すことができます。おもちゃのマトリックスを生成して、両方のオプションをテストしましょう。DQRDC
DGEQP3
set.seed(0); X <- matrix(rnorm(50), 10, 5)
qr_linpack <- qr.default(X)
qr_lapack <- qr.default(X, LAPACK = TRUE)
str(qr_linpack)
# List of 4
# $ qr : num [1:10, 1:5] -3.79 -0.0861 0.3509 0.3357 0.1094 ...
# $ rank : int 5
# $ qraux: num [1:5] 1.33 1.37 1.03 1.01 1.15
# $ pivot: int [1:5] 1 2 3 4 5
# - attr(*, "class")= chr "qr"
str(qr_lapack)
# List of 4
# $ qr : num [1:10, 1:5] -3.79 -0.0646 0.2632 0.2518 0.0821 ...
# $ rank : int 5
# $ qraux: num [1:5] 1.33 1.21 1.56 1.36 1.09
# $ pivot: int [1:5] 1 5 2 4 3
# - attr(*, "useLAPACK")= logi TRUE
# - attr(*, "class")= chr "qr"
$pivot
は 2 つのオブジェクトで異なることに注意してください。
次に、計算するラッパー関数を定義しますQQ'
。
f <- function (QR) {
## thin Q-factor
Q <- qr.qy(QR, diag(1, nrow = nrow(QR$qr), ncol = QR$rank))
## QQ'
tcrossprod(Q)
}
それを見て、同じ射影行列qr_linpack
を与えます。qr_lapack
H1 <- f(qr_linpack)
H2 <- f(qr_lapack)
mean(abs(H1 - H2))
# [1] 9.530571e-17
特異値分解の使用

R では、svd
特異値分解を計算します。上記の例の行列を引き続き使用しますX
。
SVD <- svd(X)
str(SVD)
# List of 3
# $ d: num [1:5] 4.321 3.667 2.158 1.904 0.876
# $ u: num [1:10, 1:5] -0.4108 -0.0646 -0.2643 -0.1734 0.1007 ...
# $ v: num [1:5, 1:5] -0.766 0.164 0.176 0.383 -0.457 ...
H3 <- tcrossprod(SVD$u)
mean(abs(H1 - H3))
# [1] 1.311668e-16
繰り返しますが、同じ射影行列が得られます。
ピボットコレスキー分解の使用

デモンストレーションのために、上記の例を引き続き使用しますX
。
## pivoted Chol for `X'X`; we want lower triangular factor `L = R'`:
## we also suppress possible rank-deficient warnings (no harm at all!)
L <- t(suppressWarnings(chol(crossprod(X), pivot = TRUE)))
str(L)
# num [1:5, 1:5] 3.79 0.552 -0.82 -1.179 -0.182 ...
# - attr(*, "pivot")= int [1:5] 1 5 2 4 3
# - attr(*, "rank")= int 5
## compute `Q'`
r <- attr(L, "rank")
piv <- attr(L, "pivot")
Qt <- forwardsolve(L, t(X[, piv]), r)
## P = QQ'
H4 <- crossprod(Qt)
## compare
mean(abs(H1 - H4))
# [1] 6.983997e-17
繰り返しますが、同じ射影行列が得られます。