Hợp nhất hai danh sách trong R


Câu trả lời:


114

Nếu các danh sách luôn có cấu trúc giống nhau, như trong ví dụ, thì một giải pháp đơn giản hơn là

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

31
Điều này tương đương với Map(c, first, second), nếu có ai quan tâm.
Masterfool

2
Tôi chỉ đang học R, tại sao Map (và mapply) lại có 'c' là tham số đầu tiên? Không phải các tham số được truyền vào chỉ đơn giản là hai danh sách?
user391339

3
'c' là tên của một hàm nguyên thủy tạo danh sách. Nhập c vào R mà không có dấu tích ở cuối sẽ hiển thị 'function (..., recursive = FALSE) .Primitive ("c")' Vì vậy, câu sáo ngữ này là ánh xạ hàm 'c' trên nội dung của thứ nhất và thứ hai.
Chris Warth

2
@Masterfool mapply () là một đánh dấu hiệu quả hơn, vì Map()có chứamapply()
Tiện nghi Eagle

cách nghiêm túc sao chúng ta cần phải lo lắng về những điều sau mapply cảnh báo: 'Lập luận còn không phải là một bội số của chiều dài ngắn hơn'
3pitt

24

Đây là một sự chuyển thể rất đơn giản của hàm modList của Sarkar. Bởi vì nó là đệ quy, nó sẽ xử lý các tình huống phức tạp hơn mapplysẽ xử lý các tình huống tên không khớp bằng cách bỏ qua các mục trong 'thứ hai' không có trong 'thứ nhất'.

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

Đây là hai tùy chọn, tùy chọn đầu tiên:

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

và thứ hai, chỉ hoạt động nếu chúng có cùng cấu trúc:

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

Cả hai đều cho kết quả mong muốn.


Tôi không nghĩ cái thứ hai của bạn hoạt động chính xác vì tôi nhận được thiết kế ma trận thay vì danh sách các vectơ.
Tyler Rinker

Bạn đúng rồi; applyđơn giản hóa nó nếu nó có thể. Nó hoạt động nếu nó không thể đơn giản hóa, chẳng hạn như nếu first$c <- c(4,5)chẳng hạn.
Aaron rời khỏi Stack Overflow

cái đầu tiên cung cấp cho tôi một danh sách dài = 0. tên được định nghĩa là một cái gì đó?
3pitt

danh sách của bạn có tên không?
Aaron rời khỏi Stack Overflow.

4

Đây là một số mã mà tôi đã viết xong, dựa trên câu trả lời của @ Andrei nhưng không có tính đơn giản / cao cấp. Ưu điểm là nó cho phép hợp nhất đệ quy phức tạp hơn và cũng có sự khác biệt giữa các phần tử cần được kết nối rbindvà những phần tử chỉ được kết nối với 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)
}

Đây là ví dụ của tôi:

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)

Kết quả này trong:

$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

Vâng, tôi biết - có lẽ không phải là hợp nhất hợp lý nhất nhưng tôi có một vòng lặp song song phức tạp mà tôi phải tạo ra một .combinehàm tùy chỉnh hơn , và do đó tôi đã viết con quái vật này :-)


1

Nói chung, người ta có thể,

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

Lưu ý rằng by()giải pháp trả về một attributedanh sách d, vì vậy nó sẽ in khác, nhưng vẫn sẽ là một danh sách. Nhưng bạn có thể loại bỏ các thuộc tính với attr(x,"_attribute.name_")<-NULL. Bạn cũng có thể sử dụng aggregate().


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

Bằng cách sử dụng purrr. Đồng thời giải quyết vấn đề danh sách của bạn không theo thứ tự.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.