在 R 中合并两个列表

我有两份名单

first = list(a = 1, b = 2, c = 3)
second = list(a = 2, b = 3, c = 4)

我想合并这两个列表,所以最终产品是

$a
[1] 1 2


$b
[1] 2 3


$c
[1] 3 4

有没有一个简单的函数可以做到这一点?

118310 次浏览

这里有两个选择,第一个:

both <- list(first, second)
n <- unique(unlist(lapply(both, names)))
names(n) <- n
lapply(n, function(ni) unlist(lapply(both, `[[`, ni)))

第二种,只有当它们具有相同的结构时才有效:

apply(cbind(first, second),1,function(x) unname(unlist(x)))

两者都给出了预期的结果。

如果列表总是具有相同的结构(如示例中所示) ,那么一个更简单的解决方案是

mapply(c, first, second, SIMPLIFY=FALSE)

这是 Sarkar 对 modfyList 函数的一个非常简单的改编。因为它是递归的,所以它将处理比 mapply更复杂的情况,并且它将通过忽略‘ second’中不在‘ first’中的项来处理不匹配的名称情况。

appendList <- function (x, val)
{
stopifnot(is.list(x), is.list(val))
xnames <- names(x)
for (v in names(val)) {
x[[v]] <- if (v %in% xnames && is.list(x[[v]]) && is.list(val[[v]]))
appendList(x[[v]], val[[v]])
else c(x[[v]], val[[v]])
}
x
}


> appendList(first,second)
$a
[1] 1 2


$b
[1] 2 3


$c
[1] 3 4

下面是我最终根据@Andrei 的回答编写的一些代码,但没有优雅/简单。优点是它允许更复杂的递归合并,并且应该与 rbind连接的元素和那些仅仅与 c连接的元素之间也有所不同:

# Decided to move this outside the mapply, not sure this is
# that important for speed but I imagine redefining the function
# might be somewhat time-consuming
mergeLists_internal <- function(o_element, n_element){
if (is.list(n_element)){
# Fill in non-existant element with NA elements
if (length(n_element) != length(o_element)){
n_unique <- names(n_element)[! names(n_element) %in% names(o_element)]
if (length(n_unique) > 0){
for (n in n_unique){
if (is.matrix(n_element[[n]])){
o_element[[n]] <- matrix(NA,
nrow=nrow(n_element[[n]]),
ncol=ncol(n_element[[n]]))
}else{
o_element[[n]] <- rep(NA,
times=length(n_element[[n]]))
}
}
}


o_unique <- names(o_element)[! names(o_element) %in% names(n_element)]
if (length(o_unique) > 0){
for (n in o_unique){
if (is.matrix(n_element[[n]])){
n_element[[n]] <- matrix(NA,
nrow=nrow(o_element[[n]]),
ncol=ncol(o_element[[n]]))
}else{
n_element[[n]] <- rep(NA,
times=length(o_element[[n]]))
}
}
}
}


# Now merge the two lists
return(mergeLists(o_element,
n_element))


}
if(length(n_element)>1){
new_cols <- ifelse(is.matrix(n_element), ncol(n_element), length(n_element))
old_cols <- ifelse(is.matrix(o_element), ncol(o_element), length(o_element))
if (new_cols != old_cols)
stop("Your length doesn't match on the elements,",
" new element (", new_cols , ") !=",
" old element (", old_cols , ")")
}


return(rbind(o_element,
n_element,
deparse.level=0))
return(c(o_element,
n_element))
}
mergeLists <- function(old, new){
if (is.null(old))
return (new)


m <- mapply(mergeLists_internal, old, new, SIMPLIFY=FALSE)
return(m)
}

下面是我的例子:

v1 <- list("a"=c(1,2), b="test 1", sublist=list(one=20:21, two=21:22))
v2 <- list("a"=c(3,4), b="test 2", sublist=list(one=10:11, two=11:12, three=1:2))
mergeLists(v1, v2)

结果是:

$a
[,1] [,2]
[1,]    1    2
[2,]    3    4


$b
[1] "test 1" "test 2"


$sublist
$sublist$one
[,1] [,2]
[1,]   20   21
[2,]   10   11


$sublist$two
[,1] [,2]
[1,]   21   22
[2,]   11   12


$sublist$three
[,1] [,2]
[1,]   NA   NA
[2,]    1    2

是的,我知道-也许不是最符合逻辑的合并,但我有一个复杂的并行循环,我必须生成一个更自定义的 .combine函数,因此我写了这个怪物: -)

一般来说,

merge_list <- function(...) by(v<-unlist(c(...)),names(v),base::c)

请注意,by()解决方案返回一个 attributed 列表,因此它将以不同的方式打印,但仍然是一个列表。但是您可以使用 attr(x,"_attribute.name_")<-NULL去掉这些属性。您也可以使用 aggregate()

merged = map(names(first), ~c(first[[.x]], second[[.x]])
merged = set_names(merged, names(first))

使用 purrr 还可以解决列表不按顺序排列的问题。

在@Aaron 左边的 Stack Overflow 和@Theo 应答之后,合并列表的元素采用向量 c的形式。 但是如果要绑定行和列,则使用 rbindcbind

merged = map(names(first), ~rbind(first[[.x]], second[[.x]])
merged = set_names(merged, names(first))

使用 dplyr,我发现这一行适用于使用相同名称的命名列表:

as.list(bind_rows(first, second))

我们可以使用 c()执行 lapply,并使用 setNames为输出分配原始名称。

setNames(lapply(1:length(first), function(x) c(first[[x]], second[[x]])), names(first))


$a
[1] 1 2


$b
[1] 2 3


$c
[1] 3 4