2012-03-01 37 views
66

tengo dos listasCombinar dos listas en I

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

quiero fusionar estas dos listas por lo que el producto final es

$a 
[1] 1 2 

$b 
[1] 2 3 

$c 
[1] 3 4 

¿Hay una función simple de hacer esto?

+0

Comprobar: http://stackoverflow.com/questions/7349608/merge-contents-within-list-of-list-by-duplicate-name – Hansi

Respuesta

94

Si las listas siempre tienen la misma estructura, como en el ejemplo, entonces una solución más simple es

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

Esto es equivalente a 'Mapa (c, primero, segundo)', si a alguien le importa. – Masterfool

+2

Estoy aprendiendo R, ¿por qué Map (y mapply) tiene 'c' como primer parámetro? ¿No deberían los parámetros pasados ​​simplemente ser las dos listas? – user391339

+2

'c' es el nombre de una función primitiva que crea listas. Escribir c en R sin los paréntesis que salen muestra la función (..., recursivo = FALSO) .Primitivo ("c") 'Así que este cliché está mapeando la función' c 'sobre los contenidos de primero y segundo. –

12

Aquí hay dos opciones, la primera:

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

y la segunda, que funciona sólo si tienen la misma estructura:

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

Ambos dan el resultado deseado.

+0

No creo que el segundo uno funciona correctamente ya que obtengo un diseño de matriz en lugar de una lista de vectores. –

+0

Tienes razón; 'apply' lo simplifica si puede. Funciona si no se puede simplificar, por ejemplo si 'first $ c <- c (4,5)'. – Aaron

+0

el primero me da una lista de longitud = 0. se supone que los nombres se definen como algo? –

23

Esta es una adaptación muy simple de la función modifyList por Sarkar. Debido a que es recursiva, manejará situaciones más complejas que mapply, y manejará situaciones de nombres que no coincidan ignorando los elementos en 'segundo' que no están en 'primero'.

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 
4

Aquí hay un código que terminé por escrito, en base a la respuesta de @ Andrei pero sin la elegancia/simplicidad. La ventaja es que permite una fusión recursiva más complejo y también difiere entre los elementos que deben ser conectados con rbind y aquellos que sólo están conectados con 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) 
} 

Aquí está mi ejemplo:

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) 

Este resultados en:

$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 

Sí, lo sé - quizás no es la fusión más lógica pero tengo un bucle paralelo complejo que tengo d para generar una función más personalizada para .combine, y por eso escribió este monstruo :-)

1

En general se podría,

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

Tenga en cuenta que la solución by() devuelve una lista de attribute d, por lo que se imprimirá de manera diferente, pero seguirá siendo una lista. Pero puede deshacerse de los atributos con attr(x,"_attribute.name_")<-NULL. Probablemente también pueda usar aggregate().

3

list3 <- append(list1, list2) también funcionará