列表中数值向量的累积和
Cumulative sum of numeric vectors in list
我希望有人能够帮助我解决这个问题。我有一个包含 48 个向量的列表对象,每个向量的长度为 2,000,000 个观察值。下面是创建相同结构的代码,每个向量只有 100,000 个项目:
mtx_sim <- matrix(data = runif(48 * 100000), ncol = 48, nrow = 100000)
mtx_list <- as.list(data.frame(mtx_sim))
我想对列表中的每一行向量求和。但是,有一个规定我只想对最后三十个向量求和。例如,列表中的第 35 个向量应添加到前面的 34 个向量中。另一方面,列表中的第四个向量应添加到前面的三个向量(向量编号三、二和一)。这是我的代码示例,它依赖于 lapply 函数结合 rowSums,它相对较慢:
start <- c(rep(1, times = 30), seq(2, 19, 1))
end <- seq(1,48,1)
system.time(xxx <- lapply(1:48, function(x)
rowSums(
matrix(
unlist(mtx_list[start[x]:end[x]]),
ncol = (end[x] - start[x] + 1)))
) )
user system elapsed
62.19 0.56 63.04
有没有人有优化代码的想法?
你在一个合理的算法中做了两件昂贵的事情:
- 您正在为每次迭代从您的列表中重新创建一个矩阵;这可能很慢
- 您正在反复重新计算整行总和,而实际上您只需要计算边际变化
这是一个替代方案。我们将原始矩阵重构一次,然后只添加边缘列。
fun_brodie <- function(mtx_list) {
mtx <- do.call(cbind, mtx_list)
base <- mtx[, 1]
res <- list(base)
for(i in seq(ncol(mtx))[-1])
res[[i]] <- res[[i - 1]] + mtx[, i] - if(i > 30) mtx[, i - 30] else 0
res
}
res <- fun_brodie(mtx_list)
确认等于:
all.equal(res, xxx)
# [1] TRUE
基准:
library(microbenchmark)
microbenchmark(times=3, fun_marat(mtx_list), fun_brodie(mtx_list), fun_op(mtx_list))
生产:
Unit: milliseconds
expr min lq mean
fun_marat(mtx_list) 1661.9135 1763.418 1800.3530
fun_brodie(mtx_list) 115.7877 116.061 153.6794
fun_op(mtx_list) 58059.7803 60388.303 62060.5557
感谢 Marat 指出我的解释错误。另外,请注意,为了使 fun_marat
可兼容,我添加了一个将列表绑定到数据框的步骤。
您可以使用此解决方案:
M <- t(apply(mtx_sim,1,cumsum))
if (ncol(M)>30) {
i <- 31:ncol(M)
M[,i] <- M[,i] - M[,i-30]
}
M
我希望有人能够帮助我解决这个问题。我有一个包含 48 个向量的列表对象,每个向量的长度为 2,000,000 个观察值。下面是创建相同结构的代码,每个向量只有 100,000 个项目:
mtx_sim <- matrix(data = runif(48 * 100000), ncol = 48, nrow = 100000)
mtx_list <- as.list(data.frame(mtx_sim))
我想对列表中的每一行向量求和。但是,有一个规定我只想对最后三十个向量求和。例如,列表中的第 35 个向量应添加到前面的 34 个向量中。另一方面,列表中的第四个向量应添加到前面的三个向量(向量编号三、二和一)。这是我的代码示例,它依赖于 lapply 函数结合 rowSums,它相对较慢:
start <- c(rep(1, times = 30), seq(2, 19, 1))
end <- seq(1,48,1)
system.time(xxx <- lapply(1:48, function(x)
rowSums(
matrix(
unlist(mtx_list[start[x]:end[x]]),
ncol = (end[x] - start[x] + 1)))
) )
user system elapsed
62.19 0.56 63.04
有没有人有优化代码的想法?
你在一个合理的算法中做了两件昂贵的事情:
- 您正在为每次迭代从您的列表中重新创建一个矩阵;这可能很慢
- 您正在反复重新计算整行总和,而实际上您只需要计算边际变化
这是一个替代方案。我们将原始矩阵重构一次,然后只添加边缘列。
fun_brodie <- function(mtx_list) {
mtx <- do.call(cbind, mtx_list)
base <- mtx[, 1]
res <- list(base)
for(i in seq(ncol(mtx))[-1])
res[[i]] <- res[[i - 1]] + mtx[, i] - if(i > 30) mtx[, i - 30] else 0
res
}
res <- fun_brodie(mtx_list)
确认等于:
all.equal(res, xxx)
# [1] TRUE
基准:
library(microbenchmark)
microbenchmark(times=3, fun_marat(mtx_list), fun_brodie(mtx_list), fun_op(mtx_list))
生产:
Unit: milliseconds
expr min lq mean
fun_marat(mtx_list) 1661.9135 1763.418 1800.3530
fun_brodie(mtx_list) 115.7877 116.061 153.6794
fun_op(mtx_list) 58059.7803 60388.303 62060.5557
感谢 Marat 指出我的解释错误。另外,请注意,为了使 fun_marat
可兼容,我添加了一个将列表绑定到数据框的步骤。
您可以使用此解决方案:
M <- t(apply(mtx_sim,1,cumsum))
if (ncol(M)>30) {
i <- 31:ncol(M)
M[,i] <- M[,i] - M[,i-30]
}
M