/ editar: Acompanhamento adicional agora você pode usar o irlba :: prcomp_irlba
/ edit: acompanhando meu próprio post. irlba
agora possui argumentos de "centro" e "escala", que permitem usá-lo para calcular componentes principais, por exemplo:
pc <- M %*% irlba(M, nv=5, nu=0, center=colMeans(M), right_only=TRUE)$v
Eu tenho um grande número Matrix
de recursos que gostaria de usar em um algoritmo de aprendizado de máquina:
library(Matrix)
set.seed(42)
rows <- 500000
cols <- 10000
i <- unlist(lapply(1:rows, function(i) rep(i, sample(1:5,1))))
j <- sample(1:cols, length(i), replace=TRUE)
M <- sparseMatrix(i, j)
Como essa matriz possui muitas colunas, eu gostaria de reduzir sua dimensionalidade para algo mais gerenciável. Posso usar o excelente pacote irlba para executar SVD e retornar os primeiros n componentes principais (5 mostrados aqui; provavelmente usarei 100 ou 500 no meu conjunto de dados real):
library(irlba)
pc <- irlba(M, nu=5)$u
No entanto, li que antes de executar o PCA, deve-se centralizar a matriz (subtrair a média da coluna de cada coluna). Isso é muito difícil de fazer no meu conjunto de dados e, além disso, destruiria a esparsidade da matriz.
Quão "ruim" é executar SVD nos dados não dimensionados e alimentá-lo diretamente em um algoritmo de aprendizado de máquina? Existem maneiras eficientes de escalar esses dados, preservando a esparsidade da matriz?
/ edit: Quando B_miner me chamou a atenção, os "PCs" deveriam realmente ser:
pc <- M %*% irlba(M, nv=5, nu=0)$v
Além disso, acho que a resposta do whuber deve ser bastante fácil de implementar, através da crossprod
função, que é extremamente rápida em matrizes esparsas:
system.time(M_Mt <- crossprod(M)) # 0.463 seconds
system.time(means <- colMeans(M)) #0.003 seconds
Agora não tenho muita certeza do que fazer com o means
vetor antes de subtraí-lo M_Mt
, mas publicarei assim que eu descobrir.
/ edit3: Aqui está a versão modificada do código do whuber, usando operações de matriz esparsa para cada etapa do processo. Se você pode armazenar toda a matriz esparsa na memória, ela funciona muito rapidamente:
library('Matrix')
library('irlba')
set.seed(42)
m <- 500000
n <- 100
i <- unlist(lapply(1:m, function(i) rep(i, sample(25:50,1))))
j <- sample(1:n, length(i), replace=TRUE)
x <- sparseMatrix(i, j, x=runif(length(i)))
n_comp <- 50
system.time({
xt.x <- crossprod(x)
x.means <- colMeans(x)
xt.x <- (xt.x - m * tcrossprod(x.means)) / (m-1)
svd.0 <- irlba(xt.x, nu=0, nv=n_comp, tol=1e-10)
})
#user system elapsed
#0.148 0.030 2.923
system.time(pca <- prcomp(x, center=TRUE))
#user system elapsed
#32.178 2.702 12.322
max(abs(pca$center - x.means))
max(abs(xt.x - cov(as.matrix(x))))
max(abs(abs(svd.0$v / pca$rotation[,1:n_comp]) - 1))
Se você definir o número de colunas para 10.000 e o número de componentes principais para 25, o irlba
PCA baseado em leva cerca de 17 minutos para calcular 50 componentes principais aproximados e consome cerca de 6 GB de RAM, o que não é muito ruim.
X %*% v %*% diag(d, ncol=length(d))
. The v matrix in the svd is equivalent to the "rotation" element of a prcomp
object, and X %*% v
or X %*% v %*% diag(d, ncol=length(d))
represents the x
element of a prcomp
object. Take a look a stats:::prcomp.default
.