【问题标题】:How to bind two lists with same structure?如何绑定两个具有相同结构的列表?
【发布时间】:2019-07-14 07:20:03
【问题描述】:

简介

我有两个具有相同结构的嵌套列表,我想合并它们(在c() 意义上)。

在图论或计算机科学中,对于我所说的相同结构,对于这种关系,可能已经存在一个概念,但我不知道。

所以这是我试图澄清我所说的相同结构的意思:

  • 某个级别的列表元素要么全部命名,要么没有命名;
  • 当我们为元素命名时,该级别的名称永远不会重复;
  • 当节点本身是命名元素时,两个列表的父子节点关系相同。

所以我想知道这个问题是否已经有解决方案,我觉得可能相当普遍和普遍......(?)任何解决方案涉及:

  • 使用基地rapply;
  • 具有purrr 函数组合的Tidyverse 解决方案;
  • rlist 包中的函数

会很棒!

示例

foobar 是两个具有相同结构的示例列表。

wonderful 是组合foobar(手动完成)产生的所需列表。

我希望它足够清楚!

# Input lists: foo and bar
foo <- list(a = list(a1 = 1:3, a2 = rep('a', 3)), b = list(b1 = list(b11 = c(4,5,6), b12 = rep('b', 3)), b2 = list(b21 = list(b31 = c(0, 1, 2)))), c = list(list(c21 = 1:3), list(c21 = 4:6), list(c21 = 7:9)))
bar <- list(a = list(a1 = 1:3, a2 = rep('z', 3)), b = list(b1 = list(b11 = c(-1,2,5), b12 = rep('b', 3)), b2 = list(b21 = list(b31 = -c(1,2,3)))), c = list(list(c21 = 3:1), list(c21 = 5:3)))

# wonderful: desired list (result from combining foo and bar)
wonderful <- list(
  a = list(
    a1 = c(foo$a$a1, bar$a$a1), 
    a2 = c(foo$a$a2, bar$a$a2)
    ),
  b = list(
    b1 = list(
      b11 = c(foo$b$b1$b11, bar$b$b1$b11),
      b12 = c(foo$b$b1$b12, bar$b$b1$b12)
      ),
    b2 = list(
      b21 = list(
        b31 = c(foo$b$b2$b21$b31, bar$b$b2$b21$b31)
        )
      )
    ),
  c = c(foo$c, bar$c)
)

str(foo)
#> List of 3
#>  $ a:List of 2
#>   ..$ a1: int [1:3] 1 2 3
#>   ..$ a2: chr [1:3] "a" "a" "a"
#>  $ b:List of 2
#>   ..$ b1:List of 2
#>   .. ..$ b11: num [1:3] 4 5 6
#>   .. ..$ b12: chr [1:3] "b" "b" "b"
#>   ..$ b2:List of 1
#>   .. ..$ b21:List of 1
#>   .. .. ..$ b31: num [1:3] 0 1 2
#>  $ c:List of 3
#>   ..$ :List of 1
#>   .. ..$ c21: int [1:3] 1 2 3
#>   ..$ :List of 1
#>   .. ..$ c21: int [1:3] 4 5 6
#>   ..$ :List of 1
#>   .. ..$ c21: int [1:3] 7 8 9

str(bar)
#> List of 3
#>  $ a:List of 2
#>   ..$ a1: int [1:3] 1 2 3
#>   ..$ a2: chr [1:3] "z" "z" "z"
#>  $ b:List of 2
#>   ..$ b1:List of 2
#>   .. ..$ b11: num [1:3] -1 2 5
#>   .. ..$ b12: chr [1:3] "b" "b" "b"
#>   ..$ b2:List of 1
#>   .. ..$ b21:List of 1
#>   .. .. ..$ b31: num [1:3] -1 -2 -3
#>  $ c:List of 2
#>   ..$ :List of 1
#>   .. ..$ c21: int [1:3] 3 2 1
#>   ..$ :List of 1
#>   .. ..$ c21: int [1:3] 5 4 3

str(wonderful)
#> List of 3
#>  $ a:List of 2
#>   ..$ a1: int [1:6] 1 2 3 1 2 3
#>   ..$ a2: chr [1:6] "a" "a" "a" "z" ...
#>  $ b:List of 2
#>   ..$ b1:List of 2
#>   .. ..$ b11: num [1:6] 4 5 6 -1 2 5
#>   .. ..$ b12: chr [1:6] "b" "b" "b" "b" ...
#>   ..$ b2:List of 1
#>   .. ..$ b21:List of 1
#>   .. .. ..$ b31: num [1:6] 0 1 2 -1 -2 -3
#>  $ c:List of 5
#>   ..$ :List of 1
#>   .. ..$ c21: int [1:3] 1 2 3
#>   ..$ :List of 1
#>   .. ..$ c21: int [1:3] 4 5 6
#>   ..$ :List of 1
#>   .. ..$ c21: int [1:3] 7 8 9
#>   ..$ :List of 1
#>   .. ..$ c21: int [1:3] 3 2 1
#>   ..$ :List of 1
#>   .. ..$ c21: int [1:3] 5 4 3

【问题讨论】:

  • 我想到了递归函数...也许可以阅读?stack
  • @zx8754:谢谢!去看看。

标签: r list purrr


【解决方案1】:

试一试:

library(purrr)

rec_map <- function(fizz, buzz) {
  if(is.atomic(fizz) | is.null(names(fizz))){
    c(fizz, buzz)
  } else {
    imap(fizz,
         ~rec_map(fizz[[.y]], buzz[[.y]]))
  }
}

temp <- rec_map(foo, bar)

all.equal(temp, wonderful)
#> [1] TRUE

我绝不是计算机科学家,所以对解决方案持保留态度。当一层没有名称时,我不确定所需的行为,但随后一层有名称(例如,foo$c)。所以如果我们遇到一个没有名字的关卡,我只是合并了结果 (c())。

编辑以获取多个列表:

prec_map <- function(...){
  dots <- list(...)
  first_el = dots[[1]]
  if(is.atomic(first_el) | is.null(names(first_el))){
    do.call(c, dots)
  } else {
    imap(first_el,
         function(el, nme){
           one_level_down <- map(dots, nme)
           do.call(prec_map, one_level_down)
         })
  }
}

temp <- prec_map(foo, bar)

all.equal(temp, wonderful)
[1] TRUE

我还没有彻底测试过,但轻度测试看起来可以完成工作。

【讨论】:

  • @rmagno 我打算在今天晚些时候有空的时候再看看这个,抱歉!
  • prec_map可以表示为function(.l) reduce(.l, rec_map)partial(reduce, .f = rec_map). %&gt;% reduce(rec_map),用作prec_map(list(foo, bar))
  • 我喜欢@Aurèle 关于使用reduce 处理更多列表的建议。我在上面(而不是左右)采取了更多自上而下的方法。
  • @zack:imap(fizz, ~rec_map(fizz[[.y]], buzz[[.y]]), buzz) 中传递的buzz 有什么作用吗?
  • 它看起来不像,我认为它是从点参数到 imap 的嗡嗡声,但它是从传递给 rec_map 函数的参数中提取的。我现在已经编辑了。
【解决方案2】:

list_merge 做了一些接近要求的事情:

library(purrr)

res <- list_merge(foo, !!! bar)

all.equal(wonderful, list_merge(foo, !!! bar))
# [1] "Component “c”: Length mismatch: comparison on first 3 components"       
# [2] "Component “c”: Component 1: Component 1: Numeric: lengths (3, 6) differ"
# [3] "Component “c”: Component 2: Component 1: Numeric: lengths (3, 6) differ"

唯一的区别似乎是未命名列表的元素(例如foo$cbar$c),其元素按位置连接(foo$c[[1]]bar$c[[1]]foo$c[[2]]bar$c[[2]] , 和 foo$c[[3]] 单独放置,因为没有 bar$c[[3]]... 而不是 c(foo$c, bar$c))。


并行版本可能是:

plist_merge <- function(.l) {
  reduce(.l, ~ list_merge(.x, !!! .y))
}

all.equal(
  plist_merge(list(foo, bar)),
  list_merge(foo, !!! bar)
)
# [1] TRUE

【讨论】:

  • 这是一个很好的解决方案!
【解决方案3】:

在总体上对这个问题进行了更多思考之后......并从 dplyr 的联接中获得了一些启发,这里有三个联接供我自己将来参考:

  • lst_left_join
  • lst_right_join
  • lst_inner_join
library(purrr)

#
# Inspired by dplyr's joins: https://r4ds.had.co.nz/relational-data.html#inner-join
# Here's some (more or less equivalent) list joins
# 
lst_left_join <- function(lst_x, lst_y) {
  if(is.atomic(lst_x) || is.null(names(lst_x))){
    c(lst_x, lst_y)
  } else {
    imap(lst_x, ~lst_left_join(lst_x[[.y]], lst_y[[.y]]))
  }
}

plst_left_join <- function(.l) reduce(.l, lst_left_join)

lst_right_join <- function(lst_x, lst_y) {
  if(is.atomic(lst_y) || is.null(names(lst_y))){
    c(lst_x, lst_y)
  } else {
    imap(lst_y, ~lst_right_join(lst_x[[.y]], lst_y[[.y]]))
  }
}

plst_right_join <- function(.l) reduce(.l, lst_right_join)

lst_inner_join <- function(lst_x, lst_y) {
  if(is.atomic(lst_y) || is.null(names(lst_y))){
    c(lst_x, lst_y)
  } else {
    common_names <- intersect(names(lst_x), names(lst_y))
    names(common_names) <- common_names # so that map preserves names
    map(common_names, ~lst_inner_join(lst_x[[.x]], lst_y[[.x]]))
  }
}
plst_inner_join <- function(.l) reduce(.l, lst_inner_join)

# Input lists: foo and bar.
foo <- list(x1 = 1:2, x3 = 30+5:6)
bar <- list(x1 = 10+1:2, x2 = 10+3:4)

# Output lists: r1, r2 and r3.
r1 <- lst_left_join(foo, bar)
r2 <- lst_right_join(foo, bar)
r3 <- lst_inner_join(foo, bar)

str(r1)
#> List of 2
#>  $ x1: num [1:4] 1 2 11 12
#>  $ x3: num [1:2] 35 36
str(r2)
#> List of 2
#>  $ x1: num [1:4] 1 2 11 12
#>  $ x2: num [1:2] 13 14
str(r3)
#> List of 1
#>  $ x1: num [1:4] 1 2 11 12

【讨论】:

    猜你喜欢
    • 2015-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-06
    • 2017-04-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多