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.

3voto

Aaron Digulla Points 143830

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

Je ne peux pas l'affirmer, mais j'en doute : Toute autre traversée inclurait plus de code Python qui doit être interprété. Les fonctions intégrées comme zip() sont écrites en C, ce qui est beaucoup plus rapide.

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

Vérifiez la longueur de la liste et si elle est impaire ( len(list) & 1 == 1 ), copier la liste et ajouter un élément.

3voto

Faites-le seulement :

>>> l = [1, 2, 3, 4, 5, 6]
>>> [(x,y) for x,y in zip(l[:-1], l[1:])]
[(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]

1 votes

Votre code est équivalent à celui, plus simple, de list(zip(l, l[1:])) et il ne divise pas la liste en paires.

1voto

Vlad Bezden Points 5024

Voici un exemple de création de paires/jambes à l'aide d'un générateur. Les générateurs ne sont pas soumis aux limites de la pile

def pairwise(data):
    zip(data[::2], data[1::2])

Exemple :

print(list(pairwise(range(10))))

Sortie :

[(0, 1), (2, 3), (4, 5), (6, 7), (8, 9)]

0 votes

Comparaison du temps d'exécution ?

0 votes

La liste n'est pas divisée en paires, car la plupart des chiffres de la liste originale apparaissent dans deux tuples. Le résultat attendu est [(0, 1), (2, 3), (4, 5)....

0 votes

@Apalala merci de le signaler. J'ai corrigé le code pour fournir la bonne sortie

0voto

frainmaster Points 1

Au cas où quelqu'un aurait besoin de la réponse en termes d'algorithme, la voici :

>>> def getPairs(list):
...     out = []
...     for i in range(len(list)-1):
...         a = list.pop(0)
...         for j in a:
...             out.append([a, j])
...     return b
>>> 
>>> k = [1, 2, 3, 4]
>>> l = getPairs(k)
>>> l
[[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]

Mais notez que votre liste originale sera également réduite à son dernier élément, parce que vous avez utilisé pop sur elle.

>>> k
[4]

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