2013-05-08 2 views
25

Допустим, что у меня есть два списка в R, не обязательно одинаковой длины, как:Interleave списки в R

a <- list('a.1','a.2', 'a.3') 
b <- list('b.1','b.2', 'b.3', 'b.4') 

Что такое лучший способ построить список вкладных элементов, где, когда элемент тем короче список был добавлен, остальные элементы длинного списка будут добавлять в конце ?, как:

interleaved <- list('a.1','b.1','a.2', 'b.2', 'a.3', 'b.3','b.4') 

без использования цикла. Я знаю, что mapply работает для случая, когда оба списка имеют равную длину.

ответ

30

Вот один из способов:

idx <- order(c(seq_along(a), seq_along(b))) 
unlist(c(a,b))[idx] 

# [1] "a.1" "b.1" "a.2" "b.2" "a.3" "b.3" "b.4" 

Как @James указывает, так как вам нужен список обратно, вы должны сделать:

(c(a,b))[idx] 
+0

Я думаю, что с редактированием единственное требование теперь состоит в том, что более короткий список должен быть a, а более длинный список должен быть b, но это достаточно легко проверить. – joran

+0

Я думаю, что я просто интерпретировал высказывания OP более строго. «Остальные элементы более длинного списка добавляются в конце» только для меня действительно имеет смысл, если вы начинаете чередование с первым элементом более короткого списка и так далее. – joran

+0

Должен ли вывод не быть списком? – James

5

Вот один из вариантов, используя функцию interleave от ggplot2. Я уверен, что это может быть улучшен, но это начало:

require(ggplot2) 
Interleave <- function(x,y){ 
    v <- list(x,y) 
    lengths <- sapply(v,length) 
    mn <- min(lengths) 
    v <- v[order(lengths)] 
    c(ggplot2:::interleave(v[[1]],v[[2]][seq_len(mn)]),v[[2]][(mn+1):length(v[[2]])]) 
} 

Interleave(a,b) 
Interleave(b,a) 

В частности, это будет делать странные вещи, если списки фактически одинаковую длину. Возможно, кто-то перезвонит с лучшим способом сделать индексацию для v[[2]] в последней строке, которая позволит избежать этого вырожденного случая.

5
interleave(a, b) 

# unlist(interleave(a, b)) 
# [1] "a.1" "b.1" "a.2" "b.2" "a.3" "b.3" "b.4" 


interleave <- function(a, b) { 

    shorter <- if (length(a) < length(b)) a else b 
    longer <- if (length(a) >= length(b)) a else b 

    slen <- length(shorter) 
    llen <- length(longer) 


    index.short <- (1:slen) + llen 
    names(index.short) <- (1:slen) 

    lindex <- (1:llen) + slen 
    names(lindex) <- 1:llen 


    sindex <- 1:slen 
    names(sindex) <- 1:slen 

    index <- c(sindex, lindex) 
    index <- index[order(names(index))] 

    return(c(a, b)[index]) 

} 
21

Исследуя подобный вопрос, я пришел через this красивое решение от Gabor Grothendieck (то есть @GGrothendieck?) для определенных случаев:

c(rbind(a,b)) 

Это работает одинаково хорошо, если a и b оба эти списка, либо если a и b оба вектора. Это не точное решение вопроса OP, потому что когда a и b имеют разную длину, он будет перерабатывать элементы более короткой последовательности, печатая предупреждение. Однако, поскольку это решение является простым и изящным, и дает ответ на очень похожий вопрос - вопрос некоторых людей (таких как я), которые в итоге попадают на эту страницу - казалось, стоит добавить в качестве ответа.

+0

Я искал формирование XML из data.frame как простой строки без особых зависимостей. Это наиболее краткий подход для открытия/закрытия тегов при обработке строки (с применением)! Я завернул rbind (без c) в paste & collapse = "". – mlt

+0

По некоторым причинам это не работает с датами. Он получается как список целых чисел. – wpkzz

Смежные вопросы