假设我有以下数据结构:
l <- list(pos1 = list(a = 1, b = 1, c = "a"),
pos2 = list(a = 2, b = 2, c = "b", d = "u"))
我现在想要获取所有a
和b
元素的向量。我可以通过循环轻松地做到这一点:
a <- b <- vector("numeric", length(l))
for (i in seq_along(l)) {
a[[i]] <- l[[i]]$a
b[[i]] <- l[[i]]$b
}
a
# [1] 1 2
b
# [1] 1 2
现在我的问题是,有没有(矢量化?)方式没有循环这样做?或者循环已经是“最佳”解决方案?
答案 0 :(得分:1)
如果您可以保证l
中的元素数量等于l
的每个元素中的元素数量,您可以在没有循环的情况下将其拉出。但作为通用解决方案,您需要在某处执行此循环。
以下产生您想要的产品吗? (它可以适用于仅搜索a
和b
)。
reorganize_list <- function(l)
{
unique_elements <-
unique(unlist(lapply(l, function(x) unique(names(x)))))
L <- vector("list", length(unique_elements))
L <- setNames(L, unique_elements)
for (i in seq_along(L))
{
L[[i]] <- unname(unlist(sapply(l, function(x) x[names(L)[i]])))
}
L
}
l <- list(pos1 = list(a = 1, b = 1, c = "a"),
pos2 = list(a = 2, b = 2, c = "b", d = "u"))
reorganize_list(l)
稍加努力,我确信我能提出一个不需要for
循环的解决方案。但我并不乐观认为这将是一个更好的&#34;解决方案(最好是一个非常主观的术语)。
例如,我们可以轻松地将列表的每个元素转换为数据框并将所有行绑定在一起。我们可以通过几行代码完成此任务:
library(tibble)
library(dplyr)
lapply(l, as_tibble) %>%
bind_rows()
我们仍然应该权衡利弊。 reorganize_list
的优点和我在这里使用的tibble方法是它们可以容纳任意长度l
,其中每个l[[i]]
具有任意长度和名称。当然,您只追求a
和b
,并且结果中只需要两个向量,您可能无法评估这种普遍性水平。
原始解决方案和Hack-R解决方案的主要优势在于它们阅读简单。缺点是,如果您需要的不仅仅是两个向量,则必须对每个向量进行单独编码。
我们还要考虑计算效率。我定时讨论了这个问题中讨论的四种方法,结果如下:
library(microbenchmark)
microbenchmark(
#* A tibble solution
tibble = {lapply(l, as_tibble) %>%
bind_rows()},
#* The reorganize_list solution
reorganize = reorganize_list(l),
#* Hack-R solution
dframe = data.frame(a = unlist(l)[grepl(".a",names(unlist(l)))],
b = unlist(l)[grepl(".b",names(unlist(l)))],
row.names = NULL),
#* Your original solution
orig = {a <- b <- vector("numeric", length(l))
for (i in seq_along(l)) {
a[[i]] <- l[[i]]$a
b[[i]] <- l[[i]]$b
}}
)
Unit: microseconds
expr min lq mean median uq max neval cld
tibble 366.260 390.1590 416.52197 402.7685 414.0585 1589.669 100 d
reorganize 104.101 122.5755 130.29376 132.2530 138.7035 155.418 100 b
dframe 333.124 357.0230 371.36554 369.4855 381.3620 546.897 100 c
orig 5.865 8.2110 10.00567 10.5570 11.4370 16.128 100 a
事实证明,使用for
循环的原始解决方案到目前为止是最快的解决方案。即使有一点需要注意,这是一个很小的对象,我会怀疑for
循环会比其他解决方案更快,因为问题的规模是如此(我没有reorganize
将保持比其他人更快的信心。
我认为我们现在已经证明for
循环可能不是唯一的解决方案,但有一个很好的例子,它是该问题的最佳解决方案。