挑战:优化不公开[easy]

时间:2010-10-25 08:04:28

标签: list optimization r vector

因为最近SO有点慢,所以我发布了一个简单的问题。我很感激,如果大鱼留在板凳上为这一个,让新手有机会回应。

有时我们的对象具有大量的大型列表元素(向量)。您如何将此对象“取消”转换为单个向量。证明您的方法比unlist()更快。

4 个答案:

答案 0 :(得分:10)

如果你不需要名字而且你的名单有一个级别,那么如果你能打败

.Internal(unlist(your_list, FALSE, FALSE))

我将在接下来的1年内对您在SO上所做的一切进行投票!

[更新:如果需要非唯一名称并且列表不是递归的,那么这是一个比unlist提高100倍的版本

 myunlist <- function(l){
    names <- names(l)
    vec <- unlist(l, F, F)
    reps <- unlist(lapply(l, length), F, F)
    names(vec) <- rep(names, reps)
    vec
    }

 myunlist(list(a=1:3, b=2))
 a a a b 
 1 2 3 2 

 > tl <- list(a = 1:20000, b = 1:5000, c = 2:30)
 > system.time(for(i in 1:200) unlist(tl))
 user  system elapsed 
 22.97    0.00   23.00 

 > system.time(for(i in 1:200) myunlist(tl))
 user  system elapsed 
 0.2     0.0     0.2 

 > system.time(for(i in 1:200) unlist(tl, F, F))
 user  system elapsed 
 0.02    0.00    0.02 

[更新2:响应来自Richie Cotton的Nr3挑战。

bigList3 <- replicate(500, rnorm(1e3), simplify = F)

unlist_vit <- function(l){
    names(l) <- NULL
    do.call(c, l)
    }

library(rbenchmark)

benchmark(unlist = unlist(bigList3, FALSE, FALSE),
          rjc    = unlist_rjc(bigList3),
          vit    = unlist_vit(bigList3),
          order  = "elapsed",
          replications = 100,
          columns = c("test", "relative", "elapsed")
          )

    test  relative elapsed
1 unlist   1.0000    2.06
3    vit   1.4369    2.96
2    rjc   3.5146    7.24

PS:我认为“大鱼”是比你更有声望的人。所以我在这里很小:)。

答案 1 :(得分:2)

unlist()解决方案必须非常快才能击败unlist()不是吗?在这里,使用长度为100,000的2000个数字向量来取消列表列表只需不到两秒钟。

> bigList2 <- as.list(data.frame(matrix(rep(rnorm(1000000), times = 200), 
+                                       ncol = 2000)))
> print(object.size(bigList2), units = "Gb")
1.5 Gb
> system.time(foo <- unlist(bigList2, use.names = FALSE))
   user  system elapsed 
  1.897   0.000   2.019

在我的工作区中使用bigList2foo,R正在使用~9Gb的可用内存。关键是use.names = FALSE。没有它unlist()非常缓慢。究竟有多缓慢我还在等待......

我们可以通过设置recursive = FALSE来加快速度,然后我们实际上与VitoshKa的回答(两个有代表性的时间)相同:

> system.time(foo <- unlist(bigList2, recursive = FALSE, use.names = FALSE))
   user  system elapsed 
  1.379   0.001   1.416
> system.time(foo <- .Internal(unlist(bigList2, FALSE, FALSE)))
   user  system elapsed 
  1.335   0.000   1.344

...最后use.names = TRUE版本已完成......:

> system.time(foo <- unlist(bigList2, use = TRUE))
    user   system  elapsed 
2307.839   10.978 2335.815

它消耗了我所有系统的16Gb内存,所以我放弃了......

答案 2 :(得分:2)

c()具有逻辑参数recursive,当设置为TRUE时,该参数将递归地取消列出向量(默认值为FALSE)。

l <- replicate(500, rnorm(1e3), simplify = F)

microbenchmark::microbenchmark(
  unlist = unlist(l, FALSE, FALSE),
  c = c(l, recursive = TRUE, use.names = FALSE)
)

# Unit: milliseconds
# expr      min       lq     mean   median       uq      max neval
# unlist 3.083424 3.121067 4.662491 3.172401 3.985668 27.35040   100
#      c 3.084890 3.133779 4.090520 3.201246 3.920646 33.22832   100

答案 3 :(得分:0)

作为一种中等大小的鱼,我正在尝试第一次尝试解决方案,为小鱼提供基准。它比unlist慢约3倍。

我正在使用较小版本的ucfagls测试列表。 (因为它更适合记忆。)

bigList3 <- as.list(data.frame(matrix(rep(rnorm(1e5), times = 200), ncol = 2000)))

基本思路是创建一个长向量来存储答案,然后遍历列表项复制列表中的值。

unlist_rjc <- function(l)
{
  lengths <- vapply(l, length, FUN.VALUE = numeric(1), USE.NAMES = FALSE)
  total_len <- sum(lengths)
  end_index <- cumsum(lengths)
  start_index <- 1 + c(0, end_index)
  v <- numeric(total_len)
  for(i in seq_along(l))
  {
    v[start_index[i]:end_index[i]] <- l[[i]]
  }
  v
}

t1 <- system.time(for(i in 1:10) unlist(bigList2, FALSE, FALSE))
t2 <- system.time(for(i in 1:10) unlist_rjc(bigList2))
t2["user.self"] / t1["user.self"]  # 3.08

小鱼的挑战:
你可以扩展它来处理除数字以外的其他类型吗?
你能用它来处理递归(嵌套列表)吗? 你能加快速度吗?

我会支持任何积分少于我的人,他的答案会遇到一个或多个这些小挑战。