7

オンラインで検索すると、入力に対して主成分分析を実行covmatする function でのフラグの使用について議論しているスレッドがいくつかあります。パラメータが定義されていない場合は、princomp最初に入力の標本共分散行列を計算します。covmatprincomp

一方、同様の関数prcompが実際にデータの主成分分析のタスクを達成するために何をするかについての議論はほとんどまたはまったくなく、それがより正確であるかどうかについての議論のみですprincomp. ここで疑問が生じます:prcomp共分散行列またはサンプル データ行列を入力として取りますか? (非式のコンテキストで)次のように述べているヘルプドキュメントからは明らかではありません。

デフォルトの S3 メソッド:

prcomp(x, retx = TRUE, center = TRUE, scale. = FALSE, tol = NULL, ...)

x- 主成分分析のデータを提供する数値または複素行列 (またはデータ フレーム)。

ヘルプ ファイルには、このメソッドの例は含まれていません。式オブジェクトで機能する上記のドキュメントの例のみです。ドキュメントは、予想される入力が次のような共分散行列であることを暗示しています。

計算は、共分散行列を使用するのではなく、(中央に配置され、場合によってはスケーリングされた) データ行列の特異値分解によって行われますeigen

ただし、「データ行列」が「共分散行列」であるかどうか、および「データ行列」xがドキュメントの前半で与えられた意味であるかどうかは不明です。

4

1 に答える 1

4

幸いなことに、答えは 2 つの関数のソース コードにあります。

まず、のソースprcomp:

> stats:::prcomp.default
function (x, retx = TRUE, center = TRUE, scale. = FALSE, tol = NULL, 
    ...) 
{
    x <- as.matrix(x)
    x <- scale(x, center = center, scale = scale.)
    cen <- attr(x, "scaled:center")
    sc <- attr(x, "scaled:scale")
    if (any(sc == 0)) 
        stop("cannot rescale a constant/zero column to unit variance")
    s <- svd(x, nu = 0)
    s$d <- s$d/sqrt(max(1, nrow(x) - 1))
    if (!is.null(tol)) {
        rank <- sum(s$d > (s$d[1L] * tol))
        if (rank < ncol(x)) {
            s$v <- s$v[, 1L:rank, drop = FALSE]
            s$d <- s$d[1L:rank]
        }
    }
    dimnames(s$v) <- list(colnames(x), paste0("PC", seq_len(ncol(s$v))))
    r <- list(sdev = s$d, rotation = s$v, center = if (is.null(cen)) FALSE else cen, 
        scale = if (is.null(sc)) FALSE else sc)
    if (retx) 
        r$x <- x %*% s$v
    class(r) <- "prcomp"
    r
}

上のブロックでは共分散計算が実行されていないことに注意してください。提供された入力に対してスケーリングとセンタリング操作が実行され、その時点で結果に対して特異値分解 (SVD) 関数が呼び出されます。次のステップは、結果のサイズを結果の対角化のランクに対してチェックして、結果が有効であることを確認することです。最後に、出力がフォーマットされ、適切なクラスに設定されます。

言い換えれば、prcomp共分散行列で単純に SVD を呼び出すのは良い改善ですが、共分散行列は計算されません。prcompデータに対して呼び出されるのではなく、いくつかのデータの共分散の提供された推定値に対して呼び出されます

編集:取り消し線が間違っています!この場合、共分散行列を作成する必要はありません。これは、数学の帽子をきちんとかぶっていれば実現できたはずです! 理由については、この math.SO スレッドを参照してください。ここでは、データ行列で SVD を使用して主成分を計算する方が確実に効率的です。

以下のコードと比較してくださいprincomp(一部のみを示します)。

if (is.list(covmat)) {
    if (any(is.na(match(c("cov", "n.obs"), names(covmat))))) 
        stop("'covmat' is not a valid covariance list")
    cv <- covmat$cov
    n.obs <- covmat$n.obs
    cen <- covmat$center
}
else if (is.matrix(covmat)) {
    if (!missing(x)) 
        warning("both 'x' and 'covmat' were supplied: 'x' will be ignored")
    cv <- covmat
    n.obs <- NA
    cen <- NULL
}
else if (is.null(covmat)) {
    dn <- dim(z)
    if (dn[1L] < dn[2L]) 
        stop("'princomp' can only be used with more units than variables")
    covmat <- cov.wt(z)
    n.obs <- covmat$n.obs
    cv <- covmat$cov * (1 - 1/n.obs)
    cen <- covmat$center
}

ご覧のとおり、このprincomp関数は、入力がどのように渡されるかによってさらに多くのことを行うため、より注意が必要です。

于 2013-11-10T23:07:07.993 に答える