/edit: Further follow up now you can use irlba::prcomp_irlba
/edit: following up on my own post. irlba
now has "center" and "scale" arguments, which let you use it to calculate principle components, e.g:
pc <- M %*% irlba(M, nv=5, nu=0, center=colMeans(M), right_only=TRUE)$v
I have a large, sparse Matrix
of features I would like to use in a machine learning algorithm:
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)
Because this matrix has many columns, I would like to reduce its dimensionality to something more manageable. I can use the excellent irlba package to perform SVD and return the first n principal components (5 shown here; I'll probably use 100 or 500 on my actual dataset):
library(irlba)
pc <- irlba(M, nu=5)$u
However, I've read that prior to performing PCA, one should center the matrix (subtract the column mean from each column). This is very difficult to do on my dataset, and furthermore would destroy the sparsity of the matrix.
How "bad" is it to perform SVD on the un-scaled data, and feed it straight into a machine learning algorithm? Are there any efficient ways I could scale this data, while preserving the sparsity of the matrix?
/edit: A brought to my attention by B_miner, the "PCs" should really be:
pc <- M %*% irlba(M, nv=5, nu=0)$v
Also, I think whuber's answer should be pretty easy to implement, via the crossprod
function, which is extremely fast on sparse matrices:
system.time(M_Mt <- crossprod(M)) # 0.463 seconds
system.time(means <- colMeans(M)) #0.003 seconds
Now I'm not quite sure what to do to the means
vector before subtracting from M_Mt
, but will post as soon as I figure it out.
/edit3: Here's modified version of whuber's code, using sparse matrix operations for each step of the process. If you can store the entire sparse matrix in memory, it works very quickly:
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))
If you set the number of columns to 10,000 and the number of principal components to 25, the irlba
-based PCA takes about 17 minutes to calculate 50 approximate principal components and consumes about 6GB of RAM, which isn't too bad.
X %*% v %*% diag(d, ncol=length(d))
. The v matrix in the svd is equivalent to the "rotation" element of aprcomp
object, andX %*% v
orX %*% v %*% diag(d, ncol=length(d))
represents thex
element of aprcomp
object. Take a look astats:::prcomp.default
. $\endgroup$