根据索引向量从列表中提取元素。

3

我希望能够根据存储在另一个向量中的索引从列表中提取元素。

这是我的尝试:

list_positions<-c(2,3,4)
my_list<-list(c(1,3,4),c(2,3,4,5,6),c(1,2,3,4,6))

my_fun<-function(x,y){
  x[y]
}

mapply(my_fun,x=my_list,y=list_positions)

也许有人可以提供更快的解决方案。我的列表大约有1400万个元素。我尝试了并行解决方案,其中我使用了clusterMap而不是mapply,但我仍希望有更好的性能表现。

通常情况下,评估一个“闭包”比原始值要昂贵得多,并且很明显,这只在重复评估时才相关,因此将my_fun替换为"["应该会获得稍微更快的速度。 - alexis_laz
2个回答

3

我们可以将列表 取消列出,根据 'my_list' 的长度创建索引并提取向量

v1 <- unlist(my_list)
p1 <- list_positions
v1[cumsum(lengths(my_list))- (lengths(my_list)-p1)]
#[1] 3 4 4

基准测试

set.seed(24)
lst <- lapply(1:1e6, function(i) sample(1:10, sample(2:5), replace=FALSE))
p2 <- sapply(lst, function(x) sample(length(x), 1))
system.time({
r1 <- mapply(`[`, lst, p2)
 })
#user  system elapsed 
#   1.84    0.02    1.86 

system.time( r4 <-  mapply(my_fun, lst, p2) )
#   user  system elapsed 
#   1.88    0.01    1.89 
system.time({ r4 <-  mapply(my_fun, lst, p2) }) #placing inside the {}
#   user  system elapsed 
#   2.31    0.00    2.31 


system.time({ ##cccmir's function
  r3 <- mapply(my_func1, lst, p2)
})
#   user  system elapsed 
#  12.10    0.03   12.13 


system.time({
v2 <- unlist(lst)
r2 <- v2[cumsum(lengths(lst))- (lengths(lst)-p2)]
})
#  user  system elapsed 
#   0.14    0.00    0.14 
identical(r1, r2)
#[1] TRUE

我猜 system.time(mapply(my_fun, lst, p2)) 也很有趣,因为 OP 使用的是“闭包”,而不是 "[" -- 在我的机器上使用 my_fun 比使用 "[" 慢了约1.8倍。顺便说一下,cccMir的不需要 mapplymy_func1(lst, p2) 就可以了。 - alexis_laz
@alexis_laz 我已经更新了,但在我的系统上只显示了最小的增加。 - akrun
@alexis_laz 让我加上 {} 然后检查是否增加。 - akrun
有趣。使用system.time进行多次运行是否显示出相似的性能?记录一下,我得到了"r4"的2.3秒和"r1"的1.3秒的一致结果。 - alexis_laz
1
@alexis_laz 我已经尝试了几次,结果在2和2.3之间波动,可能为2.13等。也许使用‘microbenchmark’会更好。 - akrun

2
在这种情况下,你应该使用for循环,例如:
 library(microbenchmark)
    list_positions<-c(2,3,4)
    my_list<-list(c(1,3,4),c(2,3,4,5,6),c(1,2,3,4,6))

    my_fun<-function(x,y){
        x[y]
    }

    mapply(my_fun,x=my_list,y=list_positions)

    my_func1 <- function(aList, positions){
        res <- numeric(length(aList))

        for(i in seq_along(aList)) {
            res[i] <- aList[[i]][positions[i]]
        }
        return(res)
    }


my_func2 <- function(aList, positions) {
    v1 <- unlist(aList)
    p1 <- positions
    v1[cumsum(lengths(my_list))- (lengths(my_list)-p1)]
}

microbenchmark(mapply(my_fun,x=my_list,y=list_positions), my_func1(my_list, list_positions), my_func2(my_list, list_positions), times = 1000)

#Unit: microseconds
#                                           expr    min     lq      mean median     uq     max neval
#mapply(my_fun, x = my_list, y = list_positions) 12.764 13.858 17.453172 14.588 16.775 119.613  1000
#               my_func1(my_list, list_positions)  5.106  5.835  7.328412  6.200  6.929  38.292  1000
#               my_func2(my_list, list_positions)  2.553  3.282  4.337367  3.283  3.648  52.514  1000

@akrun的解决方案是最快的。


我看到结果了,我只是想知道为什么mapply比循环慢! - Vitalijs
我相信通过(i)使用compiler::cmpfunmy_func1进行包装,以及(ii)分配resvector(typeof(aList[[1]]), length(aList))以避免类型不匹配时的强制转换,可以显著提高其速度。 - alexis_laz
抱歉,我没明白,我不确定为什么。 - cccmir
@VitalijsJascisens:对于较大的数据集,mapply应该与“for循环”相当。我猜在这个小数据集中,mapply被其开销所击败。 - alexis_laz
@VitalijsJascisens,你应该使用microbenchmark来测试大数据集上方法之间的性能。 - cccmir
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接