Tek listedeki çiftler


101

Yeterince çoğu zaman, bir listeyi çiftler halinde işleme ihtiyacını buldum. Bunu yapmanın pitonik ve verimli yolunun hangisi olacağını merak ediyordum ve bunu Google'da buldum:

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

Bunun yeterince pitonik olduğunu düşündüm, ancak son zamanlarda deyimler ile verimlilik arasındaki tartışmanın ardından bazı testler yapmaya karar verdim:

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

Bilgisayarımdaki sonuçlar şunlardı:

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

Bunları doğru yorumluyorsam, bu Python'da listelerin, liste indekslemenin ve liste dilimlemenin uygulanmasının çok verimli olduğu anlamına gelmelidir. Hem rahatlatıcı hem de beklenmedik bir sonuçtur.

Bir listeyi çiftler halinde geçmenin başka, "daha iyi" bir yolu var mı?

Listede tek sayıda eleman varsa, sonuncunun hiçbir çift içinde olmayacağına dikkat edin.

Tüm öğelerin dahil edilmesini sağlamanın doğru yolu hangisidir?

Cevaplardan şu iki öneriyi testlere ekledim:

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

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

Sonuçlar şunlardır:

0.00159502029419 1.25745987892 1.25586485863
0.00222492218018 1.23795199394 1.23572707176

Şimdiye kadarki sonuçlar

Çoğu pitonik ve çok verimli:

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

En verimli ve çok pitonik:

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

İlk cevabın iki yineleyici, ikincisinin ise tek bir yineleyici kullandığını düşünmem biraz zaman aldı.

Tek sayıda öğeye sahip dizilerle başa çıkmak için, öneri, Noneönceki son öğe ile eşleştirilen bir öğe ( ) ekleyerek orijinal diziyi büyütmek olmuştur , bu da elde edilebilecek bir şeydir itertools.izip_longest().

En sonunda

Python 3.x'te olduğu zip()gibi davrandığını itertools.izip()ve itertools.izip() gittiğini unutmayın.


RE: "doğru yol" - "doğru" yol yok! Kullanım durumuna bağlıdır.
Andrew Jaffe

@Andrew Jaffe Bu durumda "en iyi" kriterini verdim: verimli ve pitonik.
Apalala

@Apalala: Tek sayıya sahip olmanın sonucunun kullanıma bağlı olduğunu söylüyorum . Örneğin: Son öğeyi bırakabilir veya belirli bir bilinen kukla öğe ekleyebilir ya da sonuncuyu kopyalayabilirsiniz
Andrew Jaffe

2
@Apalala: çünkü timeitmodül yerine biraz mumbo-jumbo kullanıyorsun .
SilentGhost

Yanıtlar:


53

Bunu yapmanın en sevdiğim yolu:

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)

Tüm öğeleri eşleştirmek istediğinizde, açıkça bir dolgu değerine ihtiyacınız olabilir:

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

İlk (ikili) işlev, ikinci yineleyicinin klonlanması ve ilerlemesi eksik görünüyor. Bkz itertoolstarifleri bölümüne.
Apalala

@Apalala: zip, aynı yineleyiciyi iki kez ilerletir.
Jochen Ritzel

Elbette haklısınız ve şimdiye kadarki en verimli ikili, nedenini bilmiyorum.
Apalala

1
Bu çözümü seviyorum: tembel ve yineleyicilerin durumlarını büyük bir etki için kullanıyor. Okunabilirlik pahasına da olsa, tek satırlık bile yapabilirsiniz:izip(*[iter(t)]*size)
Channing Moore

İkinci çözümünüz için, performanstan sonra bir liste oluşturmaktan kaçınmak istemez miydiniz?
en fazla

42

İlk çözümünüzün pairs = zip(t[::2], t[1::2])en iyisi olduğunu söyleyebilirim çünkü okunması en kolay çözümdür (ve Python 3'te zipbir liste yerine otomatik olarak bir yineleyici döndürür).

Tüm öğelerin dahil edildiğinden emin olmak için listeyi basitçe uzatabilirsiniz None.

Daha sonra, listede tek sayıda eleman varsa, son çift olacaktır (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)]

6

Küçük bir sorumluluk reddi ile başlıyorum - aşağıdaki kodu kullanmayın. Pythonic değil, sadece eğlence için yazdım. @ THC4k pairwiseişlevine benzer ancak kullanır iterve kapatır lambda. itertoolsModül kullanmaz ve desteklemez fillvalue. Buraya koydum çünkü birisi onu ilginç bulabilir:

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

4

Çoğu pitonik söz konusu olduğunda , python kaynak belgelerinde sağlanan tariflerin (bazıları @JochenRitzel'in sağladığı cevaplara çok benzeyen) muhtemelen en iyi seçeneğiniz olduğunu söyleyebilirim;)

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)

Modern python'da zip_longest(*args, fillvalue=fillvalue) , ilgili belge sayfasına göre kullanmanız yeterlidir .


2

Bir listeyi çiftler halinde geçmenin başka, "daha iyi" bir yolu var mı?

Kesin olarak söyleyemem ama bundan şüpheliyim: Başka herhangi bir geçiş, yorumlanması gereken daha fazla Python kodu içerir. Zip () gibi yerleşik işlevler çok daha hızlı olan C ile yazılmıştır.

Tüm öğelerin dahil edilmesini sağlamanın doğru yolu hangisidir?

Listenin uzunluğunu kontrol edin ve eğer tek ( len(list) & 1 == 1) ise listeyi kopyalayın ve bir öğe ekleyin.


2
>>> 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)]

IndexError: boş listeden pop
HQuser

@HQuser Elbette, listede tek sayıda öğe varsa bu hatayı alırsınız. Çiftlerinizin olduğundan emin olmanız veya bu hata durumunu kontrol etmeniz gerekir.
WaterMolecule

1

Sadece yap:

>>> 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)]

Kodunuz basit olana eşdeğerdir list(zip(l, l[1:]))ve listeyi çiftlere ayırmaz.
Apalala

0

İşte bir jeneratör kullanarak çiftler / bacaklar oluşturmanın bir örneği. Jeneratörlerde yığın sınırları yoktur

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

Misal:

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

Çıktı:

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

Uygulama süresinin karşılaştırılması?
Alan

Orijinal listedeki çoğu sayı iki demet halinde göründüğünden liste çiftlere bölünmez. Beklenen çıktı[(0, 1), (2, 3), (4, 5)....
Apalala

@Apalala işaret ettiğiniz için teşekkür ederim. Doğru çıktıyı sağlamak için kodu düzelttim
Vlad Bezden

zip()Python 3.x'te zaten bir jeneratör döndürüyor, @VladBezden
Apalala

liste uzunluğu eşit değilse, en son öğe
atılır

-1

Birinin algoritma açısından cevaba ihtiyacı olması durumunda, işte burada:

>>> 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]]

Ancak, üzerinde kullandığınız için orijinal listenizin de son öğesine indirgeneceğini unutmayın pop.

>>> k
[4]
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.