web-dev-qa-db-fra.com

Itérer sur tous les deux éléments d'une liste

Comment créer une boucle for ou une compréhension de liste de sorte que chaque itération me donne deux éléments?

l = [1,2,3,4,5,6]

for i,k in ???:
    print str(i), '+', str(k), '=', str(i+k)

Sortie:

1+2=3
3+4=7
5+6=11
160
jackhab

Vous avez besoin d'une implémentation pairwise() (ou grouped()).

Pour Python 2:

from itertools import izip

def pairwise(iterable):
    "s -> (s0, s1), (s2, s3), (s4, s5), ..."
    a = iter(iterable)
    return izip(a, a)

for x, y in pairwise(l):
   print "%d + %d = %d" % (x, y, x + y)

Ou plus généralement:

from itertools import izip

def grouped(iterable, n):
    "s -> (s0,s1,s2,...sn-1), (sn,sn+1,sn+2,...s2n-1), (s2n,s2n+1,s2n+2,...s3n-1), ..."
    return izip(*[iter(iterable)]*n)

for x, y in grouped(l, 2):
   print "%d + %d = %d" % (x, y, x + y)

En Python 3, vous pouvez remplacer izip par la fonction intégrée Zip() et abandonner la import.

Tout crédit à martineau _ pour sa réponse à ma question , j'ai trouvé cela très efficace, car il ne fait qu'une itération unique dans la liste et n'en crée pas listes inutiles dans le processus. 

NB: Ceci ne doit pas être confondu avec la pairwise recette } dans la propre _ { itertools documentation } de Python, qui donne s -> (s0, s1), (s1, s2), (s2, s3), ..., comme indiqué par @lazyr dans les commentaires.

196
Johnsyweb

Eh bien, vous avez besoin d'une pile de 2 éléments, donc

data = [1,2,3,4,5,6]
for i,k in Zip(data[0::2], data[1::2]):
    print str(i), '+', str(k), '=', str(i+k)

Où:

  • data[0::2] signifie créer un sous-ensemble d'éléments qui (index % 2 == 0)
  • Zip(x,y) crée une collection Tuple à partir des mêmes éléments d'index des collections x et y.
150
Margus
>>> l = [1,2,3,4,5,6]

>>> Zip(l,l[1:])
[(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]

>>> Zip(l,l[1:])[::2]
[(1, 2), (3, 4), (5, 6)]

>>> [a+b for a,b in Zip(l,l[1:])[::2]]
[3, 7, 11]

>>> ["%d + %d = %d" % (a,b,a+b) for a,b in Zip(l,l[1:])[::2]]
['1 + 2 = 3', '3 + 4 = 7', '5 + 6 = 11']
58
pyanon

Une solution simple.

 l = [1, 2, 3, 4, 5, 6] 

 pour i dans l'intervalle (0, len (l), 2): 
 print str (l [i]), '+', str (l [i + 1]), '=', str (l [i] + l [i + 1]) 
55
taskinoor

Bien que toutes les réponses utilisant Zip soient correctes, je trouve que la mise en œuvre de la fonctionnalité vous-même conduit à un code plus lisible:

def pairwise(it):
    it = iter(it)
    while True:
        yield next(it), next(it)

La partie it = iter(it) garantit que it est en réalité un itérateur, pas seulement un itératif. Si it est déjà un itérateur, cette ligne est un no-op.

Usage:

for a, b in pairwise([0, 1, 2, 3, 4, 5]):
    print(a + b)
34
mic_e

Toutes mes excuses pour mon retard. J'espère que ce sera une façon encore plus élégante de le faire.

a = [1,2,3,4,5,6]
Zip(a[::2], a[1::2])

[(1, 2), (3, 4), (5, 6)]
12
Vivek Srinivasan

Au cas où les performances vous intéresseraient, j’ai fait un petit test pour comparer les performances des solutions et j’ai inclus une fonction de l’un de mes packages: iteration_utilities.grouper

def Johnsyweb(l):
    def pairwise(iterable):
        "s -> (s0, s1), (s2, s3), (s4, s5), ..."
        a = iter(iterable)
        return Zip(a, a)

    for x, y in pairwise(l):
        pass

def Margus(data):
    for i, k in Zip(data[0::2], data[1::2]):
        pass

def pyanon(l):
    list(Zip(l,l[1:]))[::2]

def taskinoor(l):
    for i in range(0, len(l), 2):
        l[i], l[i+1]

def mic_e(it):
    def pairwise(it):
        it = iter(it)
        while True:
            try:
                yield next(it), next(it)
            except StopIteration:
                return

    for a, b in pairwise(it):
        pass

def MSeifert(it):
    for item1, item2 in grouper(it, 2):
        pass

from iteration_utilities import grouper
from simple_benchmark import benchmark_random_list

b = benchmark_random_list(
    [Johnsyweb, Margus, pyanon, taskinoor, mic_e, MSeifert],
    sizes=[2**i for i in range(1, 20)])

b.plot_both(relative_to=MSeifert)

 enter image description here

Windows 10 64 bits Anaconda Python 3.6

Donc, si vous voulez la solution la plus rapide sans dépendances externes, vous devriez probablement utiliser simplement l'approche donnée par Johnysweb (au moment de la rédaction de cet article, c'est la réponse la plus votée et la plus acceptée).

Si la dépendance supplémentaire ne vous dérange pas, alors la grouper de iteration_utilities sera probablement un peu plus rapide.

Pensées supplémentaires

Certaines des approches ont des restrictions, qui n'ont pas été discutées ici.

Par exemple, quelques solutions ne fonctionnent que pour les séquences (listes, chaînes, etc.), par exemple les solutions Margus/pyanon/taskinoor qui utilisent l’indexation, tandis que d’autres solutions fonctionnent sur toutes les unités éditables (séquences et générateurs, itérateurs). ) comme Johnysweb/mic_e/my solutions.

Ensuite, Johnysweb a également fourni une solution qui fonctionne pour des tailles autres que 2 alors que les autres réponses ne le sont pas (d'accord, le iteration_utilities.grouper permet également de définir le nombre d'éléments sur "groupe").

Ensuite, il y a aussi la question de ce qui devrait se passer s'il y a un nombre impair d'éléments dans la liste. Le dernier élément doit-il être rejeté? La liste doit-elle être complétée pour la rendre uniforme? L'article restant doit-il être retourné en tant que single? L'autre réponse ne traite pas directement ce point, cependant si je n'ai rien oublié, ils suivent tous l'approche selon laquelle le dernier élément doit être rejeté (sauf pour la réponse de taskinoors - qui déclenchera en réalité une exception).

Avec grouper, vous pouvez décider de ce que vous voulez faire:

>>> from iteration_utilities import grouper

>>> list(grouper([1, 2, 3], 2))  # as single
[(1, 2), (3,)]

>>> list(grouper([1, 2, 3], 2, truncate=True))  # ignored
[(1, 2)]

>>> list(grouper([1, 2, 3], 2, fillvalue=None))  # padded
[(1, 2), (3, None)]
10
MSeifert
for (i, k) in Zip(l[::2], l[1::2]):
    print i, "+", k, "=", i+k

Zip(*iterable) renvoie un tuple avec le prochain élément de chaque itérable.

l[::2] renvoie le 1er, le 3ème, le 5ème, etc. élément de la liste: le premier deux-points indique que la tranche commence au début car il n'y a pas de numéro derrière celle-ci, le second est uniquement nécessaire si vous voulez une étape dans la tranche '(dans ce cas 2).

l[1::2] fait la même chose mais commence par le deuxième élément de la liste afin de renvoyer les éléments 2, 4, 6, etc. de la liste original .

9
alexpinho98

Utilisez les commandes Zip et iter ensemble:

Je trouve cette solution utilisant iter assez élégante:

it = iter(l)
list(Zip(it, it))
# [(1, 2), (3, 4), (5, 6)]

Ce que j'ai trouvé dans la documentation Python 3 Zip .

it = iter(l)
print(*(f'{u} + {v} = {u+v}' for u, v in Zip(it, it)), sep='\n')

# 1 + 2 = 3
# 3 + 4 = 7
# 5 + 6 = 11

Pour généraliser à N éléments à la fois:

N = 2
list(Zip(*([iter(l)] * N)))
# [(1, 2), (3, 4), (5, 6)]
7
Quantum Mechanic

Pour tout le monde, cela pourrait aider, voici une solution à un problème similaire mais avec des paires qui se chevauchent (au lieu de paires mutuellement exclusives).

À partir de la documentation Python itertools :

from itertools import izip

def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = tee(iterable)
    next(b, None)
    return izip(a, b)

Ou plus généralement:

from itertools import izip

def groupwise(iterable, n=2):
    "s -> (s0,s1,...,sn-1), (s1,s2,...,sn), (s2,s3,...,sn+1), ..."
    t = tee(iterable, n)
    for i in range(1, n):
        for j in range(0, i):
            next(t[i], None)
    return izip(*t)
2
Chris Malek

vous pouvez utiliser more_itertools package.

import more_itertools

lst = range(1, 7)
for i, j in more_itertools.chunked(lst, 2):
    print(f'{i} + {j} = {i+j}')
1
Scott Ming

Je dois diviser une liste par un nombre et le fixer comme ceci. 

l = [1,2,3,4,5,6]

def divideByN(data, n):
        return [data[i*n : (i+1)*n] for i in range(len(data)//n)]  

>>> print(divideByN(l,2))
[[1, 2], [3, 4], [5, 6]]

>>> print(divideByN(l,3))
[[1, 2, 3], [4, 5, 6]]
1
Ali Katkar

Une approche simpliste:

[(a[i],a[i+1]) for i in range(0,len(a),2)]

ceci est utile si votre tableau est un et que vous voulez le parcourir par paires. Pour itérer sur des triplets ou plus, changez simplement la commande "range" step, par exemple:

[(a[i],a[i+1],a[i+2]) for i in range(0,len(a),3)]

(vous devez gérer des valeurs excédentaires si votre longueur de tableau et votre étape ne correspondent pas) 

0
mchrgr2000

Je pensais que c’était un bon endroit pour partager ma généralisation de ceci pour n> 2, ce qui n’est qu’une fenêtre glissante sur une valeur

def sliding_window(iterable, n):
    its = [ itertools.islice(iter, i, None) 
            for i, iter
            in enumerate(itertools.tee(iterable, n)) ]                               

    return itertools.izip(*its)
0
Yuval

Utiliser le typage pour pouvoir vérifier les données avec l’outil d’analyse mypy static:

from typing import Iterator, Any, Iterable, TypeVar, Tuple

T_ = TypeVar('T_')
Pairs_Iter = Iterator[Tuple[T_, T_]]

def legs(iterable: Iterator[T_]) -> Pairs_Iter:
    begin = next(iterable)
    for end in iterable:
        yield begin, end
        begin = end
0
Vlad Bezden

Le titre de cette question est trompeur, vous semblez rechercher des paires consécutives, mais si vous souhaitez effectuer une itération sur l'ensemble des paires possibles, cela fonctionnera:

for i,v in enumerate(items[:-1]):
        for u in items[i+1:]:
0
Ofek Ron