122 votes

Paires de la liste unique

Il m'est arrivé assez souvent de devoir traiter une liste par paires. Je me demandais quelle serait la manière pythique et efficace de le faire, et j'ai trouvé ceci sur Google :

pairs = zip(t[::2], t[1::2])

Je pensais que c'était assez pythonique, mais après une discussion récente impliquant idiomes contre efficacité j'ai décidé de faire quelques tests :

import time
from itertools import islice, izip

def pairs_1(t):
    return zip(t[::2], t[1::2]) 

def pairs_2(t):
    return izip(t[::2], t[1::2]) 

def pairs_3(t):
    return izip(islice(t,None,None,2), islice(t,1,None,2))

A = range(10000)
B = xrange(len(A))

def pairs_4(t):
    # ignore value of t!
    t = B
    return izip(islice(t,None,None,2), islice(t,1,None,2))

for f in pairs_1, pairs_2, pairs_3, pairs_4:
    # time the pairing
    s = time.time()
    for i in range(1000):
        p = f(A)
    t1 = time.time() - s

    # time using the pairs
    s = time.time()
    for i in range(1000):
        p = f(A)
        for a, b in p:
            pass
    t2 = time.time() - s
    print t1, t2, t2-t1

Voici les résultats obtenus sur mon ordinateur :

1.48668909073 2.63187503815 1.14518594742
0.105381965637 1.35109519958 1.24571323395
0.00257992744446 1.46182489395 1.45924496651
0.00251388549805 1.70076990128 1.69825601578

Si je les interprète correctement, cela devrait signifier que l'implémentation des listes, de l'indexation des listes et du découpage des listes en Python est très efficace. C'est un résultat à la fois réconfortant et inattendu.

Existe-t-il une autre façon, "meilleure", de parcourir une liste par paires ?

Notez que si la liste comporte un nombre impair d'éléments, le dernier élément ne figurera dans aucune des paires.

Quelle serait la bonne façon de s'assurer que tous les éléments sont inclus ?

J'ai ajouté ces deux suggestions à partir des réponses aux tests :

def pairwise(t):
    it = iter(t)
    return izip(it, it)

def chunkwise(t, size=2):
    it = iter(t)
    return izip(*[it]*size)

Voici les résultats :

0.00159502029419 1.25745987892 1.25586485863
0.00222492218018 1.23795199394 1.23572707176

Résultats jusqu'à présent

Très pythoniques et très efficaces :

pairs = izip(t[::2], t[1::2])

Très efficace et très pythique :

pairs = izip(*[iter(t)]*2)

Il m'a fallu un moment pour comprendre que la première réponse utilise deux itérateurs alors que la seconde n'en utilise qu'un seul.

Pour traiter les séquences comportant un nombre impair d'éléments, il a été suggéré d'ajouter un élément à la séquence originale ( None ) qui est apparié avec le dernier élément précédent, ce qui peut être réalisé avec itertools.izip_longest() .

Enfin

Notez que, dans Python 3.x, zip() se comporte comme itertools.izip() et itertools.izip() est parti.

0 votes

RE : la "bonne façon" -- il n'y a pas de "bonne" façon ! Cela dépend du cas d'utilisation.

0 votes

@Andrew Jaffe J'ai donné les critères du "meilleur" dans ce cas : efficace, et pythonique.

0 votes

@Apalala : Je veux dire que le résultat d'avoir un nombre impair dépend de l'utilisation. Par exemple, vous pouvez simplement omettre le dernier élément, ou ajouter un élément fictif spécifique connu, ou encore dupliquer le dernier élément.

64voto

Jochen Ritzel Points 42916

Ma façon préférée de le faire :

from itertools import izip

def pairwise(t):
    it = iter(t)
    return izip(it,it)

# for "pairs" of any length
def chunkwise(t, size=2):
    it = iter(t)
    return izip(*[it]*size)

Lorsque vous voulez jumeler tous les éléments, vous pouvez évidemment avoir besoin d'une valeur de remplissage :

from itertools import izip_longest
def blockwise(t, size=2, fillvalue=None):
    it = iter(t)
    return izip_longest(*[it]*size, fillvalue=fillvalue)

0 votes

La première fonction (par paire) semble manquer le clonage et l'avancement du second itérateur. Voir le itertools la section des recettes.

0 votes

@Apalala : zip fait avancer deux fois le même itérateur.

0 votes

Bien sûr, vous avez raison, et la méthode par paire est la plus efficace jusqu'à présent, je ne sais pas pourquoi.

53voto

Tim Pietzcker Points 146308

Je dirais que votre solution initiale pairs = zip(t[::2], t[1::2]) est la meilleure car elle est la plus facile à lire (et en Python 3, zip renvoie automatiquement un itérateur au lieu d'une liste).

Pour s'assurer que tous les éléments sont inclus, vous pouvez simplement étendre la liste par None .

Ensuite, si la liste a un nombre impair d'éléments, la dernière paire sera (item, None) .

>>> t = [1,2,3,4,5]
>>> t.append(None)
>>> zip(t[::2], t[1::2])
[(1, 2), (3, 4), (5, None)]
>>> t = [1,2,3,4,5,6]
>>> t.append(None)
>>> zip(t[::2], t[1::2])
[(1, 2), (3, 4), (5, 6)]

6voto

Tomasz Elendt Points 998

Je commence par un petit avertissement - n'utilisez pas le code ci-dessous. Ce n'est pas du tout Python, je l'ai écrit juste pour le plaisir. C'est similaire à @THC4k pairwise mais elle utilise la fonction iter y lambda fermetures. Il n'utilise pas itertools et ne prend pas en charge le module fillvalue . Je l'ai mis ici parce que quelqu'un pourrait le trouver intéressant :

pairwise = lambda t: iter((lambda f: lambda: (f(), f()))(iter(t).next), None)

4voto

Pat Points 4681

En ce qui concerne les plus pythoniques, je dirais que les recettes fournies dans les documents sources de python (dont certaines ressemblent beaucoup aux réponses fournies par @JochenRitzel) est probablement votre meilleure option ;)

def grouper(iterable, n, fillvalue=None):
    "Collect data into fixed-length chunks or blocks"
    # grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx
    args = [iter(iterable)] * n
    return izip_longest(fillvalue=fillvalue, *args)

Sur un python moderne, il suffit d'utiliser zip_longest(*args, fillvalue=fillvalue) selon la page du document correspondant .

4voto

>>> my_list = [1,2,3,4,5,6,7,8,9,10]
>>> my_pairs = list()
>>> while(my_list):
...     a = my_list.pop(0); b = my_list.pop(0)
...     my_pairs.append((a,b))
... 
>>> print(my_pairs)
[(1, 2), (3, 4), (5, 6), (7, 8), (9, 10)]

0 votes

IndexError : pop à partir d'une liste vide

0 votes

@HQuser Bien sûr, vous obtiendrez cette erreur si vous avez un nombre impair d'éléments dans la liste. Vous devez être sûr que vous avez des paires ou vérifier cette condition d'erreur.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X