Об’єднайте два списки в R


75

У мене є два списки

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

Чи існує проста функція для цього?



Відповіді:


114

Якщо списки завжди мають однакову структуру, як у прикладі, тоді простіше рішення

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

31
Це рівнозначно Map(c, first, second), якщо когось це цікавить.
Masterfool

2
Я тільки вивчаю R, чому Map (і mapply) має першим параметром 'c'? Чи не повинні передані параметри бути просто двома списками?
user391339

3
'c' - це назва примітивної функції, яка створює списки. Введення c у R без кінцевих парен показує функцію '(..., рекурсивно = FALSE) .Примітив ("c")' Отже, цей кліше відображає функцію 'c' над вмістом першого та другого.
Кріс Уорт

2
@Masterfool mapply () є галочкою більш ефективною, оскільки Map()міститьmapply()
Comfort Eagle

наскільки серйозно нам потрібно турбуватися про наступне попередження: "довший аргумент не кратний довжині коротшого"
3pitt

24

Це дуже проста адаптація функції modifyList Саркаром. Оскільки він є рекурсивним, він буде обробляти складніші ситуації, ніж це mapplyбуло б, і оброблятиме ситуації з невідповідними іменами, ігноруючи елементи у "другому", які не в "першому".

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

12

Ось два варіанти, перший:

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)))

Обидва дають бажаний результат.


Я не думаю, що ваш другий працює правильно, оскільки я отримую дизайн матриці замість списку векторів.
Тайлер Рінкер,

Ти правий; applyспрощує це, якщо може. Це працює, якщо не може спростити, наприклад, якби first$c <- c(4,5), наприклад.
Аарон залишив переповнення стеку

перший дає мені список довжини = 0. чи імена слід визначати як щось?
3pitt

у ваших списках є імена?
Аарон залишив переповнення стека

4

Ось деякий код, який я написав, на основі відповіді @ Андрія, але без елегантності / простоти. Перевага полягає в тому, що воно дозволяє більш складне рекурсивне злиття, а також відрізняється між елементами, з якими слід пов’язати, 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функцію, і тому я написав цього монстра :-)


1

Загалом можна,

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

Зверніть увагу, що by()рішення повертає список attributed, тому друкуватиметься інакше, але все одно буде списком. Але ви можете позбутися атрибутів за допомогою attr(x,"_attribute.name_")<-NULL. Ви, можливо, також можете використовувати aggregate().


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

Використовуючи purrr. Також вирішує проблему, що ваші списки не в порядку.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.