我正在尝试加速重复稀疏数据帧以及随后添加生成的矩阵。一些背景信息:我想要重复稀疏的数据集非常大(大约 200 行和 >> 100 000 列)。到目前为止,我已经尝试过以下方法:
i) 重复稀疏并将稀疏数据帧存储在列表中以供后续矩阵添加。不幸的是,如果执行多次重复稀疏操作,内存很快就会成为限制因素。
ii)我还通过 foreach 并行化重复的稀疏,将稀疏的数据帧写入磁盘并稍后检索它们以进行矩阵加法。虽然第一部分相当快,但后者否定了所有速度优势。
遗憾的是,目前最快的解决方案是非并行解决方案:
library(vegan)
data <- matrix(sample(1:20, 10000, replace = TRUE), ncol = 100)
min_sample <- min(rowSums(data)[rowSums(data) > 0])
for (i in 1:100) {
if (i == 1) {
suppressWarnings(dataRF1 <- rrarefy(data, min_sample))
}
else if (i > 2) {
suppressWarnings(dataRF2 <- rrarefy(data, min_sample))
dataRF1 <- dataRF1 + dataRF2
}
}
我还尝试了 foreach 与
.combine = "+"
。然而,稀疏数据帧似乎也保存在内存中,并在所有稀疏发生后最早进行求和。
由于内存限制,我需要一种在构造单个稀薄数据帧后立即执行矩阵加法的解决方案或一种类似的内存高效且快速的方法。
感谢您的帮助。
更新: 谢谢@SamR 和另一位。事实上,通过 foreach 并行稀疏化,我知道并行处理所带来的速度增益并不是完全被开销所抵消,而是被从磁盘导入文件和下游矩阵加法所抵消(参见方法 ii)。我很清楚,通过套接字并行化,每个工作线程都以严格隔离的方式运行,因此,使用共享起始矩阵进行矩阵加法是不可能的。然而,我正在考虑分别为每个工人提供这样一个起始矩阵。
例如,通过foreach对三个worker进行处理。在每个工作线程中,通过矩阵加法对稀疏数据帧进行求和,从而产生总共三个矩阵,在并行处理完成后将它们自身求和。为此,我需要分别向每个工作人员提供这个起始矩阵(与输入数据帧具有相同维度的矩阵)。这可能吗?
检查
vegan::rrarefy
的代码表明它正在从多元超几何分布中获取独立(按行)样本。 rmvhyper
的 extraDistr
实现要快得多(可能是多个数量级,具体取决于 min_sample
的值)。
并行执行样本非常简单,并且可以相对节省内存。对 200 x 2e5 矩阵的 100 个稀疏样本的总和进行计时:
library(parallel)
data <- matrix(sample(1:20, 4e7, replace = TRUE), ncol = 2e5)
system.time({
rs <- rowSums(data)
min_sample <- min(rs[rs > 0])
n <- nrow(data)
# set up the parallel cluster
nc <- detectCores() - 1L
cl <- makeCluster(nc)
invisible(clusterEvalQ(cl, {library(extraDistr); library(Rfast)}))
clusterExport(cl, "min_sample")
# sum 100 random rarefied samples
dataRF <- matrix(
unlist(
parLapply(
cl, lapply(1:n, \(i) data[i,]),
\(x) as.integer(colsums(rmvhyper(100, x, min_sample)))
)
),
n, byrow = TRUE
)
stopCluster(cl)
})
#> user system elapsed
#> 1.30 0.53 94.92
对于所描述的矩阵维度,内存限制将取决于要求和的样本数量。
rmvhyper(100, x, min_sample)
会在每个集群节点中生成一个维度为 c(100, ncol(x))
的矩阵。对于许多样本,此过程可以重复多次(例如,对于 1000 个样本,再迭代调用 parLapply
9 次,并将其添加到之前的累积和中。