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


2264

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

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

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

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


1
새 답변을 게시하기 전에이 질문에 대해 이미 60 개 이상의 답변이 있다고 생각하십시오. 귀하의 답변이 기존 답변에 속하지 않은 정보를 제공하는지 확인하십시오.
janniks

임의의 작은 최종 덩어리를 피하려는 사용자의 경우에서 살펴 분할 거의 같은 길이 N의 부분으로 목록
WIM

답변:


3150

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

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

71
목록의 길이를 알 수 없으면 어떻게됩니까? itertools.repeat ([1, 2, 3])에서 시도해보십시오. 예 :
jespern

47
그것은 질문에 대한 흥미로운 확장이지만, 원래 질문은 목록에서 작업하는 것에 대해 분명히 물었습니다.
Ned Batchelder

33
이 함수는 망할 표준 라이브러리에
있어야

6
@Calimo : 당신은 무엇을 제안합니까? 47 개의 요소가있는 목록을 건네줍니다. "균등 한 크기의 덩어리"로 어떻게 나누시겠습니까? OP는 대답을 받아들 였으므로 마지막으로 다른 크기의 청크로 명확하게 확인됩니다. 아마도 영어 문구가 정확하지 않습니까?
Ned Batchelder 2016 년

8
변수의 이름을 l로 지정하지 마십시오. 정확히 1과 같고 혼동됩니다. 사람들이 코드를 복사하고 있으며 이것이 정상이라고 생각합니다.
Yasen

555

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

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


6
또는 (이 특정 함수의 다른 표현을 수행하는 경우) 다음을 통해 람다 함수를 정의 할 수 있습니다. ]. 나는이 목록 이해 방법을 좋아한다!
JP

4
돌아온 후에는 [, not (
alwbtc

2
"Super simple"은 무한 루프를 디버깅 할 필요가 없음을 의미합니다 max().
Bob Stein

이 솔루션에 대한 간단한 것은 없습니다
mit

1
@Nhoj_Gonk 죄송합니다. 무한 루프는 아니지만 chunks (L, 0)은 max ()없이 ValueError를 발생시킵니다. 대신 max ()는 1보다 작은 것을 1로 바꿉니다.
Bob Stein

295

(구) 파이썬 문서 (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


@ninjagecko : list(grouper(3, range(10)))returns [(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, None, None)], 모든 튜플의 길이는 3입니다. 이해할 수 없으므로 의견을 정교하게 작성하십시오. 무엇을 당신은 전화를 할 일이 어떻게 당신은되고 정의합니까 3의 배수 "당신의 일이 3의 배수가 될 것으로 기대"에? 미리 감사드립니다.
tzot

14
생성기 (렌 없음)에서 작동하고 일반적으로 더 빠른 itertools 모듈을 사용하기 때문에 이것을 피봇했습니다.
마이클 딜런

88
itertools단순하고 순진한 순수한 파이썬 구현과 비교할 때 읽을 수없는 슬러지를 밝혀내는 멋진 기능적 접근법 의 전형적인 예
wim

15
이 답변은 파이썬 문서에서 코드 조각으로 시작 감안할 @wim, 난 당신에 문제를 열 좋을 것 bugs.python.org .
tzot

1
@pedrosaurio 경우 l==[1, 2, 3]다음 f(*l)과 동등하다 f(1, 2, 3). 그 질문공식 문서를 참조하십시오 .
tzot

225

나는 이것이 오래되었지만 아무도 언급하지 않았다는 것을 알고있다 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])]

12
이를 통해 청크 당 요소 수가 아닌 총 청크 수를 설정할 수 있습니다.
FizxMike

6
수학을 직접 할 수 있습니다. 10 개의 요소가 있다면 2, 5 개의 요소 덩어리 또는 5 개의 2 개의 요소 덩어리로 그룹화 할 수 있습니다.
Moj

24
+1이 솔루션은 어레이를 균등 한 크기 의 어레이로 나누면서 다른 솔루션은 그렇지 않기 때문에 내가 가장 좋아하는 솔루션입니다. (다른 모든 솔루션에서는 마지막 어레이가 임의로 작을 수 있음)
MiniQuark

@ MiniQuark 그러나 블록 수가 원래 배열 크기의 요소가 아닌 경우 어떻게합니까?
Baldrickk

1
@Baldrickk N 요소를 K 청크로 분할하면 첫 번째 N % K 청크에는 N // K + 1 요소가 있고 나머지에는 N // K 요소가 있습니다. 예를 들어 108 개의 요소를 포함하는 배열을 5 개의 청크로 분할하면 첫 번째 108 % 5 = 3 개의 청크에 108 // 5 + 1 = 22 개의 요소가 포함되고 나머지 청크는 108 // 5 = 21이됩니다. 집단.
MiniQuark

147

나는 놀랄 아무도 사용하여 생각하지 않았다이야 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)]

7
멋진, 당신의 간단한 버전은 내가 가장 좋아하는 것입니다. 다른 사람들도 기본 islice(it, size)표현을 생각해 내고 그것을 루프 구조에 포함 시켰습니다. 오직 당신 만이 iter()(아무것도 알지 못했던) 2 인수 버전을 생각했는데 , 이는 매우 우아하고 (아마도 가장 성능 적으로 효과적입니다). 나는 첫 번째 주장이iter센티넬이 주어 졌을 때 0 인수 함수 변경 . 청크의 (pot. infinite) 반복자를 반환하고 (pot. infinite) 반복자를 입력으로 사용할 수 있으며 len()배열 슬라이스 가 있거나 없습니다 . 대박!
ThomasH

1
그렇기 때문에 나는 최고의 커플을 스캔하는 대신 답을 읽습니다. 필자의 경우 선택적인 패딩이 필요했으며, 두 가지 인수의 iter에 대해서도 배웠습니다.
Kerr

나는 이것을 높이 평가했지만 여전히 과장하지 말자! 우선, 람다는 나빠질 수 있습니다 ( it반복자에 대한 느린 폐쇄 . 둘째, 대부분의 padval
수입

@TomaszGandor, 나는 당신의 첫 번째 포인트를 취할! 내 이해는 lambda가 일반 함수보다 느리지 않다는 것이지만 물론 함수 호출 및 클로저 조회로 인해 속도가 느려질 수 있습니다. izip_longest예를 들어 이것의 상대적인 성능 히트가 접근 방식 과 비교할 수있는 방법을 모르겠습니다. 복잡한 트레이드 오프 일 수 있습니다. 그러나 ... 매개 변수 padval를 제공하는 모든 답변 이 문제를 공유 하지 padval않습니까?
senderle

1
@TomaszGandor, 충분합니다! 그러나이 문제를 해결하는 버전을 만드는 것은 그리 어렵지 않았습니다. (또한 ()센티넬로 사용되는 첫 번째 버전 올바르게 작동합니다. 이것은 비어 있을 때 tuple(islice(it, size))산출량 때문 입니다.)()it
senderle

93

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

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

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

map(None, iter)같습니다 izip_longest(iter).
Thomas Ahle

1
@TomaszWysocki *반복자 튜플 앞에서 설명해 주 시겠습니까? 아마도 귀하의 답변 텍스트에는 있지만 *이전에는 Python에서 그 방식 을 사용하는 것을 보았습니다 . 감사!
theJollySin

1
@theJollySin이 문맥에서는 이것을 splat 연산자라고합니다. 사용법은 여기에 설명되어 있습니다 -stackoverflow.com/questions/5917522/unzipping-and-theator .
rlms

2
마지막 청크에는 닫을 요소가 없습니다. 이것은 결함 일 수도 있고 아닐 수도 있습니다. 그래도 정말 멋진 패턴입니다.

49

단순하면서도 우아함

l = range(1, 1000)
print [l[x:x+10] for x in xrange(0, len(l), 10)]

또는 원하는 경우 :

def chunks(l, n): return [l[x: x+n] for x in xrange(0, len(l), n)]
chunks(l, 10)

18
아라비아 숫자처럼 변수를 더빙해서는 안됩니다. 일부 글꼴에서 1l구별 할 수 있습니다. 과 같습니다 0O. 그리고 때로는 I1.
Alfe

14
@Alfe 결함 글꼴. 사람들은 그런 글꼴을 사용해서는 안됩니다. 프로그래밍이 아니라 아무것도 아닙니다 .
Jerry B

17
Lambdas는 명명되지 않은 함수로 사용됩니다. 그런 식으로 사용하는 데는 아무런 의미가 없습니다. 또한 오류가 발생할 경우 역 추적에서 "청크 단위"대신 "in <lambda>"를보고하므로 디버깅이 더 어려워집니다. 이 모든 것들이 있다면 문제를 찾아
내길 바랍니다.

1
이것은 0에서 1이 아닌 내부 xrange되어야print [l[x:x+10] for x in xrange(1, len(l), 10)]
scottydelta

참고 : Python 3 사용자의 경우을 사용하십시오 range.
Christian Dean

40

다른 답변에 대한 비판 :

이 답변 중 어느 것도 고른 크기의 청크가 아니며 끝 부분에 런트 청크가 남으므로 완전히 균형이 맞지 않습니다. 이러한 기능을 사용하여 작업을 배포하는 경우 다른 작업보다 먼저 마무리 작업이 완료 될 수 있으므로 다른 작업을 계속하는 동안 아무 작업도 수행하지 않아도됩니다.

예를 들어, 현재 최상위 답변은 다음과 같이 끝납니다.

[60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
[70, 71, 72, 73, 74]]

나는 결국 그 런트를 싫어한다!

다른 사람 같은 list(grouper(3, xrange(7))), 그리고 chunk(xrange(7), 3)모두 반환 : [(0, 1, 2), (3, 4, 5), (6, None, None)]. 의 None의견은 단지 패딩이며 오히려 우아하지 않습니다. 그들은 iterables를 고르게 청크하지 않습니다.

왜 더 잘 나눌 수 없습니까?

내 솔루션

여기에 내가 (교체 파이썬 3 주 생산에 사용했던 함수에서 적응 균형 잡힌 솔루션의 xrange와 함께 range) :

def baskets_from(items, maxbaskets=25):
    baskets = [[] for _ in xrange(maxbaskets)] # in Python 3 use range
    for i, item in enumerate(items):
        baskets[i % maxbaskets].append(item)
    return filter(None, baskets) 

그리고 목록에 넣으면 똑같은 발전기를 만들었습니다.

def iter_baskets_from(items, maxbaskets=3):
    '''generates evenly balanced baskets from indexable iterable'''
    item_count = len(items)
    baskets = min(item_count, maxbaskets)
    for x_i in xrange(baskets):
        yield [items[y_i] for y_i in xrange(x_i, item_count, baskets)]

마지막으로 위의 모든 함수가 주어진 순서대로 요소를 연속적인 순서로 반환한다는 것을 알았습니다.

def iter_baskets_contiguous(items, maxbaskets=3, item_count=None):
    '''
    generates balanced baskets from iterable, contiguous contents
    provide item_count if providing a iterator that doesn't support len()
    '''
    item_count = item_count or len(items)
    baskets = min(item_count, maxbaskets)
    items = iter(items)
    floor = item_count // baskets 
    ceiling = floor + 1
    stepdown = item_count % baskets
    for x_i in xrange(baskets):
        length = ceiling if x_i < stepdown else floor
        yield [items.next() for _ in xrange(length)]

산출

테스트하려면 :

print(baskets_from(xrange(6), 8))
print(list(iter_baskets_from(xrange(6), 8)))
print(list(iter_baskets_contiguous(xrange(6), 8)))
print(baskets_from(xrange(22), 8))
print(list(iter_baskets_from(xrange(22), 8)))
print(list(iter_baskets_contiguous(xrange(22), 8)))
print(baskets_from('ABCDEFG', 3))
print(list(iter_baskets_from('ABCDEFG', 3)))
print(list(iter_baskets_contiguous('ABCDEFG', 3)))
print(baskets_from(xrange(26), 5))
print(list(iter_baskets_from(xrange(26), 5)))
print(list(iter_baskets_contiguous(xrange(26), 5)))

출력되는 내용 :

[[0], [1], [2], [3], [4], [5]]
[[0], [1], [2], [3], [4], [5]]
[[0], [1], [2], [3], [4], [5]]
[[0, 8, 16], [1, 9, 17], [2, 10, 18], [3, 11, 19], [4, 12, 20], [5, 13, 21], [6, 14], [7, 15]]
[[0, 8, 16], [1, 9, 17], [2, 10, 18], [3, 11, 19], [4, 12, 20], [5, 13, 21], [6, 14], [7, 15]]
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11], [12, 13, 14], [15, 16, 17], [18, 19], [20, 21]]
[['A', 'D', 'G'], ['B', 'E'], ['C', 'F']]
[['A', 'D', 'G'], ['B', 'E'], ['C', 'F']]
[['A', 'B', 'C'], ['D', 'E'], ['F', 'G']]
[[0, 5, 10, 15, 20, 25], [1, 6, 11, 16, 21], [2, 7, 12, 17, 22], [3, 8, 13, 18, 23], [4, 9, 14, 19, 24]]
[[0, 5, 10, 15, 20, 25], [1, 6, 11, 16, 21], [2, 7, 12, 17, 22], [3, 8, 13, 18, 23], [4, 9, 14, 19, 24]]
[[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]]

연속 생성기는 다른 두 패턴과 동일한 길이 패턴으로 청크를 제공하지만 항목은 모두 순서대로 나열되며 개별 요소 목록을 분할 할 수있는만큼 균일하게 분할됩니다.


위의 어느 것도 균일 한 크기의 청크를 제공하지 않습니다. 그러나 이 하나 처럼, 수행 이 하나 .
senderle

1
@senderle, 첫 번째 list(grouper(3, xrange(7))), chunk(xrange(7), 3)두 번째는 모두 다음을 반환 [(0, 1, 2), (3, 4, 5), (6, None, None)]합니다. 의 None의견은 단지 패딩이며 오히려 우아하지 않습니다. 그들은 iterables를 고르게 청크하지 않습니다. 투표 해 주셔서 감사합니다!
Aaron Hall

4
동일한 크기의 청크 (가능한 경우 마지막을 제외하고) 또는 균형 잡힌 (가능한 한 좋은) 결과가 더 자주 필요한지 여부에 대한 질문을 명시 적으로하지 않고 여기서 지금 수행합니다. 균형 솔루션이 선호된다고 가정합니다. 만약 당신이 프로그래밍 한 것이 실제 세계에 가깝다면 (예를 들어, 시뮬레이션 된 카드 게임을위한 카드 처리 알고리즘) 이것은 사실 일 수 있습니다. 다른 경우 (단어로 단어를 채우는 것과 같이) 가능한 한 줄을 가득 채우는 것이 좋습니다. 그래서 나는 서로를 정말로 선호 할 수 없습니다. 그들은 단지 다른 사용 사례를위한 것입니다.
Alfe

@ ChristopherBarrington-Leigh 좋은 점은 DataFrames의 경우 슬라이스를 사용해야 할 것입니다. DataFrame 객체는 일반적으로 슬라이싱시 복사되지 않는다고 생각합니다.import pandas as pd; [pd.DataFrame(np.arange(7))[i::3] for i in xrange(3)]
Aaron Hall

1
@AaronHall 죄송합니다. 내 비평을 두 번 추측했기 때문에 내 의견을 삭제했지만 추첨에 빠르다. 감사! 사실, 데이터 프레임에서 작동하지 않는다는 주장은 사실입니다. items가 데이터 프레임 인 경우 yield items [range (x_i, item_count, baskets)]를 마지막 줄로 사용하십시오. 원하는 (최소) 그룹 크기를 지정하는 별도의 (아직) 답변을 제공했습니다.
CPBL

38

이 질문 의 복제본 에서 가장 멋진 Python-ish 답변을 보았습니다 .

from itertools import zip_longest

a = range(1, 16)
i = iter(a)
r = list(zip_longest(i, i, i))
>>> print(r)
[(1, 2, 3), (4, 5, 6), (7, 8, 9), (10, 11, 12), (13, 14, 15)]

모든 n에 대해 n- 튜플을 만들 수 있습니다. 인 경우 a = range(1, 15)결과는 다음과 같습니다.

[(1, 2, 3), (4, 5, 6), (7, 8, 9), (10, 11, 12), (13, 14, None)]

목록이 균등하게 나누어 경우에, 당신은 대체 할 수 zip_longestzip달리 삼중가 (13, 14, None)손실 될 것입니다. 파이썬 3이 위에서 사용되었습니다. Python 2의 경우을 사용하십시오 izip_longest.


목록과 청크가 짧으면 목록을 1000 청크로 분할하기 위해 어떻게 조정할 수 있습니까? 당신은 "zip (i, i, i, i, i, i, i, i, i, i ..... i = 1000)을 코딩하지 않을 것입니다.
Tom Smith

9
zip(i, i, i, ... i)zip ()에 "chunk_size"인수를 사용 zip(*[i]*chunk_size)하면 물론 좋은 아이디어인지 아닌지 로 쓸 수 있습니다 .
Wilson F

1
이것의 단점은 균등하게 나누지 않으면 zip이 가장 짧은 반복 가능한 지점에서 멈추기 때문에 요소를 삭제한다는 것입니다. & izip_longest는 기본 요소를 추가합니다.
Aaron Hall

zip_longest:에 다 같이 사용되어야한다 stackoverflow.com/a/434411/1959808
요안 Filippidis을

에 15 range(1, 15)개의 요소가 range(1, 15)아닌 14 개의 요소가 있기 때문에 대답 은 이미 누락되었습니다 .
Ioannis Filippidis

35

목록 크기를 알고있는 경우 :

def SplitList(mylist, chunk_size):
    return [mylist[offs:offs+chunk_size] for offs in range(0, len(mylist), chunk_size)]

그렇지 않으면 (반복자) :

def IterChunks(sequence, chunk_size):
    res = []
    for item in sequence:
        res.append(item)
        if len(res) >= chunk_size:
            yield res
            res = []
    if res:
        yield res  # yield the last, incomplete, portion

후자의 경우, 시퀀스에 항상 주어진 크기의 전체 청크 수가 포함되어 있는지 (즉, 불완전한 마지막 청크가 없음) 확신 할 수 있으면보다 아름다운 방식으로 다시 표현할 수 있습니다.


나는 이것이 지금까지 묻혀서 슬프다. IterChunks는 모든 것을 위해 작동하며 일반적인 해결책이며 내가 알고있는주의 사항이 없습니다.
Jason Dunkelberger

18

툴들은의 라이브러리는이 partition이것에 대한 기능을 :

from toolz.itertoolz.core import partition

list(partition(2, [1, 2, 3, 4]))
[(1, 2), (3, 4)]

이것은 모든 제안 중 가장 간단한 것 같습니다. 나는 그러한 파티션 기능을 얻기 위해 타사 라이브러리를 사용해야한다는 것이 실제로 사실인지 궁금합니다. 해당 파티션 함수와 동등한 것이 내장 언어로 존재할 것으로 예상했을 것입니다.
카스퍼 드

1
itertools로 파티션을 만들 수 있습니다. 그러나 나는 toolz 라이브러리를 좋아한다. 기능적인 스타일로 컬렉션 작업을위한 클로저에서 영감을 얻은 라이브러리입니다. 불변성을 얻지 못하지만 간단한 컬렉션 작업을위한 작은 어휘를 얻습니다. 또한 cytoolz는 cython으로 작성되었으며 성능이 크게 향상되었습니다. github.com/pytoolz/cytoolz matthewrocklin.com/blog/work/2014/05/01/Introducing-CyToolz
zach

zach의 주석 링크는 후행 슬래시를 생략
mit


16

나는 tzot과 JFSebastian이 제안한 Python doc의 버전을 좋아하지만 두 가지 단점이 있습니다.

  • 그것은 명백하지 않다
  • 나는 보통 마지막 덩어리에 채우기 값을 원하지 않는다

내 코드에서 이것을 많이 사용하고 있습니다.

from itertools import islice

def chunks(n, iterable):
    iterable = iter(iterable)
    while True:
        yield tuple(islice(iterable, n)) or iterable.next()

업데이트 : 게으른 청크 버전 :

from itertools import chain, islice

def chunks(n, iterable):
   iterable = iter(iterable)
   while True:
       yield chain([next(iterable)], islice(iterable, n-1))

while True루프 의 브레이크 조건은 무엇입니까 ?
wjandrea

@wjandrea : 가 비어 있고 실행될 StopIteration때 발생합니다 . 그러나 현대 파이썬에서는 발생기를 종료 하지 않고 생성기를 종료 해야하는 곳에서는 제대로 작동하지 않습니다 . A는 전체 루프 (변화하는 주위 에 교차 버전의 compat 위해) 적어도 최소의 오버 헤드를 해결이. tupleiterable.next()returnStopIterationtry/except StopIteration: returniterable.next()next(iterable)
ShadowRanger

15
[AA[i:i+SS] for i in range(len(AA))[::SS]]

AA가 배열 인 경우 SS는 청크 크기입니다. 예를 들면 다음과 같습니다.

>>> AA=range(10,21);SS=3
>>> [AA[i:i+SS] for i in range(len(AA))[::SS]]
[[10, 11, 12], [13, 14, 15], [16, 17, 18], [19, 20]]
# or [range(10, 13), range(13, 16), range(16, 19), range(19, 21)] in py3

2
최고이며 간단합니다.
F.Tamy

2
짧고 간단합니다. 복잡성에 대한 단순성.
dkrynicki

15

다른 접근 방식의 성능에 대해 궁금했고 여기에 있습니다.

Python 3.5.1에서 테스트

import time
batch_size = 7
arr_len = 298937

#---------slice-------------

print("\r\nslice")
start = time.time()
arr = [i for i in range(0, arr_len)]
while True:
    if not arr:
        break

    tmp = arr[0:batch_size]
    arr = arr[batch_size:-1]
print(time.time() - start)

#-----------index-----------

print("\r\nindex")
arr = [i for i in range(0, arr_len)]
start = time.time()
for i in range(0, round(len(arr) / batch_size + 1)):
    tmp = arr[batch_size * i : batch_size * (i + 1)]
print(time.time() - start)

#----------batches 1------------

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

print("\r\nbatches 1")
arr = [i for i in range(0, arr_len)]
start = time.time()
for x in batch(arr, batch_size):
    tmp = x
print(time.time() - start)

#----------batches 2------------

from itertools import islice, chain

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


print("\r\nbatches 2")
arr = [i for i in range(0, arr_len)]
start = time.time()
for x in batch(arr, batch_size):
    tmp = x
print(time.time() - start)

#---------chunks-------------
def chunks(l, n):
    """Yield successive n-sized chunks from l."""
    for i in range(0, len(l), n):
        yield l[i:i + n]
print("\r\nchunks")
arr = [i for i in range(0, arr_len)]
start = time.time()
for x in chunks(arr, batch_size):
    tmp = x
print(time.time() - start)

#-----------grouper-----------

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

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

arr = [i for i in range(0, arr_len)]
print("\r\ngrouper")
start = time.time()
for x in grouper(arr, batch_size):
    tmp = x
print(time.time() - start)

결과 :

slice
31.18285083770752

index
0.02184295654296875

batches 1
0.03503894805908203

batches 2
0.22681021690368652

chunks
0.019841909408569336

grouper
0.006506919860839844

3
모듈을 time사용할 때 라이브러리를 사용한 벤치마킹 은 좋은 생각이 아닙니다timeit
Azat Ibrakov

13

암호:

def split_list(the_list, chunk_size):
    result_list = []
    while the_list:
        result_list.append(the_list[:chunk_size])
        the_list = the_list[chunk_size:]
    return result_list

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

print split_list(a_list, 3)

결과:

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

12

라이브러리의 get_chunks기능을 다음 utilspie과 같이 사용할 수도 있습니다 .

>>> from utilspie import iterutils
>>> a = [1, 2, 3, 4, 5, 6, 7, 8, 9]

>>> list(iterutils.get_chunks(a, 5))
[[1, 2, 3, 4, 5], [6, 7, 8, 9]]

utilspiepip를 통해 설치할 수 있습니다 :

sudo pip install utilspie

면책 조항 : 나는 utilspie library 의 제작자입니다 .


11

이 시점에서 우리는 재귀 발생기 가 필요하다고 생각합니다 .

파이썬 2에서 :

def chunks(li, n):
    if li == []:
        return
    yield li[:n]
    for e in chunks(li[n:], n):
        yield e

파이썬 3에서 :

def chunks(li, n):
    if li == []:
        return
    yield li[:n]
    yield from chunks(li[n:], n)

또한 대규모 외계인 침공의 경우 장식 된 재귀 생성기 가 유용 할 수 있습니다.

def dec(gen):
    def new_gen(li, n):
        for e in gen(li, n):
            if e == []:
                return
            yield e
    return new_gen

@dec
def chunks(li, n):
    yield li[:n]
    for e in chunks(li[n:], n):
        yield e

9

Python 3.8의 Assignment Expressions 를 사용하면 꽤 좋아집니다.

import itertools

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

이것은 목록뿐만 아니라 임의의 반복 가능에서 작동합니다.

>>> import pprint
>>> pprint.pprint(list(batch(range(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]]

1
이제 이것은이 질문에 대한 가치있는 새로운 답변입니다. 나는 실제로 이것을 아주 좋아합니다. 나는 과제 표현에 회의적이지만 그들이 일할 때 효과가 있습니다.
juanpa.arrivillaga

7

허, 한 줄 버전

In [48]: chunk = lambda ulist, step:  map(lambda i: ulist[i:i+step],  xrange(0, len(ulist), step))

In [49]: chunk(range(1,100), 10)
Out[49]: 
[[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, 75, 76, 77, 78, 79, 80],
 [81, 82, 83, 84, 85, 86, 87, 88, 89, 90],
 [91, 92, 93, 94, 95, 96, 97, 98, 99]]

36
"chunk = lambda"대신 "def chunk"를 사용하십시오. 동일하게 작동합니다. 한 줄. 동일한 기능. n00bz가 읽고 이해하기가 훨씬 쉽습니다.
S.Lott

4
@ S.Lott : n00bz가 scheme : P에서 나온 것이 아니라면 이것은 실제 문제가 아닙니다. 구글 키워드도 있습니다! n00bz를 위해 피해야 할 다른 기능은 무엇입니까? 나는 수확량이 n00b 친화적 일 정도로 필수적 / c와 같지 않다고 생각합니다.
Janus Troelsen

16
def chunk대신에 함수 객체 chunk=lambda에 '<lambda>'대신에 .__ name__ 속성 ​​'chunk'가 있습니다. 특정 이름은 역 추적에 더 유용합니다.
Terry Jan Reedy 2018 년

1
@Alfe : 주요 의미 론적 차이라고 부를 수 있는지 확실하지 않지만 대신 역 추적에 유용한 이름이 있는지 여부 <lamba>는 눈에 띄는 차이입니다.
martineau

1
성능을 위해 여러 가지를 테스트 한 후에는 훌륭합니다!
Sunny Patel

7
def split_seq(seq, num_pieces):
    start = 0
    for i in xrange(num_pieces):
        stop = start + len(seq[i::num_pieces])
        yield seq[start:stop]
        start = stop

용법:

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

for seq in split_seq(seq, 3):
    print seq

7

더 명백한 다른 버전.

def chunkList(initialList, chunkSize):
    """
    This function chunks a list into sub lists 
    that have a length equals to chunkSize.

    Example:
    lst = [3, 4, 9, 7, 1, 1, 2, 3]
    print(chunkList(lst, 3)) 
    returns
    [[3, 4, 9], [7, 1, 1], [2, 3]]
    """
    finalList = []
    for i in range(0, len(initialList), chunkSize):
        finalList.append(initialList[i:i+chunkSize])
    return finalList

(2016 년 9 월 12 일)이 답변은 가장 독립적 인 언어이며 읽기 쉬운 것입니다.
D Adams

7

큰 목록에 좋은 len ()을 호출하지 않으면 :

def splitter(l, n):
    i = 0
    chunk = l[:n]
    while chunk:
        yield chunk
        i += n
        chunk = l[i:i+n]

그리고 이것은 iterables를위한 것입니다 :

def isplitter(l, n):
    l = iter(l)
    chunk = list(islice(l, n))
    while chunk:
        yield chunk
        chunk = list(islice(l, n))

위의 기능적 풍미 :

def isplitter2(l, n):
    return takewhile(bool,
                     (tuple(islice(start, n))
                            for start in repeat(iter(l))))

또는:

def chunks_gen_sentinel(n, seq):
    continuous_slices = imap(islice, repeat(iter(seq)), repeat(0), repeat(n))
    return iter(imap(tuple, continuous_slices).next,())

또는:

def chunks_gen_filter(n, seq):
    continuous_slices = imap(islice, repeat(iter(seq)), repeat(0), repeat(n))
    return takewhile(bool,imap(tuple, continuous_slices))

16
len()큰 목록 을 피할 이유가 없습니다 . 일정한 시간 작업입니다.
토마스 Wouters

7

추가 접근 방식의 목록은 다음과 같습니다.

주어진

import itertools as it
import collections as ct

import more_itertools as mit


iterable = range(11)
n = 3

암호

표준 라이브러리

list(it.zip_longest(*[iter(iterable)] * n))
# [(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, None)]

d = {}
for i, x in enumerate(iterable):
    d.setdefault(i//n, []).append(x)

list(d.values())
# [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10]]

dd = ct.defaultdict(list)
for i, x in enumerate(iterable):
    dd[i//n].append(x)

list(dd.values())
# [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10]]

more_itertools+

list(mit.chunked(iterable, n))
# [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10]]

list(mit.sliced(iterable, n))
# [range(0, 3), range(3, 6), range(6, 9), range(9, 11)]

list(mit.grouper(n, iterable))
# [(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, None)]

list(mit.windowed(iterable, len(iterable)//n, step=n))
# [(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, None)]

참고 문헌

+ itertools 레시피 등 을 구현하는 타사 라이브러리 .> pip install more_itertools


6

참조 이 참조를

>>> orange = range(1, 1001)
>>> otuples = list( zip(*[iter(orange)]*10))
>>> print(otuples)
[(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), ... (991, 992, 993, 994, 995, 996, 997, 998, 999, 1000)]
>>> olist = [list(i) for i in otuples]
>>> print(olist)
[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], ..., [991, 992, 993, 994, 995, 996, 997, 998, 999, 1000]]
>>> 

파이썬 3


3
멋지지만 크기가 청크의 정수와 일치하지 않으면 끝에 요소를 삭제합니다. 예를 들어 입력을 zip(*[iter(range(7))]*3)반환 [(0, 1, 2), (3, 4, 5)]하고 잊어 버립니다 6.
Alfe

6

여기 모두가 반복자에 대해 이야기하기 때문에. boltons이를위한 완벽한 방법이 iterutils.chunked_iter있습니다.

from boltons import iterutils

list(iterutils.chunked_iter(list(range(50)), 11))

산출:

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

그러나 메모리에 자비를 원하지 않으면 구식을 사용 list하여 처음부터 전체 를 저장할 수 있습니다 iterutils.chunked.


그리고 이것은 실제로 하나의 서브 반복자를 보는 순서에 관계없이 작동합니다!
Peter Gerdes

6

하나 더 솔루션

def make_chunks(data, chunk_size): 
    while data:
        chunk, data = data[:chunk_size], data[chunk_size:]
        yield chunk

>>> for chunk in make_chunks([1, 2, 3, 4, 5, 6, 7], 2):
...     print chunk
... 
[1, 2]
[3, 4]
[5, 6]
[7]
>>> 

5
def chunks(iterable,n):
    """assumes n is an integer>0
    """
    iterable=iter(iterable)
    while True:
        result=[]
        for i in range(n):
            try:
                a=next(iterable)
            except StopIteration:
                break
            else:
                result.append(a)
        if result:
            yield result
        else:
            break

g1=(i*i for i in range(10))
g2=chunks(g1,3)
print g2
'<generator object chunks at 0x0337B9B8>'
print list(g2)
'[[0, 1, 4], [9, 16, 25], [36, 49, 64], [81]]'

1
이것은 itertools 기반의 응답만큼 짧거나 예쁘지 않을 수도 있지만 첫 번째에 액세스하기 전에 두 번째 하위 목록을 인쇄하려는 경우 실제로 작동합니다. i0 = next (g2); i1 = 다음 (g2); i0을 사용하기 전에 i1을 사용하면 깨지지 않습니다!
Peter Gerdes

5

matplotlib.cbook 조각 사용을 고려하십시오

예를 들면 다음과 같습니다.

import matplotlib.cbook as cbook
segments = cbook.pieces(np.arange(20), 3)
for s in segments:
     print s

실수로 두 개의 계정을 만든 것 같습니다. 당신은 할 수 있습니다 팀에 문의 그들이 당신이 당신의 기여에 직접 편집 권한을 회복 할 수있는 통합하도록.
Georgy
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.