向量化和并行化列表的分解
vectorizing & parallelizing the disagregation of a list
下面是一些生成 data.frame
列表的代码,然后将该原始列表转换为新列表,其中每个列表元素都是每个数据框的行列表。
例如。
- l1
的长度为 10,每个元素都是一个包含 1000 行的 data.frame
。
- l2
是长度为 1000 (nrow(l1[[k]])
) 的列表,每个元素都是长度为 10 (length(l1)
) 的 list
,包含来自 [=13] 元素的行向量=]
l1 <- vector("list", length= 10)
set.seed(65L)
for (i in 1:10) {
l1[[i]] <- data.frame(matrix(rnorm(10000),ncol=10))
}
l2 <- vector(mode="list", length= nrow(l1[[1]]))
for (i in 1:nrow(l1[[1]])) {
l2[[i]] <- lapply(l1, function(l) return(unlist(l[i,])))
}
Edit 为了阐明 l1
与 l2
的关系,这里是语言无关代码。
for (j in 1:length(l1) {
for (i in 1:nrow(l1[[1]]) { # where nrow(l1[[1]]) == nrow(l1[[k]]) k= 2,...,10
l2[[i]][[j]] <- l1[[j]][i,]
}
}
如何通过矢量化或并行化加快 l2
的创建速度? 我遇到的问题是 parallel::parLapplyLB
拆分列表;但是,我不想拆分列表 l1
,我想做的是拆分 l1
的每个元素内的行。一个中间解决方案将通过使用一些 *apply
函数来替换 for 循环来矢量化我当前的方法。这显然也可以扩展到并行解决方案。
如果在可接受的解决方案之前我自己解决了这个问题,我会post在这里回答。
我会完全破坏结构并通过 split
重建第二个列表。这种方法比原来的方法需要更多的内存,但至少对于给定的例子,它要快 10 倍以上:
sgibb <- function(x) {
## get the lengths of all data.frames (equal to `sapply(x, ncol)`)
n <- lengths(x)
## destroy the list structure
y <- unlist(x, use.names = FALSE)
## generate row indices (stores the information which row the element in y
## belongs to)
rowIndices <- unlist(lapply(n, rep.int, x=1L:nrow(x[[1L]])))
## split y first by rows
## and subsequently loop over these lists to split by columns
lapply(split(y, rowIndices), split, f=rep.int(seq_along(n), n))
}
alex <- function(x) {
l2 <- vector(mode="list", length= nrow(x[[1]]))
for (i in 1:nrow(x[[1]])) {
l2[[i]] <- lapply(x, function(l) return(unlist(l[i,])))
}
l2
}
## check.attributes is need because the names differ
all.equal(alex(l1), sgibb(l1), check.attributes=FALSE)
library(rbenchmark)
benchmark(alex(l1), sgibb(l1), order = "relative", replications = 10)
# test replications elapsed relative user.self sys.self user.child sys.child
#2 sgibb(l1) 10 0.808 1.000 0.808 0 0 0
#1 alex(l1) 10 11.970 14.814 11.972 0 0 0
下面是一些生成 data.frame
列表的代码,然后将该原始列表转换为新列表,其中每个列表元素都是每个数据框的行列表。
例如。
- l1
的长度为 10,每个元素都是一个包含 1000 行的 data.frame
。
- l2
是长度为 1000 (nrow(l1[[k]])
) 的列表,每个元素都是长度为 10 (length(l1)
) 的 list
,包含来自 [=13] 元素的行向量=]
l1 <- vector("list", length= 10)
set.seed(65L)
for (i in 1:10) {
l1[[i]] <- data.frame(matrix(rnorm(10000),ncol=10))
}
l2 <- vector(mode="list", length= nrow(l1[[1]]))
for (i in 1:nrow(l1[[1]])) {
l2[[i]] <- lapply(l1, function(l) return(unlist(l[i,])))
}
Edit 为了阐明 l1
与 l2
的关系,这里是语言无关代码。
for (j in 1:length(l1) {
for (i in 1:nrow(l1[[1]]) { # where nrow(l1[[1]]) == nrow(l1[[k]]) k= 2,...,10
l2[[i]][[j]] <- l1[[j]][i,]
}
}
如何通过矢量化或并行化加快 l2
的创建速度? 我遇到的问题是 parallel::parLapplyLB
拆分列表;但是,我不想拆分列表 l1
,我想做的是拆分 l1
的每个元素内的行。一个中间解决方案将通过使用一些 *apply
函数来替换 for 循环来矢量化我当前的方法。这显然也可以扩展到并行解决方案。
如果在可接受的解决方案之前我自己解决了这个问题,我会post在这里回答。
我会完全破坏结构并通过 split
重建第二个列表。这种方法比原来的方法需要更多的内存,但至少对于给定的例子,它要快 10 倍以上:
sgibb <- function(x) {
## get the lengths of all data.frames (equal to `sapply(x, ncol)`)
n <- lengths(x)
## destroy the list structure
y <- unlist(x, use.names = FALSE)
## generate row indices (stores the information which row the element in y
## belongs to)
rowIndices <- unlist(lapply(n, rep.int, x=1L:nrow(x[[1L]])))
## split y first by rows
## and subsequently loop over these lists to split by columns
lapply(split(y, rowIndices), split, f=rep.int(seq_along(n), n))
}
alex <- function(x) {
l2 <- vector(mode="list", length= nrow(x[[1]]))
for (i in 1:nrow(x[[1]])) {
l2[[i]] <- lapply(x, function(l) return(unlist(l[i,])))
}
l2
}
## check.attributes is need because the names differ
all.equal(alex(l1), sgibb(l1), check.attributes=FALSE)
library(rbenchmark)
benchmark(alex(l1), sgibb(l1), order = "relative", replications = 10)
# test replications elapsed relative user.self sys.self user.child sys.child
#2 sgibb(l1) 10 0.808 1.000 0.808 0 0 0
#1 alex(l1) 10 11.970 14.814 11.972 0 0 0