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

209

Comment faire un for boucle ou une liste de compréhension pour 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)

Production:

1+2=3
3+4=7
5+6=11
jackhab
la source
2
Pour les paires qui se chevauchent: stackoverflow.com/questions/5434891/…
user202729

Réponses:

231

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

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)

Dans Python 3, vous pouvez remplacer izippar la zip()fonction intégrée et supprimer leimport .

Tout crédit à Martineau pour sa réponse à ma question , j'ai trouvé que c'était très efficace car il ne fait qu'une itération sur la liste et ne crée pas de listes inutiles dans le processus.

NB : Cela ne doit pas être confondu avec la pairwiserecette dans la propre itertoolsdocumentation de Python , qui donne s -> (s0, s1), (s1, s2), (s2, s3), ..., comme l'a souligné @lazyr dans les commentaires.

Petit ajout pour ceux qui souhaitent faire une vérification de type avec mypy sur Python 3:

from typing import Iterable, Tuple, TypeVar

T = TypeVar("T")

def grouped(iterable: Iterable[T], n=2) -> Iterable[Tuple[T, ...]]:
    """s -> (s0,s1,s2,...sn-1), (sn,sn+1,sn+2,...s2n-1), ..."""
    return zip(*[iter(iterable)] * n)
Johnsyweb
la source
16
À ne pas confondre avec la fonction par paire suggérée dans la section des recettes itertools , qui donnes -> (s0,s1), (s1,s2), (s2, s3), ...
Lauritz V. Thaulow
1
Cela fait une chose différente. Votre version ne rapporte que la moitié du nombre de paires par rapport à la itertoolsfonction de recette du même nom. Bien sûr, le vôtre est plus rapide ...
Sven Marnach
Hein? Votre fonction et la fonction à laquelle j'ai fait référence font des choses différentes, et c'était l'objet de mon commentaire.
Lauritz V. Thaulow
5
FAITES ATTENTION! L'utilisation de ces fonctions vous expose au risque de ne pas répéter les derniers éléments d'un itérable. Exemple: liste (groupée ([1,2,3], 2)) >>> [(1, 2)] .. quand vous vous attendez à [(1,2), (3,)]
egafni
4
@ Erik49: Dans le cas spécifié dans la question, cela n'aurait aucun sens d'avoir un tuple "incomplet". Si vous souhaitez inclure un tuple incomplet, vous pouvez utiliser à la izip_longest()place de izip(). Par exemple: list(izip_longest(*[iter([1, 2, 3])]*2, fillvalue=0))-> [(1, 2), (3, 0)]. J'espère que cela t'aides.
Johnsyweb
191

Eh bien, vous avez besoin d'un tuple 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 (index % 2 == 0)
  • zip(x,y) crée une collection de tuple à partir des collections x et y des mêmes éléments d'index.
Margus
la source
8
Cela peut également être étendu si plus de deux éléments sont nécessaires. Par exemplefor i, j, k in zip(data[0::3], data[1::3], data[2::3]):
lifebalance
19
Tellement plus propre que de faire une importation et de définir une fonction!
kmarsh
7
@kmarsh: Mais cela ne fonctionne que sur les séquences, la fonction fonctionne sur n'importe quel itérable; et cela utilise O (N) d'espace supplémentaire, la fonction ne fonctionne pas; en revanche, c'est généralement plus rapide. Il y a de bonnes raisons de choisir l'un ou l'autre; avoir peur importn'en fait pas partie.
abarnert
77
>>> 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']
pyanon
la source
1
Cela ne fonctionne pas sur Python-3.6.0 mais fonctionne toujours sur Python-2.7.10
Hamid Rohani
6
@HamidRohani ziprenvoie un zipobjet en Python 3, qui n'est pas indexable. Il doit être converti en une séquence ( list, tuple, etc.) en premier lieu , mais « ne fonctionne pas » est un peu exagéré.
vaultah
58

Une solution simple.

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

pour i dans la plage (0, len (l), 2):
    afficher str (l [i]), '+', str (l [i + 1]), '=', str (l [i] + l [i + 1])
tâche à l'intérieur
la source
1
que faire si votre liste n'est pas paire et que vous souhaitez simplement afficher le dernier numéro tel quel?
Hans de Jong
@HansdeJong ne vous a pas compris. Veuillez expliquer un peu plus.
taskinoor
2
Merci. J'ai déjà compris comment le faire. Le problème était que si vous aviez une liste qui ne contenait même pas de nombre, elle obtiendrait une erreur d'index. Résolu avec un essai: sauf:
Hans de Jong
Ou ((l[i], l[i+1])for i in range(0, len(l), 2))pour un générateur, peut être facilement modifié pour des tuples plus longs.
Basel Shishani
44

Bien que toutes les réponses utilisées zipsoient correctes, je trouve que l'implémentation de la fonctionnalité vous-même conduit à un code plus lisible:

def pairwise(it):
    it = iter(it)
    while True:
        try:
            yield next(it), next(it)
        except StopIteration:
            # no more elements in the iterator
            return

La it = iter(it)partie garantit qu'il its'agit bien d'un itérateur, et pas seulement d'un itérable. Si itest 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)
mic_e
la source
2
Cette solution permet de généraliser à la taille des tuples> 2
guilloptero
1
Cette solution fonctionne également si itn'est qu'un itérateur et non un itérable. Les autres solutions semblent reposer sur la possibilité de créer deux itérateurs indépendants pour la séquence.
skyking
J'ai trouvé cette approche sur stackoverflow.com/a/16815056/2480481 avant de voir cette réponse. Est plus propre, plus facile que de traiter avec zip ().
m3nda
2
J'aime que cela permette d'éviter de tripler l'utilisation de la mémoire comme réponse acceptée.
Kentzo
Cela ne fonctionne pas bien avec les forboucles en Python 3.5+ en raison de PEP 479 , qui remplace tout déclenché StopIterationdans un générateur par un RuntimeError.
sidney
28

J'espère que ce sera une manière 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)]
Vivek Srinivasan
la source
18

Si vous êtes intéressé par les performances, j'ai fait un petit benchmark (en utilisant ma bibliothèque simple_benchmark) pour comparer les performances des solutions et j'ai inclus une fonction de l'un de mes packages:iteration_utilities.grouper

from iteration_utilities import grouper
import matplotlib as mpl
from simple_benchmark import BenchmarkBuilder

bench = BenchmarkBuilder()

@bench.add_function()
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

@bench.add_function()
def Margus(data):
    for i, k in zip(data[0::2], data[1::2]):
        pass

@bench.add_function()
def pyanon(l):
    list(zip(l,l[1:]))[::2]

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

@bench.add_function()
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

@bench.add_function()
def MSeifert(it):
    for item1, item2 in grouper(it, 2):
        pass

bench.use_random_lists_as_arguments(sizes=[2**i for i in range(1, 20)])
benchmark_result = bench.run()
mpl.rcParams['figure.figsize'] = (8, 10)
benchmark_result.plot_both(relative_to=MSeifert)

entrez la description de l'image ici

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

Si cela ne vous dérange pas la dépendance supplémentaire alors à grouperpartir iteration_utilitiessera probablement un peu plus rapide.

Réflexions 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 (c'est-à-dire les listes, les chaînes, etc.), par exemple les solutions Margus / pyanon / taskinoor qui utilisent l'indexation tandis que d'autres solutions fonctionnent sur n'importe quel itérable (c'est-à-dire les séquences et générateurs, les itérateurs) comme Johnysweb / mic_e / mes solutions.

Ensuite, Johnysweb a également fourni une solution qui fonctionne pour d'autres tailles que 2 tandis que les autres réponses ne le font 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 savoir 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 rembourrée pour la rendre de même taille? L'article restant doit-il être retourné comme pièce unique? L'autre réponse n'aborde pas ce point directement, mais si je n'ai rien oublié, ils suivent tous l'approche selon laquelle l'élément restant doit être rejeté (sauf pour la réponse des taskinoors - qui soulèvera en fait une exception).

Avec groupervous pouvez décider 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)]
MSeifert
la source
12

Utilisez les commandes zipet iterensemble:

Je trouve cette solution iterassez élégante:

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

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

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 aux Néléments à la fois:

N = 2
list(zip(*([iter(l)] * N)))
# [(1, 2), (3, 4), (5, 6)]
Mécanicien quantique
la source
10
for (i, k) in zip(l[::2], l[1::2]):
    print i, "+", k, "=", i+k

zip(*iterable) renvoie un tuple avec l'élément suivant 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 nombre derrière, le deuxième deux-points n'est nécessaire que si vous voulez une étape dans la tranche »(dans ce cas 2).

l[1::2]fait la même chose mais commence dans le deuxième élément des listes et renvoie donc les 2e, 4e, 6e, etc. éléments de la liste d' origine .

alexpinho98
la source
4
Cette réponse a déjà été donnée par Margus il y a deux ans. stackoverflow.com/questions/5389507/…
cababunga
1
1 pour expliquer le fonctionnement de la [number::number]syntaxe. utile pour ceux qui n'utilisent pas souvent python
Alby
2

vous pouvez utiliser le package more_itertools .

import more_itertools

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

Avec déballage:

l = [1,2,3,4,5,6]
while l:
    i, k, *l = l
    print(str(i), '+', str(k), '=', str(i+k))
toaruScar
la source
Hou la la! Pourquoi je ne pouvais pas y penser :) Vous devez simplement gérer le cas où il n'y a pas de paire absolue (entrées impaires)
Saurav Kumar
1

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 s'excluant mutuellement).

Dans 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)
Chris Malek
la source
1

J'ai besoin de diviser une liste par un nombre et fixé comme ça.

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]]
Ali Katkar
la source
1

Il y a plusieurs façons de procéder. Par exemple:

lst = [1,2,3,4,5,6]
[(lst[i], lst[i+1]) for i,_ in enumerate(lst[:-1])]    
>>>[(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]

[i for i in zip(*[iter(lst)]*2)]    
>>>[(1, 2), (3, 4), (5, 6)]
Binit Bhagat
la source
0

J'ai pensé que c'était un bon endroit pour partager ma généralisation de ceci pour n> 2, qui est juste une fenêtre coulissante sur un itérable:

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

    return itertools.izip(*its)
Yuval
la source
0

Le titre de cette question est trompeur, vous semblez rechercher des paires consécutives, mais si vous voulez parcourir l'ensemble de toutes les paires possibles, cela fonctionnera:

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

Utilisation de la saisie pour vérifier les données à l'aide de l'outil d'analyse statique mypy :

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
Vlad Bezden
la source
0

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 souhaitez l'itérer par paires. Pour itérer sur des triplets ou plus, il suffit de changer la commande d'étape "range", par exemple:

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

(vous devez gérer les valeurs excédentaires si la longueur de votre tableau et le pas ne correspondent pas)

mchrgr2000
la source
0
from itertools import tee
def pairwise(iterable):
    a = iter(iterable)
    for i in a:
        try:
            yield (i, next(a))
        except StopIteration:
            yield(i, None)
       
    

for i in pairwise([3, 7, 8, 9, 90, 900]):
    print(i)

Production:

(3, 7)
(8, 9)
(90, 900)
> 
Vishesh Mangla
la source
vous pouvez choisir n'importe quel remplissage pour None
Vishesh Mangla
-1

Ici, nous pouvons avoir une alt_elemméthode qui peut s'adapter à votre boucle for.

def alt_elem(list, index=2):
    for i, elem in enumerate(list, start=1):
        if not i % index:
           yield tuple(list[i-index:i])


a = range(10)
for index in [2, 3, 4]:
    print("With index: {0}".format(index))
    for i in alt_elem(a, index):
       print(i)

Production:

With index: 2
(0, 1)
(2, 3)
(4, 5)
(6, 7)
(8, 9)
With index: 3
(0, 1, 2)
(3, 4, 5)
(6, 7, 8)
With index: 4
(0, 1, 2, 3)
(4, 5, 6, 7)

Remarque: La solution ci-dessus peut ne pas être efficace compte tenu des opérations effectuées dans func.

Sanket Sudake
la source
-1
a_list = [1,2,3,4,5,6]
empty_list = [] 
for i in range(0,len(a_list),2):
   empty_list.append(a_list[i]+a_list[i+1])   
print(empty_list)
user391
la source