[python] 리스트를 어떻게 고른 크기의 덩어리로 나누나요?

나는 임의의 길이의 목록을 가지고 있으며, 그것을 동일한 크기의 덩어리로 나누고 작동해야합니다. 카운터와 두 개의 목록을 유지하는 것과 같은 명백한 방법이 있습니다. 두 번째 목록이 채워지면 첫 번째 목록에 추가하고 다음 데이터 라운드를 위해 두 번째 목록을 비 웁니다. 그러나 이것은 잠재적으로 매우 비쌉니다.

누구든지 길이가 긴 목록 (예 : 생성기 사용)에 대해 이것에 대한 좋은 해결책이 있는지 궁금합니다.

유용한 무언가를 찾고 itertools있었지만 분명히 유용한 것을 찾을 수 없었습니다. 그래도 놓쳤을 수도 있습니다.

관련 질문 : 청크 목록을 반복하는 가장 “파이썬”방법은 무엇입니까?



답변

다음은 원하는 청크를 생성하는 생성기입니다.

def chunks(lst, n):
    """Yield successive n-sized chunks from lst."""
    for i in range(0, len(lst), n):
        yield lst[i:i + n]

import pprint
pprint.pprint(list(chunks(range(10, 75), 10)))
[[10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
 [20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
 [30, 31, 32, 33, 34, 35, 36, 37, 38, 39],
 [40, 41, 42, 43, 44, 45, 46, 47, 48, 49],
 [50, 51, 52, 53, 54, 55, 56, 57, 58, 59],
 [60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
 [70, 71, 72, 73, 74]]

Python 2를 사용 xrange()하는 경우 대신 다음을 사용해야 합니다 range().

def chunks(lst, n):
    """Yield successive n-sized chunks from lst."""
    for i in xrange(0, len(lst), n):
        yield lst[i:i + n]

또한 함수를 작성하는 대신 간단히 목록 이해를 사용할 수 있지만 코드를 이해하기 쉽도록 명명 된 함수에 이와 같은 연산을 캡슐화하는 것이 좋습니다. 파이썬 3 :

[lst[i:i + n] for i in range(0, len(lst), n)]

파이썬 2 버전 :

[lst[i:i + n] for i in xrange(0, len(lst), n)]


답변

아주 간단한 것을 원한다면 :

def chunks(l, n):
    n = max(1, n)
    return (l[i:i+n] for i in range(0, len(l), n))

Python 2.x의 경우 xrange()대신 사용range()


답변

(구) 파이썬 문서 (itertools에 대한 레시피)에서 직접 :

from itertools import izip, chain, repeat

def grouper(n, iterable, padvalue=None):
    "grouper(3, 'abcdefg', 'x') --> ('a','b','c'), ('d','e','f'), ('g','x','x')"
    return izip(*[chain(iterable, repeat(padvalue, n-1))]*n)

JFSebastian이 제안한 현재 버전 :

#from itertools import izip_longest as zip_longest # for Python 2.x
from itertools import zip_longest # for Python 3.x
#from six.moves import zip_longest # for both (uses the six compat library)

def grouper(n, iterable, padvalue=None):
    "grouper(3, 'abcdefg', 'x') --> ('a','b','c'), ('d','e','f'), ('g','x','x')"
    return zip_longest(*[iter(iterable)]*n, fillvalue=padvalue)

귀도의 타임머신 작업 (작동했거나 작동했을 것임)이 다시 작동 한 것 같습니다.

이러한 솔루션 은 목록에서 반복되는 하나의 반복자를 [iter(iterable)]*n작성 하기 때문에 작동합니다 (또는 이전 버전과 동일) . 그런 다음 효과적으로 “각”반복자의 라운드 로빈을 수행합니다. 이것은 동일한 반복자이므로 각 호출에 의해 진행되어 이러한 zip-roundrobin이 하나의 튜플 항목을 생성 합니다.nizip_longestn


답변

나는 이것이 오래되었지만 아무도 언급하지 않았다는 것을 알고있다 numpy.array_split.

import numpy as np

lst = range(50)
np.array_split(lst, 5)
# [array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),
#  array([10, 11, 12, 13, 14, 15, 16, 17, 18, 19]),
#  array([20, 21, 22, 23, 24, 25, 26, 27, 28, 29]),
#  array([30, 31, 32, 33, 34, 35, 36, 37, 38, 39]),
#  array([40, 41, 42, 43, 44, 45, 46, 47, 48, 49])]


답변

나는 놀랄 아무도 사용하여 생각하지 않았다이야 iter이야 ‘ 2 개의 인수를 양식 :

from itertools import islice

def chunk(it, size):
    it = iter(it)
    return iter(lambda: tuple(islice(it, size)), ())

데모:

>>> list(chunk(range(14), 3))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13)]

이것은 모든 iterable과 작동하며 느리게 출력됩니다. 반복자보다는 튜플을 반환하지만 그럼에도 불구하고 특정 우아함이 있다고 생각합니다. 또한 패드가 없습니다. 패딩을 원한다면 위의 간단한 변형으로 충분합니다.

from itertools import islice, chain, repeat

def chunk_pad(it, size, padval=None):
    it = chain(iter(it), repeat(padval))
    return iter(lambda: tuple(islice(it, size)), (padval,) * size)

데모:

>>> list(chunk_pad(range(14), 3))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, None)]
>>> list(chunk_pad(range(14), 3, 'a'))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 'a')]

izip_longest기반 솔루션 과 마찬가지로 위의 내용은 항상 채워집니다. 내가 아는 한, 선택적으로 패딩 하는 함수에 대한 한 줄 또는 두 줄의 itertools 레시피는 없습니다 . 위의 두 가지 접근 방식을 결합하면이 접근 방식이 매우 가깝습니다.

_no_padding = object()

def chunk(it, size, padval=_no_padding):
    if padval == _no_padding:
        it = iter(it)
        sentinel = ()
    else:
        it = chain(iter(it), repeat(padval))
        sentinel = (padval,) * size
    return iter(lambda: tuple(islice(it, size)), sentinel)

데모:

>>> list(chunk(range(14), 3))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13)]
>>> list(chunk(range(14), 3, None))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, None)]
>>> list(chunk(range(14), 3, 'a'))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 'a')]

나는 이것이 선택적 패딩을 제공하는 가장 짧은 청커 제안이라고 생각합니다.

Tomasz Gandor가 관찰 한 바와 같이 , 2 개의 패딩 청커는 긴 일련의 패드 값을 만나면 예기치 않게 멈출 것입니다. 다음은 합리적으로 해당 문제를 해결하는 최종 변형입니다.

_no_padding = object()
def chunk(it, size, padval=_no_padding):
    it = iter(it)
    chunker = iter(lambda: tuple(islice(it, size)), ())
    if padval == _no_padding:
        yield from chunker
    else:
        for ch in chunker:
            yield ch if len(ch) == size else ch + (padval,) * (size - len(ch))

데모:

>>> list(chunk([1, 2, (), (), 5], 2))
[(1, 2), ((), ()), (5,)]
>>> list(chunk([1, 2, None, None, 5], 2, None))
[(1, 2), (None, None), (5, None)]


답변

다음은 임의의 이터 러블에서 작동하는 생성기입니다.

def split_seq(iterable, size):
    it = iter(iterable)
    item = list(itertools.islice(it, size))
    while item:
        yield item
        item = list(itertools.islice(it, size))

예:

>>> import pprint
>>> pprint.pprint(list(split_seq(xrange(75), 10)))
[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
 [10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
 [20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
 [30, 31, 32, 33, 34, 35, 36, 37, 38, 39],
 [40, 41, 42, 43, 44, 45, 46, 47, 48, 49],
 [50, 51, 52, 53, 54, 55, 56, 57, 58, 59],
 [60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
 [70, 71, 72, 73, 74]]


답변

def chunk(input, size):
    return map(None, *([iter(input)] * size))