単一リストのペア


98

多くの場合、リストをペアで処理する必要があることがわかりました。私はそれを行うためのpythonicかつ効率的な方法はどれだろうと思っていました、そしてこれをGoogleで見つけました:

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

私はそれは十分にパイソンだと思ったが、イディオム対効率に関する最近の議論の後で、私はいくつかのテストをすることに決めた:

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

これらは私のコンピュータ上の結果でした:

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

私がそれらを正しく解釈している場合、それは、Pythonでのリスト、リストのインデックス付け、リストのスライスの実装が非常に効率的であることを意味するはずです。それは慰めと予想外の両方の結果です。

リストをペアでトラバースする別の「より良い」方法はありますか?

リストの要素数が奇数の場合、最後の要素はどのペアにも含まれないことに注意してください。

すべての要素が含まれていることを確認するための正しい方法はどれですか?

テストへの回答から次の2つの提案を追加しました。

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

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

これらは結果です:

0.00159502029419 1.25745987892 1.25586485863
0.00222492218018 1.23795199394 1.23572707176

これまでの結果

最もpythonicで非常に効率的:

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

最も効率的で非常にpythonic:

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

最初の答えが2つのイテレータを使用しているのに対して、2番目のイテレータは1つのイテレータを使用しているのを見るのに少し時間がかかりました。

奇数の要素を持つシーケンスを処理するためにNone、以前の最後の要素とペアになる1つの要素()を追加する元のシーケンスを拡張するという提案がありましたitertools.izip_longest()。これはで実現できます。

最後に

Python 3.xでは、zip()として動作しitertools.izip()itertools.izip() なくなっていることに注意してください。


RE:「正しい方法」-「正しい」方法はありません!ユースケースによって異なります。
Andrew Jaffe、2011年

@Andrew Jaffe私はこの場合の「最高」の基準を与えました:効率的で、pythonic。
アパララ2011年

@Apalala:奇数の結果は用途に依存するということです。たとえば、最後の要素を省略したり、特定の既知のダミー要素を追加したり、最後の要素を複製したりできます
Andrew Jaffe

2
@Apalala:timeitモジュールの代わりにいくつかのmumbo-jumboを使用しているため。
SilentGhost 2011年

回答:


52

それを行う私のお気に入りの方法:

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)

すべての要素をペアにする場合は、明らかにfillvalueが必要になることがあります。

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

最初の(ペアワイズ)関数には、2番目のイテレーターの複製と拡張が欠落しているようです。itertoolsレシピのセクションを参照してください。
アパララ2011年

@Apalala:zipは同じイテレーターを2回進めます。
Jochen Ritzel、2011年

もちろん、あなたは正しいですし、ペアワイズがこれまでのところ最も効率的です。理由はわかりません。
アパララ2011年

1
私はこのソリューションが大好きです。それは怠惰で、イテレータのステートフル性を利用して大きな効果をもたらします。おそらく読みやすさを犠牲にして、それをワンライナーにすることさえできます:izip(*[iter(t)]*size)
Channing Moore 14

2番目のソリューションでは、パフォーマンスを追求する場合、リストの作成を避けたくありませんか?
最大

40

私はあなたの最初の解決策pairs = zip(t[::2], t[1::2])が最も読みやすいので最良の解決策だと思います(そしてPython 3ではzip、リストの代わりに反復子を自動的に返します)。

すべての要素が確実に含まれるようにするには、でリストを拡張しますNone

次に、リストの要素数が奇数の場合、最後のペアはになります(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

私は小さな免責事項から始めます-以下のコードを使用しないでください。まったくPythonicではありません。私は単に楽しみのために書きました。これは@ THC4k pairwise関数に似ていますがiter、とlambdaクロージャーを使用します。itertoolsモジュールを使用せず、をサポートしていませんfillvalue。誰かが面白いと思うかもしれないので、ここに置きました:

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

3

ほとんどのpythonicに関する限り、私はpythonソースドキュメントで提供されているレシピ(@JochenRitzelが提供した回答によく似ているもの)がおそらく最善の策だと思います;)

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)

2

リストをペアでトラバースする別の「より良い」方法はありますか?

はっきりとは言えませんが、疑います。他のトラバーサルには、解釈が必要なPythonコードがさらに含まれます。zip()のような組み込み関数はCで書かれており、はるかに高速です。

すべての要素が含まれていることを確認するための正しい方法はどれですか?

リストの長さを確認し、それが奇数(len(list) & 1 == 1)の場合は、リストをコピーしてアイテムを追加します。


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:空のリストからポップ
HQuser

@HQuser確かに、リストに奇数の項目がある場合、そのエラーが発生します。ペアがあることを確認するか、このエラー状態を確認する必要があります。
WaterMolecule 2019年


0

これは、ジェネレーターを使用してペア/レッグを作成する例です。ジェネレータにはスタック制限がありません

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

例:

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

出力:

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

実行時間の比較?
アラン

元のリストのほとんどの数値は2つのタプルで表示されるため、リストはペアに分割されていません。期待される出力がある[(0, 1), (2, 3), (4, 5)....
アパラーラ

@Apalala指摘していただきありがとうございます。適切な出力を提供するようにコードを修正しました
Vlad Bezden

zip()Python 3.xでは@VladBezdenのジェネレータが既に返されます
Apalala

-1

誰かがアルゴリズム的に答えを必要とする場合に備えて、ここにあります:

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

ただし、元のリストを使用popしたため、元のリストも最後の要素に削減されることに注意してください。

>>> k
[4]
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.