按元素名称组合/合并列表(列表中的列表)

10

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

我的问题与按元素名称合并/组合列表有关,但我示例中的数据结构更加复杂,因此,在上面提到的链接下提供的解决方案在这种情况下无法使用。

这里是一个简化的玩具例子:

l.1 <- list(list(c(10,20), NULL),list(c(10,20,30), NULL), list(c(9,12,13), NULL))
names(l.1) <- c("a","b","c")

l.2 <- list(list(NULL,c(1,0)),list(NULL,c(1,2,3)))
names(l.2) <- c("a","b")

因此,数据的类型是“列表中的列表”,并且看起来像这样:

# > l.1
# $a
# $a[[1]]
# [1] 10 20
# $a[[2]]
# NULL
# 
# $b
# $b[[1]]
# [1] 10 20 30
# $b[[2]]
# NULL
# 
# $c
# $c[[1]]
# [1]  9 12 13
# $c[[2]]
# NULL
# 
# > l.2
# $a
# $a[[1]]
# NULL
# $a[[2]]
# [1] 1 0
# 
# $b
# $b[[1]]
# NULL
# $b[[2]]
# [1] 1 2 3

合并这两个列表的结果应该看起来像这样:

# $a
# $a[[1]]
# [1] 10 20
# $a[[2]]
# [1] 1 0
# 
# $b
# $b[[1]]
# [1] 10 20 30
# $b[[2]]
# [1] 1 2 3
# 
# $c
# $c[[1]]
# [1]  9 12 13
# $c[[2]]
# NULL

我已经根据Combine/merge lists by elements names中给出的解决方案进行了适应,但这似乎对于这种数据结构不起作用。

以下是我尝试过的:

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

我感激任何帮助。

5个回答

6
您可以使用lapply操作键进行合并:
keys <- unique(c(names(l.1), names(l.2)))
setNames(lapply(keys, function(key) list(c(l.1[[key]][[1]], l.2[[key]][[1]]),
                                         c(l.1[[key]][[2]], l.2[[key]][[2]]))),
         keys)
# $a
# $a[[1]]
# [1] 10 20
# 
# $a[[2]]
# [1] 1 0
# 
# $b
# $b[[1]]
# [1] 10 20 30
# 
# $b[[2]]
# [1] 1 2 3
# 
# $c
# $c[[1]]
# [1]  9 12 13
# 
# $c[[2]]
# NULL

1
这应该是 keys <- unique(c(names(l.1), names(l.2))) 吗?也就是说,里面不应该有一个 c() 吗? - GSee

6
受josilber答案的启发,这里我们不会硬编码子列表的长度,而是使用lapply在结果中创建它们:
keys <- unique(c(names(l.1), names(l.2)))
setNames(lapply(keys, function(key) {
    l1 <- l.1[[key]]
    l2 <- l.2[[key]]
    len <- max(length(l1), length(l2))

    lapply(seq(len), function(i) c(l1[[i]], l2[[i]]))
  }),
  keys)

谢谢你的回答。请看我的后续问题:http://stackoverflow.com/questions/23493873/combine-merge-lists-by-elements-names-list-in-list-in-list - majom

4

这里有三行:

out <- l.1
mnames <- intersect(names(l.1), names(l.2))
out[mnames] <- Map(function(a, b) Map(c, a, b), l.1[mnames], l.2[mnames])

#$a
#$a[[1]]
#[1] 10 20
#$a[[2]]
#[1] 1 0
#
#$b
#$b[[1]]
#[1] 10 20 30
#$b[[2]]
#[1] 1 2 3
#
#$c
#$c[[1]]
#[1]  9 12 13
#$c[[2]]
#NULL

你能否想到一种代码版本,其中a、b、c不必硬编码。我想知道你的代码性能与matthew-lundberg的解决方案相比如何。我正在使用以下输入数据来测试性能:library(digest); l.1 <- rep(list(list(c(10,20), NULL),list(c(10,20,30), NULL), list(c(9,12,13), NULL)), 10000); names(l.1) <- sapply(sample(1:30000, 30000, replace=FALSE), digest); l.2 <- rep(list(list(NULL,c(1,0)),list(NULL,c(1,2,3))), 10000); names(l.2) <- names(l.1)[1:20000] - majom

1
这是一种嵌套合并函数,可以产生所需的输出。我觉得应该有更简单的方法,但我想不出来。它会优先使用第一个参数的值,但如果有匹配的名称或索引,则会与第二个参数的值合并。
nestedMerge<-function(a,b) {
    if(is.list(a) & is.list(b)) {
        out<-list()
        if(!is.null(names(a))) {
            for(n in names(a)) {
                if(n %in% names(b) && !is.null(b[[n]])) {
                    out<-append(out, list(Recall(a[[n]], b[[n]])))
                } else {
                    out<-append(out, list(a[[n]]))
                }
                names(out)[length(out)]<-n
            }
        } else {
            for(i in seq_along(a))
                if(i <=length(b) && !is.null(b[[i]])) {
                    out<-append(out, Recall(a[[i]], b[[i]]))
                } else {
                    out<-append(out, list(a[[i]]))
                }
        }
        return(out)
    } else {
        return(list(c(a,b)))
    }
}

#and now, use the function
nestedMerge(l.1,l.2)

是的,这个问题的解决方案过于复杂,允许多级递归和不同的索引方式。@thelatemail的解决方案可能更好,更易于理解。 - MrFlick
@rawr 在测试时我一定改变了函数的名称。它应该调用自己。我已经更新了代码。 - MrFlick
那就是我想的。一如既往的好解决方案。 - rawr

0

这里有一个额外的解决方案。它使用mapplyc来组合列表:

## get all possible names
l.names <- union(names(l.1), names(l.2)) 
## combine lists
r <- mapply(c, l.1[l.names], l.2[l.names]) 
## get rid of NULL entries
l.3 <- sapply(names(r), 
              function(x) r[[x]][!sapply(r[[x]], is.null)], USE.NAMES=TRUE)

我从这个SO问题的答案这个R帮助问题的答案中适应了这个答案。

第一行收集了两个列表中至少出现过一次的名称(即所有可能的名称)。第二行使用mapplyc和先前收集的名称的列表索引来合并列表,尽管存在额外的NULL条目。第三行去除这些NULL条目同时保留列表名称。

请注意,此答案确实删除了列表元素cNULL条目。


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