类似笛卡尔积的列表操作

3

我有以下列表:

lst = list(
    cat = c("room","shower","garden"),
    dog = c("street", "garden")
)

我希望能够获得输出:

list(
    list(
        animal="cat",
        place ="room"
    ),
    list(
        animal="cat",
        place ="shower"
    ),
    list(
        animal="cat",
        place ="garden"
    ),
    list(
        animal="dog",
        place ="street"
    ),
    list(
        animal="dog",
        place ="garden"
    )
)

目前,我正在使用以下代码:

library(plyr)

grasp <- function(animal, places)
{
    llply(places, function(u) list(animal=animal, place=u))
}

Reduce(append, Map(grasp, names(lst), lst))

但也许有更加优雅/简洁/新颖的选择吗?


1
我认为你目前的方法相当不错。 - Rich Scriven
2个回答

2

这与expand.grid函数非常接近;然而,它返回一个数据框(您可能考虑改用它)。但是,您可以使用以下代码将其转换为数据框的列表:

# library(magrittr)
do.call(`expand.grid`, c(lst,stringsAsFactors = FALSE)) %>% split(., 1:nrow(.))

这应该像你想要的那样运作。


1
我不知道这种方法是否更优雅或更简洁,我猜它并不是最新的,但仍然可以是获得结果的另一种方式:
unlist(lapply(names(lst),function(x){
                             lapply(lst[[x]],function(y,x){
                                               list(animal=x,place=y)
                                                },x=x)
                              }),recursive=F)

我对我的解决方案和你的plyr方法进行了基准测试,针对每个“动物”有1000个“地点”,每个“动物”有50个“地点”(我尝试过更多,但在我的电脑上太慢了...),以下是结果(我没有对magrittr方法进行基准测试,因为我在“虚拟”列表中遇到了错误):

base_meth<-function(){unlist(lapply(names(lst),function(x){lapply(lst[[x]],function(y,x){list(animal=x,place=y)},x=x)}),recursive=F)}

plyr_meth<-function(){Reduce(append, Map(grasp, names(lst), lst))}

microbenchmark(base_meth(),plyr_meth(),unit="relative",times=500)

 # Unit: relative
 #        expr      min       lq     mean   median       uq      max neval cld
 # base_meth() 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000   500  a 
 # plyr_meth() 6.885256 6.844418 5.798948 6.527788 5.475684 7.589215   500   b

有趣!我的方法更加函数式,但要慢得多!我猜区别在于我使用 Map 和 Reduce,而你只用了一个 lapply 循环和 unlist(我们使用相同的内部循环!)点赞! - Colonel Beauvel
@ColonelBeauvel,谢谢:-)。我必须说,基准测试结果有点让我惊讶;-),但你一定是对的! - Cath

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