我正在尝试减少 R 代码的计算时间。我发现其中一部分来自访问大矩阵的少量行。所以我正在创建一个 mwe 来解释。
我有一个尺寸为 100000 x 20 的矩阵
mt
。我有一个包含 100000 个元素的列表 nbr
,每个元素都是长度为 500 的向量,并且是 1:100000 的子集(请注意,在我的原始代码中, nbr
中的向量长度并不完全相同,但我是为了这个例子而制作它们的,所以我无法将 nbr
保存为矩阵)。我循环遍历 nbr
的元素,对于每个 x
,我计算 which.min(colSums(mt[x,]))
。这是重新创建的代码。
mt <- matrix(runif(100000 * 20), ncol = 20)
nbr <- lapply(1:100000, function(i) sample.int(100000, 500)) #takes 10-15 seconds
p1 <- proc.time()
out <- sapply(nbr, function(x) {
which.min(colSums(mt[x,]))
})
p2 <- proc.time()
p2 - p1
在我的计算机上运行大约需要 50-60 秒。请注意,即使我固定索引 1:500 而不是
x
,那么运行也需要大约 25-30 秒,因此时间消耗并不完全来自访问 nbr
的元素。但对于固定索引,我会提前计算子矩阵。无论如何,现在,对于所有 i,子矩阵 mt[x,]
的尺寸为 500 x 20。我发现如果我只有一个 500 x 20 矩阵而不是 mt[x,]
,那么运行时间会少得多,比如 3-4 秒。
mt2 <- matrix(runif(500 * 20), ncol = 20)
p1 <- proc.time()
out <- sapply(1:100000, function(i) {
which.min(colSums(mt2))
})
p2 <- proc.time()
p2 - p1
因此,大量时间花费在访问
mt
的行和 nbr
的元素上。无论如何,我可以减少这个时间,使其更接近 500 x 20 矩阵的实际时间吗?
set.seed(42)
mt <- matrix(runif(100000 * 20), ncol = 20)
nbr <- lapply(1:100000, function(i) sample.int(100000, 500)) #takes 10-15 seconds
p1 <- proc.time()
out <- sapply(nbr, function(x) {
which.min(colSums(mt[x,]))
})
p2 <- proc.time()
p2 - p1
# user system elapsed
#18.86 0.97 19.84
library(data.table)
system.time({
l <- lengths(nbr)
vnbr <- do.call(c, nbr)
DT <- data.table(mt)
DT <- DT[vnbr] #do all subsets at once
DT[, g := rep(seq_along(nbr), l)] #add grouping ID for the subsets
DTagg <- DT[, lapply(.SD, sum), by = g] #column sums by group
out1 <- melt(DTagg, id.vars = "g")[, which.min(value), by = g][["V1"]] #melt and which.min
})
# user system elapsed
#16.85 1.45 2.94
all.equal(out, out1)
#[1] TRUE