En Python, existe-t-il un bon moyen d'entrelacer deux listes de même longueur?
Disons que je reçois [1,2,3]
et [10,20,30]
. Je voudrais transformer ceux-ci en [1,10,2,20,3,30]
.
Après avoir posté la question, je me suis rendu compte que je pouvais simplement faire ce qui suit:
[val for pair in Zip(l1, l2) for val in pair]
où l1
et l2
sont les deux listes.
S'il y a N listes à entrelacer, alors
lists = [l1, l2, ...]
[val for tup in Zip(*lists) for val in tup]
Pour plus de recettes, suivez Le meilleur moyen d'entrelacer une liste avec ses valeurs de suffixe . Certaines des méthodes démontrées peuvent être généralisées à deux ou plusieurs listes d'égale longueur.
Pour python> = 2.3, il existe une syntaxe extended slice :
>>> 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]
La ligne c = a + b
est utilisée comme un moyen simple de créer une nouvelle liste de la longueur exacte (à ce stade, son contenu n’est pas important). Les deux lignes suivantes font le travail d’entrelacement a
et b
: la première affecte les éléments de a
à tous les index pairs de c
; la seconde attribue les éléments de b
à tous les index impairs de c
.
Donné
a = [1, 2, 3]
b = [10, 20, 30]
c = [100, 200, 300, 999]
Code
En supposant des listes de longueur égale, vous pouvez obtenir une liste entrelacée avec itertools.chain
et Zip
:
import itertools
list(itertools.chain(*Zip(a, b)))
# [1, 10, 2, 20, 3, 30]
Alternatives
Plus généralement avec des listes inégales, utilisez Zip_longest
(recommandé):
[x for x in itertools.chain(*itertools.Zip_longest(a, c)) if x is not None]
# [1, 100, 2, 200, 3, 300, 999]
De nombreuses listes peuvent être entrelacées en toute sécurité:
[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]
Bibliothèque livrée avec la recette roundrobin
itertools, interleave
et 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
Enfin, pour quelque chose d’intéressant dans Python 3 (bien que cela ne soit pas recommandé):
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]
+Installer avec pip install more_itertools
Alternative:
>>> 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]
Cela fonctionne parce que map fonctionne sur les listes en parallèle. Il fonctionne de la même manière sous 2.2. Par lui-même, avec None
comme fonctions appelées, map
produit une liste de n-uplets:
>>> map(None,l1,l2,'abcd')
[(1, 10, 'a'), (2, 20, 'b'), (3, 30, 'c'), (None, None, 'd')]
Il suffit ensuite d’aplatir la liste des n-uplets.
L’avantage, bien sûr, est que map
fonctionnera pour n’importe quel nombre de listes et fonctionnera même si leur longueur est différente:
>>> 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]
J'avais besoin d'un moyen de le faire avec des listes de tailles différentes auxquelles la réponse acceptée ne répond pas.
Ma solution utilise un générateur et son utilisation semble un peu plus agréable à cause de cela:
def interleave(l1, l2):
iter1 = iter(l1)
iter2 = iter(l2)
while True:
try:
if iter1 != None:
yield next(iter1)
except StopIteration:
iter1 = None
try:
if iter2 != None:
yield next(iter2)
except StopIteration:
iter2 = None
if iter1 == None and iter2 == None:
raise StopIteration()
Et son utilisation:
>>> 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']
J'aime mieux la solution d'Aix. Voici une autre façon, je pense, devrait fonctionner dans la version 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]
et une autre manière:
>>> a=[x,y]
>>> [a[i][j] for j in range(3) for i in (0,1)]
[0, 7, 1, 8, 2, 9]
et:
>>> 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]
Tant que vous n'avez pas None
que vous voulez garder
Pour répondre au titre de la question "Entrelacer plusieurs listes de même longueur en Python", nous pouvons généraliser la réponse à 2 listes de @ekhumoro. Cela exige explicitement que les listes aient la même longueur, contrairement à la solution (élégante) de @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
Exemples:
>>> 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]