Pythonで、同じ長さの2つのリストをインターリーブする良い方法はありますか?
私が与えられた[1,2,3]
と言うと[10,20,30]
。それらをに変換したいと思い[1,10,2,20,3,30]
ます。
Pythonで、同じ長さの2つのリストをインターリーブする良い方法はありますか?
私が与えられた[1,2,3]
と言うと[10,20,30]
。それらをに変換したいと思い[1,10,2,20,3,30]
ます。
回答:
質問を投稿して、私は簡単に次のことができることに気づきました。
[val for pair in zip(l1, l2) for val in pair]
ここでl1
、l2
は2つのリストです。
インターリーブするリストがN個ある場合、
lists = [l1, l2, ...]
[val for tup in zip(*lists) for val in tup]
izip_longest
、python2とzip_longest
python3 `の[val for pair in itertools.zip_longest(l1, l2) for val in pair]
結果に使用してください['a', 'b', 'a', 'b', 'a', 'b', None, 'b', None, 'b', None, 'b']
Python> = 2.3の場合、拡張スライス構文があります。
>>> a = [0, 2, 4, 6, 8]
>>> b = [1, 3, 5, 7, 9]
>>> c = a + b
>>> c[::2] = a
>>> c[1::2] = b
>>> c
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
この行c = a + b
は、正確に正しい長さの新しいリストを作成する簡単な方法として使用されます(この段階では、その内容は重要ではありません)。次の2行は、インターリーブの実際の作業をa
行いb
ます。最初の行は、の要素a
をc
;のすべての偶数のインデックスに割り当てます。2つ目は、の要素b
をのすべての奇数番号のインデックスに割り当てますc
。
与えられた
a = [1, 2, 3]
b = [10, 20, 30]
c = [100, 200, 300, 999]
コード
同じ長さのリストを仮定すると、あなたがインターリーブされたリストを取得することができますitertools.chain
し、zip
:
import itertools
list(itertools.chain(*zip(a, b)))
# [1, 10, 2, 20, 3, 30]
代替案
より一般的には、リストが等しくない場合は、次を使用しますzip_longest
(推奨)。
[x for x in itertools.chain(*itertools.zip_longest(a, c)) if x is not None]
# [1, 100, 2, 200, 3, 300, 999]
多くのリストを安全にインターリーブできます。
[x for x in itertools.chain(*itertools.zip_longest(a, b, c)) if x is not None]
# [1, 10, 100, 2, 20, 200, 3, 30, 300, 999]
roundrobin
itertoolsレシピに同梱されているライブラリ、interleave
およびinterleave_longest
。
import more_itertools
list(more_itertools.roundrobin(a, b))
# [1, 10, 2, 20, 3, 30]
list(more_itertools.interleave(a, b))
# [1, 10, 2, 20, 3, 30]
list(more_itertools.interleave_longest(a, c))
# [1, 100, 2, 200, 3, 300, 999]
yield from
最後に、Python 3で興味深いものについて(推奨されていませんが):
list(filter(None, ((yield from x) for x in zip(a, b))))
# [1, 10, 2, 20, 3, 30]
list([(yield from x) for x in zip(a, b)])
# [1, 10, 2, 20, 3, 30]
+を使用してインストールpip install more_itertools
受け入れられた答えが対処していないさまざまなサイズのリストでこれを行う方法が必要でした。
私のソリューションはジェネレーターを使用しており、その使用法はそれのために少し良く見えます:
def interleave(l1, l2):
iter1 = iter(l1)
iter2 = iter(l2)
while True:
try:
if iter1 is not None:
yield next(iter1)
except StopIteration:
iter1 = None
try:
if iter2 is not None:
yield next(iter2)
except StopIteration:
iter2 = None
if iter1 is None and iter2 is None:
raise StopIteration()
そしてその使用法:
>>> a = [1, 2, 3, 4, 5]
>>> b = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
>>> list(interleave(a, b))
[1, 'a', 2, 'b', 3, 'c', 4, 'd', 5, 'e', 'f', 'g']
>>> list(interleave(b, a))
['a', 1, 'b', 2, 'c', 3, 'd', 4, 'e', 5, 'f', 'g']
roundrobin
レシピitertools
は、これをより一般的に拡張したものです。
代替:
>>> l1=[1,2,3]
>>> l2=[10,20,30]
>>> [y for x in map(None,l1,l2) for y in x if y is not None]
[1, 10, 2, 20, 3, 30]
これは、マップがリストで並行して機能するために機能します。2.2でも同じように機能します。None
呼び出された関数として、それ自体map
でタプルのリストを生成します。
>>> map(None,l1,l2,'abcd')
[(1, 10, 'a'), (2, 20, 'b'), (3, 30, 'c'), (None, None, 'd')]
次に、タプルのリストをフラット化します。
もちろん、利点は、map
任意の数のリストで機能し、長さが異なっていても機能することです。
>>> l1=[1,2,3]
>>> l2=[10,20,30]
>>> l3=[101,102,103,104]
>>> [y for x in map(None,l1,l2,l3) for y in x if y in not None]
[1, 10, 101, 2, 20, 102, 3, 30, 103, 104]
if y
フィルター0
も除去され、if y is not None
壊れにくくなります。
私はaixのソリューションが一番好きです。これが2.2で機能すると思う別の方法です:
>>> x=range(3)
>>> x
[0, 1, 2]
>>> y=range(7,10)
>>> y
[7, 8, 9]
>>> sum(zip(x,y),[])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only concatenate list (not "tuple") to list
>>> sum(map(list,zip(x,y)),[])
[0, 7, 1, 8, 2, 9]
そしてもう1つの方法:
>>> a=[x,y]
>>> [a[i][j] for j in range(3) for i in (0,1)]
[0, 7, 1, 8, 2, 9]
そして:
>>> sum((list(i) for i in zip(x,y)),[])
[0, 7, 1, 8, 2, 9]
[el for el in itertools.chain(*itertools.izip_longest([1,2,3], [4,5])) if el is not None]
None
残しておきたいものがない限り
「Pythonで同じ長さの複数のリストをインターリーブする」という質問のタイトルに答えるために、@ ekhumoroの2つのリストの答えを一般化することができます。これには、@ NPEによる(エレガントな)ソリューションとは異なり、リストが同じ長さであることが明示的に必要です。
import itertools
def interleave(lists):
"""Interleave a list of lists.
:param lists: List of lists; each inner length must be the same length.
:returns: interleaved single list
:rtype: list
"""
if len(set(len(_) for _ in lists)) > 1:
raise ValueError("Lists are not all the same length!")
joint = list(itertools.chain(*lists))
for l_idx, li in enumerate(lists):
joint[l_idx::len(lists)] = li
return joint
例:
>>> interleave([[0,2,4], [1, 3, 5]])
[0, 1, 2, 3, 4, 5]
>>> interleave([[0,2,4], [1, 3, 5], [10, 11, 12]])
[0, 1, 10, 2, 3, 11, 4, 5, 12]
>>> interleave([[0,2,4], [1, 3, 5], [10, 11, 12], [13, 14, 15]])
[0, 1, 10, 13, 2, 3, 11, 14, 4, 5, 12, 15]
>>> interleave([[0,2,4], [1, 3, 5], [10, 11, 12], [13, 14]])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 10, in interleave
ValueError: Lists are not all the same length!
>>> interleave([[0,2,4]])
[0, 2, 4]
it = iter(l1); list((yield next(it)) or i for i in l2)