将巨大的稀疏矩阵转换为 data.table 以便在 R 中更快地进行子集化

Convert huge sparse matrix into data.table for faster subsetting in R

我有一个大问题,还有一个更具体的问题,我希望在解决后能够解决更大的问题。如果有人有任何想法让我尝试,我将不胜感激。

基本上我有一个巨大的稀疏矩阵(大约 300k x 150k,最初是使用 R 的 {tm} 包创建的术语文档矩阵),使用 {slam} 包保存为简单的三元组矩阵,我是运行 一个循环遍历术语集然后根据这些术语对其进行子集化的函数。不幸的是,子集化过程非常缓慢。

在试图弄清楚如何更快地进行子集化时,我偶然发现了 data.table 包,它在我 运行 的一些测试中表现非常好。但是,当我尝试将我的稀疏矩阵转换为 data.table 时,我得到

Error in vector(typeof(x$v), nr * nc) : vector size cannot be NA
In addition: Warning message:
In nr * nc : NAs produced by integer overflow

我理解这是因为它首先尝试将其转换为标准矩阵,从技术上讲这是 R 的向量,300k*150k 远高于 .Machine$integer.max

所以我的问题是:有谁知道如何将简单的三元组矩阵转换为 data.frame 或 data.table 而无需先将其转换为矩阵,从而避免整数溢出?

如果没有,是否有人 a) 有其他解决方法或 b) 对快速对巨大的稀疏矩阵 and/or 简单三重矩阵进行子集化有任何建议?

下面是一个可重复的例子。在我的机器上,对前 10 行中的每一行进行子集化的循环大约需要 3 秒。一旦我们开始遍历数十万行,那很快就会让人望而却步。在此先感谢您的帮助:

require(slam)
STM <- simple_triplet_matrix(i = as.integer(runif(10000000,1,300000)), 
                  j = as.integer(runif(10000000,1,150000)),
                  v = rep(rnorm(10), 1000000),
                  nrow = 300000,
                  ncol = 150000)

start <- Sys.time()
for (i in 1:10) {
  vec <- as.matrix(STM[,i])
}
Sys.time() - start

旁注:请注意,如果您尝试 STMm <- as.matrix(STM),您会得到与上面显示的相同的溢出错误。

STM对象其实就是一个列表,可以正常取子集:

STM_DT <- data.table(i = STM$i, j = STM$j, v = STM$v)

这给出:

> STM_DT
               i      j           v
       1: 186598    756  0.34271080
       2: 278329  72334  2.03924976
       3: 178388  32708  1.03925605
       4: 260635 101424  0.05780086
       5: 169321 126202  1.00027529
      ---                          
 9999996:  96209  90019 -1.09341023
 9999997:  54467  16612 -2.08070273
 9999998: 179029  96906 -0.86197333
 9999999: 153017 148731  0.47765003
10000000: 104145 123291  0.24258613

速度几乎是瞬时的

您很可能需要这样的东西。

这个例子最初是为了解决一个更具体的问题,如何将稀疏(但巨大)模型矩阵附加到数据 table.

# New Example
set.seed(0)
df = data.frame(matrix(letters[sample(4,15,replace=TRUE)],5))
mat = Matrix::sparse.model.matrix(~.-1,df)
mat

示例稀疏矩阵:

5 x 8 sparse Matrix of class "dgCMatrix"
  X1a X1b X1c X1d X2b X2c X3b X3c
1   .   1   .   .   1   .   1   .
2   1   .   .   .   .   .   .   1
3   .   .   .   1   .   1   .   1
4   .   .   1   .   .   1   .   .
5   1   .   .   .   1   .   .   .

print(object.size(mat))

3760 字节

具有属性:

mat@i # 0-based row index

[1] 1 4 0 3 2 0 4 2 3 0 1 2

mat@p # 0-based column start

[1] 0 2 3 4 5 7 9 10 12

这里是如何转换为数据table:

# Conversion to Data Table
dt = data.table::data.table(matrix(FALSE,nrow(mat),ncol(mat)))
setnames(dt,colnames(mat))

for(cStart in 1:ncol(mat))
  set(dt, i = mat@i[(mat@p[cStart]:(mat@p[cStart+1L]-1L))+1L]+1L, 
j=colnames(mat)[cStart], value=TRUE)

print(object.size(dt))

2696 字节

dt[,lapply(.SD, as.integer)]

Returns想要的:

   X1a X1b X1c X1d X2b X2c X3b X3c
1:   0   1   0   0   1   0   1   0
2:   1   0   0   0   0   0   0   1
3:   0   0   0   1   0   1   0   1
4:   0   0   1   0   0   1   0   0
5:   1   0   0   0   1   0   0   0