/edición: Para seguir con el seguimiento, ahora se puede utilizar irlba::prcomp_irlba
/edición: seguimiento de mi propio post. irlba
tiene ahora argumentos "centro" y "escala", que permiten utilizarlo para calcular los componentes principales, por ejemplo
pc <- M %*% irlba(M, nv=5, nu=0, center=colMeans(M), right_only=TRUE)$v
Tengo un gran y escaso 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)
Como esta matriz tiene muchas columnas, me gustaría reducir su dimensionalidad a algo más manejable. Puedo utilizar el excelente paquete irlba para realizar la SVD y devolver los primeros n componentes principales (5 mostrados 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 el PCA hay que centrar la matriz (restar la media de cada columna). Esto es muy difícil de hacer en mi conjunto de datos, y además destruiría la dispersión de la matriz.
¿Qué tan "malo" es realizar la SVD en los datos sin escalar, y alimentar directamente a un algoritmo de aprendizaje automático? ¿Hay alguna forma eficiente de escalar estos datos, preservando la dispersión de la matriz?
/edit: A traído a mi atención por B_miner, el "PC" debe ser realmente:
pc <- M %*% irlba(M, nv=5, nu=0)$v
Además, creo que la respuesta de whuber debe ser bastante fácil de implementar, a través de la crossprod
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 sé muy bien qué hacer con el means
vector antes de restar de M_Mt
pero lo publicaré tan pronto como lo resuelva.
/edit3: Aquí está la versión modificada del código de whuber, utilizando 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 se fija el número de columnas en 10.000 y el número de componentes principales en 25, el irlba
-El PCA basado en la tecnología de la información tarda unos 17 minutos en calcular 50 componentes principales aproximados y consume unos 6 GB de RAM, lo que no está nada mal.