我有两个嵌套列表,它们的结构要合并(在c()
的意义上说。)
对于这种关系,在图论或计算机科学中可能已经存在一个我所说的“相同结构”的概念,但我不知道。
因此,这是我试图阐明相同结构的意思:
所以我想知道是否已经有解决这个问题的方法,我认为这可能是相当普遍和普遍的...(?)任何涉及以下内容的解决方案:
rapply
; purrr
函数的某种组合的Tidyverse解决方案; rlist
软件包中的功能太好了!
foo
和bar
是两个具有相同结构的示例列表。
wonderful
是将foo
和bar
(手动完成)组合而成的理想列表。
我希望它足够清楚!
# 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
答案 0 :(得分:3)
这是一个尝试:
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
我还没有对它进行彻底的测试,但是轻度测试似乎可以完成工作。
答案 1 :(得分: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$c
和bar$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
答案 2 :(得分:0)
在对这个问题进行了更多思考之后……并从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