J'ai deux listes
first = list(a = 1, b = 2, c = 3)
second = list(a = 2, b = 3, c = 4)
Je veux fusionner ces deux listes pour que le produit final soit
$a
[1] 1 2
$b
[1] 2 3
$c
[1] 3 4
Y a-t-il une fonction simple pour faire cela?
Si les listes ont toujours la même structure, comme dans l'exemple, une solution plus simple est alors utilisée.
mapply(c, first, second, SIMPLIFY=FALSE)
Ceci est une adaptation très simple de la fonction modifyList de Sarkar. Comme il est récursif, il gérera des situations plus complexes que mapply
et gérera les situations de noms incompatibles en ignorant les éléments de 'seconde' qui ne sont pas en 'premier'.
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
Voici deux options, la première:
both <- list(first, second)
n <- unique(unlist(lapply(both, names)))
names(n) <- n
lapply(n, function(ni) unlist(lapply(both, `[[`, ni)))
et le second, qui ne fonctionne que s'ils ont la même structure:
apply(cbind(first, second),1,function(x) unname(unlist(x)))
Les deux donnent le résultat souhaité.
Voici un code que j'ai fini par écrire, basé sur la réponse de @ Andrei mais sans élégance ni simplicité. L'avantage est qu'il permet une fusion récursive plus complexe et diffère également entre les éléments devant être connectés à rbind
et ceux qui viennent d'être connectés à 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)
}
Voici mon exemple:
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)
Cela se traduit par:
$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
Ouais, je sais - ce n'est peut-être pas la fusion la plus logique, mais j'ai une boucle parallèle complexe pour laquelle je devais générer une fonction .combine
Plus personnalisée, et j'ai donc écrit ce monstre :-)
list3 <- append(list1, list2)
fonctionnera également
En général, on pourrait,
merge_list <- function(...) by(v<-unlist(c(...)),names(v),base::c)
Notez que la solution by()
renvoie une liste attribute
d. Elle sera donc imprimée différemment, mais restera une liste. Mais vous pouvez vous débarrasser des attributs avec attr(x,"_attribute.name_")<-NULL
. Vous pouvez probablement aussi utiliser aggregate()
.