从循环列表中提取元素没有循环

时间:2016-08-23 13:20:41

标签: r list

假设我有以下数据结构:

l <- list(pos1 = list(a = 1, b = 1, c = "a"),
          pos2 = list(a = 2, b = 2, c = "b", d = "u"))

我现在想要获取所有ab元素的向量。我可以通过循环轻松地做到这一点:

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

现在我的问题是,有没有(矢量化?)方式没有循环这样做?或者循环已经是“最佳”解决方案?

1 个答案:

答案 0 :(得分:1)

如果您可以保证l中的元素数量等于l的每个元素中的元素数量,您可以在没有循环的情况下将其拉出。但作为通用解决方案,您需要在某处执行此循环。

以下产生您想要的产品吗? (它可以适用于仅搜索ab)。

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]]具有任意长度和名称。当然,您只追求ab,并且结果中只需要两个向量,您可能无法评估这种普遍性水平。

原始解决方案和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循环可能不是唯一的解决方案,但有一个很好的例子,它是该问题的最佳解决方案。