75 votes

Fusionner deux listes en R

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 ?

116voto

Andrei Points 523

Si les listes ont toujours la même structure, comme dans l'exemple, alors une solution plus simple est

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

24voto

BondedDust Points 105234

Ceci est une adaptation très simple de la fonction modifyList par Sarkar. Comme elle est récursive, elle gèrera des situations plus complexes que mapply le ferait, et elle gérera les situations de noms incompatibles en ignorant les éléments de 'second' qui ne sont pas dans '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

12voto

Aaron Points 15093

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 la deuxième, qui fonctionne seulement si elles ont la même structure :

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

Les deux donnent le résultat souhaité.

4voto

Max Gordon Points 1139

Voici du code que j'ai finalement écrit, basé sur la réponse de @Andrei mais sans l'élégance/simplicité. L'avantage est qu'il permet une fusion récursive plus complexe et différencie également entre les éléments qui doivent être connectés avec rbind et ceux qui sont simplement connectés avec c:

# J'ai décidé de déplacer ceci en dehors de la mapply, je ne suis pas sûr que ce soit 
# très important pour la vitesse mais j'imagine que redéfinir la fonction 
# pourrait prendre du temps
mergeLists_internal <- function(o_element, n_element){
  if (is.list(n_element)){
    # Remplir les éléments inexistants avec des éléments NA
    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]]))
          }
        }
      }
    }  

    # Fusionner maintenant les deux listes
    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("Votre longueur ne correspond pas sur les éléments,",
           " le nouvel élément (", new_cols , ") !=",
           " l'ancien élément (", 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 donne :

$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

Oui, je sais - peut-être pas la fusion la plus logique mais j'ai une boucle parallèle complexe pour laquelle j'ai dû générer une fonction .combine plus personnalisée, donc j'ai écrit ce monstre :-)

2voto

Theo Points 11
fusionné = map(noms(premier), ~c(premier[[.x]], deuxième[[.x]])
fusionné = set_names(fusionné, noms(premier))

En utilisant purrr. Résout également le problème de vos listes qui ne sont pas dans l'ordre.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X