按元素名称合并列表

41

我有两个列表,它们的元素名称部分重叠,我需要将它们逐个元素合并/组合成一个单独的列表:

> lst1 <- list(integers=c(1:7), letters=letters[1:5],
                words=c("two", "strings"))
> lst2 <- list(letters=letters[1:10], booleans=c(TRUE, TRUE, FALSE, TRUE),
                words=c("another", "two"), floats=c(1.2, 2.4, 3.8, 5.6))

> lst1
$integers
[1] 1 2 3 4 5 6 7

$letters
[1] "a" "b" "c" "d" "e"

$words
[1] "two"     "strings"

> lst2
$letters
 [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j"

$booleans
[1]  TRUE  TRUE FALSE  TRUE

$words
[1] "another" "two"    

$floats
[1] 1.2 2.4 3.8 5.6

我尝试使用mapply,它基本上通过索引将这两个列表组合在一起(即:"[["),而我需要按名称将它们组合起来(即:"$")。此外,由于这些列表的长度不同,因此应用了循环利用规则(结果相当难以预测)。

> mapply(c, lst1, lst2)
$integers
 [1] "1" "2" "3" "4" "5" "6" "7" "a" "b" "c" "d" "e" "f" "g" "h" "i" "j"

$letters
[1] "a"     "b"     "c"     "d"     "e"     "TRUE"  "TRUE"  "FALSE" "TRUE" 

$words
[1] "two"     "strings" "another" "two"    

$<NA>
 [1] 1.0 2.0 3.0 4.0 5.0 6.0 7.0 1.2 2.4 3.8 5.6

Warning message:
In mapply(c, lst1, lst2) :
  longer argument not a multiple of length of shorter

正如你所想象的那样,我正在寻找的是:

$integers
[1] 1 2 3 4 5 6 7

$letters
[1] "a" "b" "c" "d" "e" "a" "b" "c" "d" "e" "f" "g" "h" "i" "j"

$words
[1] "two"     "strings"   "another" "two"

$booleans
[1]  TRUE  TRUE FALSE  TRUE

$floats
[1] 1.2 2.4 3.8 5.6

有没有办法实现这个呢? 谢谢!

4个回答

49

你可以做:

keys <- unique(c(names(lst1), names(lst2)))
setNames(mapply(c, lst1[keys], lst2[keys]), keys)

对任意数量的列表进行泛化需要结合do.calllapply

l <- list(lst1, lst2, lst1)
keys <- unique(unlist(lapply(l, names)))
setNames(do.call(mapply, c(FUN=c, lapply(l, `[`, keys))), keys)

2

我将提供自己的解决方案,该方案基于tapply函数。

lst1 <- list(integers=c(1:7), letters=letters[1:5],
               words=c("two", "strings"))
lst2 <- list(letters=letters[1:10], booleans=c(TRUE, TRUE, FALSE, TRUE),
               words=c("another", "two"), floats=c(1.2, 2.4, 3.8, 5.6))

binded <- c(lst1, lst2) # and for list of lists Reduce("c", list(lst1, lst2))

tapply(binded, names(binded), function(x) unlist(x, FALSE, FALSE)) # double false for better performance

2

以下是针对tidyverse用户的对flodel的回答进行更新后的最初的回答:

list1 <- list(integers=c(1:7), letters=letters[1:5],
               words=c("two", "strings"))
list2 <- list(letters=letters[1:10], booleans=c(TRUE, TRUE, FALSE, TRUE),
               words=c("another", "two"), floats=c(1.2, 2.4, 3.8, 5.6))

input_list <- list(list1, list2, list1, list2)

我们希望对输出列表中的每个元素精确复制两次。使用map2reduce,我们可以比基本的R解决方案更清晰地实现这一点,该解决方案涉及do.callmapplylapply。首先,我们声明一个函数,使用c()按其命名元素组合两个列表,然后通过reduce在输入列表上调用我们的函数:
library(purrr)

cat_lists <- function(list1, list2) {  

  keys <- unique(c(names(list1), names(list2)))
  map2(list1[keys], list2[keys], c) %>% 
    set_names(keys)  

}

combined_output <- reduce(input_list, cat_lists)

这让我们得到了想要的结果:

最初的回答:

> combined_output

#> $integers
#>  [1] 1 2 3 4 5 6 7 1 2 3 4 5 6 7
#> 
#> $letters
#>  [1] "a" "b" "c" "d" "e" "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "a" "b"
#> [18] "c" "d" "e" "a" "b" "c" "d" "e" "f" "g" "h" "i" "j"
#> 
#> $words
#> [1] "two"     "strings" "another" "two"     "two"     "strings" "another"
#> [8] "two"    
#> 
#> $booleans
#> [1]  TRUE  TRUE FALSE  TRUE  TRUE  TRUE FALSE  TRUE
#> 
#> $floats
#> [1] 1.2 2.4 3.8 5.6 1.2 2.4 3.8 5.6

0

我也使用 grep,不知道它是更好、更差还是相等的!

l_tmp <- c(lst1, lst2, lst1)
keys = unique(names(l_tmp))
l = sapply(keys, function(name) {unlist(l_tmp[grep(name, names(l_tmp))])})

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