단일 목록의 쌍


98

종종 나는 쌍으로 목록을 처리해야 할 필요성을 발견했습니다. 나는 그것을 수행하는 비단뱀적이고 효율적인 방법이 무엇인지 궁금해했으며 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

올바르게 해석하고 있다면 파이썬에서 목록, 목록 인덱싱 및 목록 분할을 구현하는 것이 매우 효율적이라는 것을 의미합니다. 편안함과 예상치 못한 결과입니다.

쌍으로 목록을 순회하는 또 다른 "더 나은"방법이 있습니까?

목록에 홀수 개의 요소가있는 경우 마지막 요소는 쌍에 포함되지 않습니다.

모든 요소가 포함되도록하는 올바른 방법은 무엇입니까?

테스트에 대한 답변에서 다음 두 가지 제안을 추가했습니다.

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

지금까지의 결과

가장 비단뱀적이고 매우 효율적입니다.

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

가장 효율적이고 매우 비단뱀 적입니다.

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

첫 번째 답변은 두 개의 반복자를 사용하고 두 번째 답변은 하나의 반복자를 사용한다는 것을 알아 차리는 데 잠시 시간이 걸렸습니다.

요소 수가 홀수 인 시퀀스를 처리하기 위해 None이전 마지막 요소와 쌍을 이루는 하나의 요소 ( )를 추가하여 원래 시퀀스를 확장하는 것이 itertools.izip_longest()좋습니다.

드디어

파이썬 3.x를, 즉 참고 zip()로 동작 itertools.izip()하고, itertools.izip() 사라 졌어요.


RE : "올바른 방법"- "올바른"방법은 없습니다! 사용 사례에 따라 다릅니다.
Andrew Jaffe 2011 년

@Andrew Jaffe 나는이 경우에 "최고"의 기준을 제시했다 : 효율적이고 비단뱀 적이다.
Apalala 2011 년

@Apalala : 홀수를 갖는 결과 는 용도에 따라 달라집니다. 예를 들면 : 당신은 단지 마지막 요소를 떠나, 또는 특정 알려진 더미 요소를 추가하거나 마지막 복제 할 수
앤드류 제피

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)

첫 번째 (쌍으로) 함수는 두 번째 반복자의 복제 및 진행이 누락 된 것 같습니다. itertools레시피 섹션을 참조하십시오 .
Apalala 2011 년

@Apalala : zip은 동일한 반복자를 두 번 진행합니다.
Jochen Ritzel 2011 년

물론, 당신 말이 맞고, 지금까지 pairwise가 가장 효율적입니다. 왜 그런지 모르겠습니다.
Apalala 2011 년

1
저는이 솔루션을 좋아합니다. 게으르고 반복기의 상태 저장성을 활용하여 큰 효과를냅니다. 당신은 가독성을 희생하지만, 아마도 그것을 한 줄을 만들 수 있습니다 :izip(*[iter(t)]*size)
채닝 무어

두 번째 솔루션의 경우 성능을 추구하는 경우 목록 작성을 피하고 싶지 않습니까?
최대

40

pairs = zip(t[::2], t[1::2])가장 읽기 쉽기 때문에 초기 솔루션 이 가장 좋다고 말하고 싶습니다 (파이썬 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

대부분의 파이썬이 진행 되는 한, 파이썬 소스 문서에 제공된 레시피 (일부는 @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 : pop from empty list
HQuser

@HQuser 물론, 목록에 홀수 개의 항목이 있으면 해당 오류가 발생합니다. 쌍이 있는지 확인하거나이 오류 조건을 확인해야합니다.
WaterMolecule

0

단지 그것을하십시오 :

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

귀하의 코드는 더 단순한 것과 동일 list(zip(l, l[1:]))하며 목록을 쌍으로 분할하지 않습니다.
Apalala

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

실행 시간 비교?
Alan은

원래 목록에있는 대부분의 숫자가 두 개의 튜플에 나타나기 때문에 목록은 쌍으로 나뉘 지 않습니다. 예상 출력은[(0, 1), (2, 3), (4, 5)....
Apalala

@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.