У меня есть два длинных списка A и B, которые имеют одинаковую длину, но содержат разное количество эквивалентных элементов:
Список A может содержать множество элементов, которые также могут повторяться в одном и том же поле.
Список B содержит либо только один элемент, либо пустое поле, то есть «символ (0)».
A также содержит несколько пустых полей, но для этих записей всегда присутствует элемент в B, поэтому нет записей с пустыми полями в A и B.
Я хочу объединить элементы A и B в новый список той же длины, C, в соответствии со следующими правилами:

  • Все элементы из A должны присутствовать в C, включая их потенциальные повторения в том же поле.
  • Если B содержит элемент, которого еще нет в A той же записи, он также будет добавлен в C.
  • Но если B содержит элемент, который уже присутствует в A той же записи, он будет проигнорирован.
  • Если у A есть пустое поле, элемент из B для этой записи будет добавлен в C.
  • Если B имеет пустое поле, элементы из A для этой записи будут добавлены в C.

Это пример того, как начинаются эти списки:

> A  
 [1] "JAMES" "JAMES"  
 [2] "JOHN" "ROBERT"  
 [3] "WILLIAM" "MICHAEL" "WILLIAM" "DAVID" "WILLIAM"  
 [4] character(0)  
...  
> B  
 [1] "RICHARD"  
 [2] "JOHN"  
 [3] character(0)  
 [4] "CHARLES"  
...  

Это правильный результат, который я ищу:

> C  
 [1] "JAMES" "JAMES" "RICHARD"  
 [2] "JOHN" "ROBERT"  
 [3] "WILLIAM" "MICHAEL" "WILLIAM" "DAVID" "WILLIAM"  
 [4] "CHARLES"  
... 

Я пробовал, например:

C <- sapply(mapply(union, A,B), setdiff, character(0))  

Но, к сожалению, это удалило повторения из A:

> C  
 [1] "JAMES" "RICHARD"  
 [2] "JOHN" "ROBERT"  
 [3] "WILLIAM" "MICHAEL" "DAVID"  
 [4] "CHARLES"  
...  

Кто-нибудь может сказать мне, пожалуйста, как объединить эти два списка, сохранить повторения из A и добиться желаемого результата?

Заранее большое спасибо!

Обновление. Машиночитаемые данные:

A <- list(c("JAMES","JAMES"),
          c("JOHN","ROBERT"), 
          c("WILLIAM","MICHAEL","WILLIAM","DAVID","WILLIAM"),  
          character(0))
B <- list("RICHARD","JOHN",character(0),"CHARLES")
4
user0815 14 Июн 2012 в 14:04
1
Могли бы вы предоставить данные таким образом, чтобы их могли прочитать другие люди? Это поможет им запустить пример, и у них будет больше времени, чтобы найти для вас хорошее решение. Посмотрите, как это можно сделать: SO . В частности, взгляните на dput.
 – 
Christoph_J
14 Июн 2012 в 14:15
Большое спасибо за ваш комментарий и совет! В следующий раз я приму это как можно лучше.
 – 
user0815
14 Июн 2012 в 16:19

1 ответ

Лучший ответ

Вот ваш фрагмент данных в воспроизводимой форме:

A <- list(c("JAMES","JAMES"),
          c("JOHN","ROBERT"), 
          c("WILLIAM","MICHAEL","WILLIAM","DAVID","WILLIAM"),  
          character(0))
B <- list("RICHARD","JOHN",character(0),"CHARLES")

Вы были близки с mapply(). Я получил желаемый результат, используя c() для объединения элементов списка в A и B, но мне пришлось манипулировать элементами предоставленных векторов, поэтому я придумал следующее:

foo <- function(...) {
    l1 <- length(..1)
    l2 <- length(..2)
    out <- character(0)
    if(l1 > 0) {
        if(l2 > 0) {
            out <- if(..2 %in% ..1)
                ..1
            else
                c(..1, ..2)
        } else {
            out <-  ..1
        }
    } else {
        out <-  ..2
    }
    out
}

Мы можем ссылаться на отдельные элементы ..., используя заполнители ..n; ..1 равно A и ..2 равно B. Конечно, foo() работает только с двумя списками, но не требует этого и не выполняет никаких проверок, просто для простоты. foo() также должен обрабатывать случаи, когда A или B или оба являются character(0), что, как я теперь думаю, foo() делает.

Когда мы используем это в вызове mapply(), я получаю:

> mapply(foo, A, B)
[[1]]
[1] "JAMES"   "JAMES"   "RICHARD"

[[2]]
[1] "JOHN"   "ROBERT"

[[3]]
[1] "WILLIAM" "MICHAEL" "WILLIAM" "DAVID"   "WILLIAM"

[[4]]
[1] "CHARLES"

Версия lapply() может быть более содержательной, чем абстрактная ..n, но использует, по сути, тот же код. Вот новая функция, которая работает с A и B напрямую, но мы перебираем индексы элементов A (1, 2, 3, length(A)), сгенерированные seq_along() :

foo2 <- function(ind, A, B) {
    l1 <- length(A[[ind]])
    l2 <- length(B[[ind]])
    out <- character(0)
    if(l1 > 0) {
        if(l2 > 0) {
            out <- if(B[[ind]] %in% A[[ind]]) {
                A[[ind]]
            } else {
                c(A[[ind]], B[[ind]])
            }
        } else {
            out <- A[[ind]]
        }
    } else {
        out <- B[[ind]]
    }
    out
}

Который называется так:

> lapply(seq_along(A), foo2, A = A, B = B)
[[1]]
[1] "JAMES"   "JAMES"   "RICHARD"

[[2]]
[1] "JOHN"   "ROBERT"

[[3]]
[1] "WILLIAM" "MICHAEL" "WILLIAM" "DAVID"   "WILLIAM"

[[4]]
[1] "CHARLES"
7
Gavin Simpson 14 Июн 2012 в 14:45
Большое спасибо за ваше решение, которое отлично работает! В дополнение к этому спасибо за ваше полезное редактирование / обновление моего вопроса.
 – 
user0815
14 Июн 2012 в 16:18