[python] iterable을 일정한 크기의 청크로 분할하는 방법

중복 가능성 :
Python에서 목록을 균등 한 크기의 청크로 어떻게 분할합니까?

이터 러블을 입력으로 받아 이터 러블의 이터 러블을 반환하는 “배치”함수를 찾을 수 없다는 것에 놀랐습니다.

예를 들면 :

for i in batch(range(0,10), 1): print i
[0]
[1]
...
[9]

또는:

for i in batch(range(0,10), 3): print i
[0,1,2]
[3,4,5]
[6,7,8]
[9]

이제 저는 매우 간단한 생성기라고 생각한 것을 작성했습니다.

def batch(iterable, n = 1):
   current_batch = []
   for item in iterable:
       current_batch.append(item)
       if len(current_batch) == n:
           yield current_batch
           current_batch = []
   if current_batch:
       yield current_batch

그러나 위의 내용은 내가 기대했던 것을 제공하지 않습니다.

for x in   batch(range(0,10),3): print x
[0]
[0, 1]
[0, 1, 2]
[3]
[3, 4]
[3, 4, 5]
[6]
[6, 7]
[6, 7, 8]
[9]

그래서 나는 무언가를 놓 쳤고 이것은 아마도 파이썬 생성기에 대한 나의 완전한 이해 부족을 보여줄 것입니다. 누구든지 나를 올바른 방향으로 안내해 줄 수 있습니까?

[편집 : 결국 위의 동작은 파이썬 자체가 아닌 ipython 내에서 실행할 때만 발생한다는 것을 깨달았습니다.]



답변

이것은 아마도 더 효율적일 것입니다 (더 빠름).

def batch(iterable, n=1):
    l = len(iterable)
    for ndx in range(0, l, n):
        yield iterable[ndx:min(ndx + n, l)]

for x in batch(range(0, 10), 3):
    print x

목록을 사용한 예

data = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # list of data 

for x in batch(data, 3):
    print(x)

# Output

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

새 목록 작성을 방지합니다.


답변

FWIW, itertools 모듈레시피 는 다음 예제를 제공합니다.

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

다음과 같이 작동합니다.

>>> list(grouper(3, range(10)))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, None, None)]


답변

다른 사람들이 언급했듯이 귀하가 제공 한 코드는 귀하가 원하는 것을 정확히 수행합니다. 사용하는 다른 접근 방식 의 경우 다음 레시피 itertools.islice 를 볼 수 있습니다 .

from itertools import islice, chain

def batch(iterable, size):
    sourceiter = iter(iterable)
    while True:
        batchiter = islice(sourceiter, size)
        yield chain([batchiter.next()], batchiter)


답변

나는 단지 하나의 대답을 주었다. 그러나 이제는 새로운 기능을 작성하지 않는 것이 최선의 해결책이라고 생각합니다. More-itertools 에는 많은 추가 도구가 포함되어 chunked있으며 그 중 하나입니다.


답변

이상하다, Python 2.x에서 잘 작동하는 것 같습니다.

>>> def batch(iterable, n = 1):
...    current_batch = []
...    for item in iterable:
...        current_batch.append(item)
...        if len(current_batch) == n:
...            yield current_batch
...            current_batch = []
...    if current_batch:
...        yield current_batch
...
>>> for x in batch(range(0, 10), 3):
...     print x
...
[0, 1, 2]
[3, 4, 5]
[6, 7, 8]
[9]


답변

이것은 lenPython 2와 3 모두에서 사용하지 않고 작동 하는 매우 짧은 코드 스 니펫입니다 (내 생성이 아님).

def chunks(iterable, size):
    from itertools import chain, islice
    iterator = iter(iterable)
    for first in iterator:
        yield list(chain([first], islice(iterator, size - 1)))


답변

len함수를 정의하지 않는 이터 러블을 사용하고 지쳐 있는 경우 Python 3.8에 대한 솔루션 :

def batcher(iterable, batch_size):
    while batch := list(islice(iterable, batch_size)):
        yield batch

사용 예 :

def my_gen():
    yield from range(10)

for batch in batcher(my_gen(), 3):
    print(batch)

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

물론 해마 운영자 없이도 구현할 수 있습니다.