2013-06-21 14 views
4

說我有比其他兩個列表一個更長的時間,x = [1,2,3,4,5,6,7,8]y = [a,b,c],我想每一個元素y中合併到每3個指標在X這樣的結果列表ž看起來像:z = [1,2,a,3,4,b,5,6,c,7,8]合併2所列出每個x位置

什麼是在python中進行此操作的最佳方式?

回答

5

下面是從itertools documentation的輪轉配方的改編版本,應該做你想要什麼:

from itertools import cycle, islice 

def merge(a, b, pos): 
    "merge('ABCDEF', [1,2,3], 3) --> A B 1 C D 2 E F 3" 
    iterables = [iter(a)]*(pos-1) + [iter(b)] 
    pending = len(iterables) 
    nexts = cycle(iter(it).next for it in iterables) 
    while pending: 
     try: 
      for next in nexts: 
       yield next() 
     except StopIteration: 
      pending -= 1 
      nexts = cycle(islice(nexts, pending)) 

例子:

>>> list(merge(xrange(1, 9), 'abc', 3)) # note that this works for any iterable! 
[1, 2, 'a', 3, 4, 'b', 5, 6, 'c', 7, 8] 

或者這裏是你如何可以使用roundrobin()因爲它沒有任何修改:

>>> x = [1,2,3,4,5,6,7,8] 
>>> y = ['a','b','c'] 
>>> list(roundrobin(*([iter(x)]*2 + [y]))) 
[1, 2, 'a', 3, 4, 'b', 5, 6, 'c', 7, 8] 

或等效的,但是稍微更可讀的版本:

>>> xiter = iter(x) 
>>> list(roundrobin(xiter, xiter, y)) 
[1, 2, 'a', 3, 4, 'b', 5, 6, 'c', 7, 8] 

注意,這兩種方法都具有可迭代的,不僅僅是序列工作。

原來這裏是roundrobin()實現:

from itertools import cycle, islice 

def roundrobin(*iterables): 
    "roundrobin('ABC', 'D', 'EF') --> A D E B F C" 
    # Recipe credited to George Sakkis 
    pending = len(iterables) 
    nexts = cycle(iter(it).next for it in iterables) 
    while pending: 
     try: 
      for next in nexts: 
       yield next() 
     except StopIteration: 
      pending -= 1 
      nexts = cycle(islice(nexts, pending)) 
3
>>> from itertools import chain 
def solve(x,y):                
    it = iter(y) 
    for i in xrange(0, len(x), 2): 
     try: 
      yield x[i:i+2] + [next(it)] 
     except StopIteration:  
      yield x[i:] 
... 

>>> x = [1,2,3,4,5,6,7,8] 
>>> y = ['a','b','c'] 

>>> list(chain.from_iterable(solve(x,y))) 
[1, 2, 'a', 3, 4, 'b', 5, 6, 'c', 7, 8] 
3

這種方法就地修改x。或者,您可以製作x的副本,如果您不想更改原件,請返回修改後的副本。過去的x年底y

def merge(x, y, offset): 
    for i, element in enumerate(y, 1): 
     x.insert(i * offset - 1, element) 

>>> x = [1,2,3,4,5,6,7,8] 
>>> y = ['a','b','c'] 
>>> merge(x, y, 3) 
>>> x 
[1, 2, 'a', 3, 4, 'b', 5, 6, 'c', 7, 8] 

所有多餘的元素只是獲得追加到末尾。

+0

我想,這是簡單明瞭,我可以理解!我無法評論任何解決方案的表現,因爲這超出了我目前的理解。 – KingFu

+1

[插入比擴展或追加更昂貴](http://stackoverflow.com/questions/7776938/python-insert-vs-append)。 – 2rs2ts

+0

@ 2rs2ts那麼複雜的事情呢!嗯,我可能會等待,讓更多的經驗豐富的人選擇最好的(最快?)解決方案,然後我選擇一個答案,然後 – KingFu

0

上述解決方案非常酷。這是一個不涉及roundrobin或itertools的替代方案。

def merge(x, y): 
    result = [] 
    while y: 
     for i in range(0, 2): result.append(x.pop(0)) 
     for i in range(0, 1): result.append(y.pop(0)) 
    result.extend(x) 
    return result 

其中2和1是任意的,並且列表y被假定爲比列表x短。

+0

這與我自己的實現類似,除了我使用了一個循環和一個條件。儘管我懷疑你的表現可能會更好 – KingFu

0
sep, lst = 2, [] 
for i in range(len(y)+1): 
    lst += x[i*sep:(i+1)*sep] + y[i:i+1] 

sepy一個元件之前的x元件的數量被插入。

性能:

>>> timeit.timeit(stmt="for i in range(len(y)+1): lst += x[i*sep:(i+1)*sep] + y[i:i+1]", setup="lst = [];x = [1,2,3,4,5,6,7,8];y = ['a','b','c'];sep = 2", number=1000000) 
2.8565280437469482 

相當好。我無法獲得stmtlet = []開頭,所以我認爲它一直附加到lst(除非我誤解了timeit),但仍然......非常好的一百萬次。

0

使用itertools.izip_longest

>>> from itertools import izip_longest, chain 
>>> y = ['a','b','c'] 
>>> x = [1,2,3,4,5,6,7,8] 
>>> lis = (x[i:i+2] for i in xrange(0, len(x) ,2)) # generator expression 
>>> list(chain.from_iterable([ (a + [b]) if b else a 
              for a, b in izip_longest(lis, y)])) 
[1, 2, 'a', 3, 4, 'b', 5, 6, 'c', 7, 8] 
0
def merge(xs, ys): 
    ys = iter(ys) 
    for i, x in enumerate(xs, 1): 
     yield x 
     if i % 2 == 0: 
      yield next(ys) 

''.join(merge('12345678', 'abc')) # => '12a34b56c78' 
3

這裏的另一種方式:

x = range(1, 9) 
y = list('abc') 

from itertools import count, izip 
from operator import itemgetter 
from heapq import merge 

print map(itemgetter(1), merge(enumerate(x), izip(count(1, 2), y))) 
# [1, 2, 'a', 3, 4, 'b', 5, 6, 'c', 7, 8] 

這使這一切懶惰建設新名單前,並讓merge自然合併序列...那種裝飾/未裝飾......它確實需要Python 2.7 for count才能擁有step參數。

所以,走它通過一個位:

a = list(enumerate(x)) 
# [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 8)] 
b = zip(count(1, 2), y) 
# [(1, 'a'), (3, 'b'), (5, 'c')] 
print list(merge(a, b)) 
# [(0, 1), (1, 2), (1, 'a'), (2, 3), (3, 4), (3, 'b'), (4, 5), (5, 6), (5, 'c'), (6, 7), (7, 8)] 

然後itemgetter(1)只需要實際值刪除索引...