[python] 청크 단위로 목록을 반복하는 가장 “pythonic”방법은 무엇입니까?

정수 목록을 입력으로 사용하는 Python 스크립트가 있는데 한 번에 4 개의 정수로 작업해야합니다. 불행히도 입력을 제어 할 수 없거나 4 요소 튜플 목록으로 전달했습니다. 현재이 방법으로 반복하고 있습니다.

for i in xrange(0, len(ints), 4):
    # dummy op for example code
    foo += ints[i] * ints[i + 1] + ints[i + 2] * ints[i + 3]

“C-think”와 비슷해 보이지만,이 상황을 다루는 더 파이썬적인 방법이 있다고 생각합니다. 반복 후 목록이 삭제되므로 보존 할 필요가 없습니다. 아마도 이런 것이 더 좋을까요?

while ints:
    foo += ints[0] * ints[1] + ints[2] * ints[3]
    ints[0:4] = []

그래도 여전히 “느낌”이 아닙니다. :-/

관련 질문 : 파이썬에서 어떻게 목록을 고른 크기의 덩어리로 나눕니 까?



답변

파이썬 itertools 문서 의 레시피 섹션 에서 수정되었습니다 .

from itertools import zip_longest

def grouper(iterable, n, fillvalue=None):
    args = [iter(iterable)] * n
    return zip_longest(*args, fillvalue=fillvalue)

예제
의사 코드로 예제를 간결하게 유지합니다.

grouper('ABCDEFG', 3, 'x') --> 'ABC' 'DEF' 'Gxx'

참고 : Python 2에서는 izip_longest대신 대신 사용하십시오 zip_longest.


답변

def chunker(seq, size):
    return (seq[pos:pos + size] for pos in range(0, len(seq), size))
# (in python 2 use xrange() instead of range() to avoid allocating a list)

단순한. 쉬운. 빠른. 모든 시퀀스에서 작동합니다.

text = "I am a very, very helpful text"

for group in chunker(text, 7):
   print repr(group),
# 'I am a ' 'very, v' 'ery hel' 'pful te' 'xt'

print '|'.join(chunker(text, 10))
# I am a ver|y, very he|lpful text

animals = ['cat', 'dog', 'rabbit', 'duck', 'bird', 'cow', 'gnu', 'fish']

for group in chunker(animals, 3):
    print group
# ['cat', 'dog', 'rabbit']
# ['duck', 'bird', 'cow']
# ['gnu', 'fish']


답변

나는 팬입니다

chunk_size= 4
for i in range(0, len(ints), chunk_size):
    chunk = ints[i:i+chunk_size]
    # process chunk of size <= chunk_size


답변

import itertools
def chunks(iterable,size):
    it = iter(iterable)
    chunk = tuple(itertools.islice(it,size))
    while chunk:
        yield chunk
        chunk = tuple(itertools.islice(it,size))

# though this will throw ValueError if the length of ints
# isn't a multiple of four:
for x1,x2,x3,x4 in chunks(ints,4):
    foo += x1 + x2 + x3 + x4

for chunk in chunks(ints,4):
    foo += sum(chunk)

또 다른 방법:

import itertools
def chunks2(iterable,size,filler=None):
    it = itertools.chain(iterable,itertools.repeat(filler,size-1))
    chunk = tuple(itertools.islice(it,size))
    while len(chunk) == size:
        yield chunk
        chunk = tuple(itertools.islice(it,size))

# x2, x3 and x4 could get the value 0 if the length is not
# a multiple of 4.
for x1,x2,x3,x4 in chunks2(ints,4,0):
    foo += x1 + x2 + x3 + x4


답변

from itertools import izip_longest

def chunker(iterable, chunksize, filler):
    return izip_longest(*[iter(iterable)]*chunksize, fillvalue=filler)


답변

이 문제에 대한 이상적인 솔루션은 시퀀스뿐만 아니라 반복자와도 작동합니다. 또한 빠릅니다.

itertools 문서에서 제공하는 솔루션입니다.

def grouper(n, iterable, fillvalue=None):
    #"grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    return itertools.izip_longest(fillvalue=fillvalue, *args)

%timeit내 Mac Book Air에서 ipython을 사용하면 루프 당 47.5 us를 얻습니다.

그러나 결과가 짝수 그룹으로 채워지기 때문에 이것은 실제로 작동하지 않습니다. 패딩이없는 솔루션은 약간 더 복잡합니다. 가장 순진한 해결책은 다음과 같습니다.

def grouper(size, iterable):
    i = iter(iterable)
    while True:
        out = []
        try:
            for _ in range(size):
                out.append(i.next())
        except StopIteration:
            yield out
            break

        yield out

간단하지만 꽤 느리다 : 루프 당 693 us

내가 만들 수있는 가장 좋은 해결책 islice은 내부 루프에 대한 용도 입니다.

def grouper(size, iterable):
    it = iter(iterable)
    while True:
        group = tuple(itertools.islice(it, None, size))
        if not group:
            break
        yield group

동일한 데이터 세트로 루프 당 305 us를 얻습니다.

그보다 더 빠른 솔루션을 얻을 수없는 경우 다음과 같은 솔루션에 중요한 경고를 제공합니다. 입력 데이터에 인스턴스가 filldata있는 경우 잘못된 답변을 얻을 수 있습니다.

def grouper(n, iterable, fillvalue=None):
    #"grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    for i in itertools.izip_longest(fillvalue=fillvalue, *args):
        if tuple(i)[-1] == fillvalue:
            yield tuple(v for v in i if v != fillvalue)
        else:
            yield i

나는이 답변을 정말로 좋아하지 않지만 상당히 빠릅니다. 루프 당 124 us


답변

세트 및 생성기와 함께 작동하는 솔루션이 필요했습니다. 나는 매우 짧고 예쁜 것을 생각 해낼 수 없었지만 적어도 꽤 읽을 수 있습니다.

def chunker(seq, size):
    res = []
    for el in seq:
        res.append(el)
        if len(res) == size:
            yield res
            res = []
    if res:
        yield res

명부:

>>> list(chunker([i for i in range(10)], 3))
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]

세트:

>>> list(chunker(set([i for i in range(10)]), 3))
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]

발전기:

>>> list(chunker((i for i in range(10)), 3))
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]