/ edit: Seguimiento adicional ahora puedes usar irlba :: prcomp_irlba
/ edit: siguiendo mi propio post. irlba
ahora tiene argumentos de "centro" y "escala", que le permiten usarlo para calcular componentes principales, por ejemplo:
pc <- M %*% irlba(M, nv=5, nu=0, center=colMeans(M), right_only=TRUE)$v
Tengo una gran variedad Matrix
de características que me gustaría utilizar en un algoritmo de aprendizaje automático:
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)
Debido a que esta matriz tiene muchas columnas, me gustaría reducir su dimensionalidad a algo más manejable. Puedo usar el excelente paquete irlba para realizar SVD y devolver los primeros n componentes principales (se muestran 5 aquí; probablemente usaré 100 o 500 en mi conjunto de datos real):
library(irlba)
pc <- irlba(M, nu=5)$u
Sin embargo, he leído que antes de realizar PCA, uno debe centrar la matriz (restar la media de la columna de cada columna). Esto es muy difícil de hacer en mi conjunto de datos, y además destruiría la escasez de la matriz.
¿Qué tan "malo" es realizar SVD en los datos no escalados y alimentarlos directamente en un algoritmo de aprendizaje automático? ¿Hay alguna forma eficiente de escalar estos datos, preservando la escasez de la matriz?
/ edit: A me llamó la atención B_miner, las "PC" realmente deberían ser:
pc <- M %*% irlba(M, nv=5, nu=0)$v
Además, creo que la respuesta de Whuber debería ser bastante fácil de implementar, a través de la crossprod
función, que es extremadamente rápida en matrices dispersas:
system.time(M_Mt <- crossprod(M)) # 0.463 seconds
system.time(means <- colMeans(M)) #0.003 seconds
Ahora no estoy muy seguro de qué hacer con el means
vector antes de restarlo M_Mt
, pero lo publicaré tan pronto como lo resuelva.
/ edit3: Aquí está la versión modificada del código de whuber, usando operaciones de matriz dispersa para cada paso del proceso. Si puede almacenar toda la matriz dispersa en la memoria, funciona muy rápidamente:
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))
Si establece el número de columnas en 10,000 y el número de componentes principales en 25, el irlba
PCA basado en la computadora demora aproximadamente 17 minutos para calcular 50 componentes principales aproximados y consume alrededor de 6GB de RAM, lo cual no es tan malo.
X %*% v %*% diag(d, ncol=length(d))
. La matriz v en el svd es equivalente al elemento de "rotación" de un prcomp
objeto, y / X %*% v
o X %*% v %*% diag(d, ncol=length(d))
representa el x
elemento de un prcomp
objeto. Echa un vistazo a stats:::prcomp.default
.