2795

任意の長さのリストがあり、それを同じサイズのチャンクに分割して操作する必要があります。カウンターと 2 つのリストを保持し、2 番目のリストがいっぱいになったら最初のリストに追加し、次のラウンドのデータのために 2 番目のリストを空にするなど、これを行ういくつかの明白な方法がありますが、これは非常にコストがかかる可能性があります。

ジェネレータを使用するなど、任意の長さのリストに対して、誰かがこれに対する良い解決策を持っているかどうか疑問に思っていました。

何か便利なものを探していましたitertoolsが、明らかに役立つものは見つかりませんでした。見落としていたかもしれませんが。

関連する質問:リストをチャンク単位で反復処理する最も「pythonic」な方法は何ですか?

4

69 に答える 69

4018

必要なチャンクを生成するジェネレーターを次に示します。

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

Python 2 バージョン:

[lst[i:i + n] for i in xrange(0, len(lst), n)]
于 2008-11-23T12:33:53.433 に答える
635

非常にシンプルなものが必要な場合:

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

于 2009-11-17T20:17:16.617 に答える
355

私はこれがちょっと古いことを知っていますが、まだ誰も言及していません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])]
于 2013-06-05T08:54:26.183 に答える
336

(古い) Python ドキュメント (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)

Guido のタイムマシンが作動し、作動し、作動し、作動し、再び作動していたと思います。

これらの解決策[iter(iterable)]*nは (または以前のバージョンの同等の方法で) 1 つの反復子を作成nし、リスト内で何度も繰り返されるため、機能します。izip_longest次に、「各」反復子のラウンドロビンを効果的に実行します。これは同じイテレータであるため、そのような呼び出しごとに進められ、そのような各 zip-roundrobin がnアイテムの 1 つのタプルを生成します。

于 2008-11-23T15:48:53.540 に答える
254

iter2 引数形式を使用することを誰も考えていなかったことに驚いています。

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ベースのソリューションと同様に、上記は常にパディングします。私の知る限り、オプションでパディングする関数の 1 行または 2 行の itertools レシピはありません。上記の 2 つのアプローチを組み合わせると、次のようになります。

_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)]
于 2014-02-26T15:02:00.293 に答える
114

以下は、任意の iterable で動作するジェネレータです。

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]]
于 2008-11-23T12:41:37.317 に答える
78

シンプルでありながらエレガント

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)
于 2010-07-12T07:58:43.420 に答える
65
def chunk(input, size):
    return map(None, *([iter(input)] * size))
于 2010-06-26T19:10:07.963 に答える
57

リストを均等なサイズのチャンクに分割するにはどうすればよいですか?

私にとって、「均等なサイズのチャンク」とは、それらがすべて同じ長さであるか、そのオプションを除いて、長さの差異が最小限であることを意味します。たとえば、21 個のアイテムに対して 5 つのバスケットを使用すると、次の結果が得られます。

>>> import statistics
>>> statistics.variance([5,5,5,5,1]) 
3.2
>>> statistics.variance([5,4,4,4,4]) 
0.19999999999999998

後者の結果を好む実際的な理由: これらの関数を使用して作業を分散していた場合、1 つの関数が他の関数よりも先に終了する可能性が組み込まれているため、他の関数が懸命に作業を続けている間、何もせずに座っていることになります。

他の回答への批判はこちら

私が最初にこの回答を書いたとき、他の回答はどれも均等なサイズのチャンクではありませんでした-それらはすべて最後にラントチャンクを残すため、バランスが取れておらず、長さの分散が必要以上に大きくなっています.

たとえば、現在の上位の回答は次のように終わります。

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

のような他のものlist(grouper(3, range(7)))と、chunk(range(7), 3)両方の戻り値: [(0, 1, 2), (3, 4, 5), (6, None, None)]。はNone単なるパディングであり、私の意見ではエレガントではありません。それらはイテラブルを均等にチャンクしていません。

これらをもっとうまく分割できないのはなぜですか?

サイクルソリューション

を使用した高レベルのバランスの取れたソリューションitertools.cycle。これは、今日私が行う方法です。セットアップは次のとおりです。

from itertools import cycle
items = range(10, 75)
number_of_baskets = 10

次に、要素を入力するためのリストが必要です。

baskets = [[] for _ in range(number_of_baskets)]

最後に、要素がなくなるまでバスケットのサイクルと一緒に割り当てようとしている要素を zip します。意味的には、これはまさに私たちが望むものです。

for element, basket in zip(items, cycle(baskets)):
    basket.append(element)

結果は次のとおりです。

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

このソリューションを製品化するために、関数を作成し、型注釈を提供します。

from itertools import cycle
from typing import List, Any

def cycle_baskets(items: List[Any], maxbaskets: int) -> List[List[Any]]:
    baskets = [[] for _ in range(min(maxbaskets, len(items)))]
    for item, basket in zip(items, cycle(baskets)):
        basket.append(item)
    return baskets

上記では、アイテムのリストとバスケットの最大数を取得しています。空のリストのリストを作成し、ラウンドロビン スタイルで各要素を追加します。

スライス

別の洗練された解決策は、スライスを使用することです。具体的には、あまり一般的に使用されないスライスへのステップ引数です。すなわち:

start = 0
stop = None
step = number_of_baskets

first_basket = items[start:stop:step]

これは、スライスがデータの長さを気にしないという点で特にエレガントです。結果として、最初のバスケットは必要な長さだけになります。各バスケットの開始点をインクリメントするだけです。

実際、これは 1 行で済む場合もありますが、読みやすくするため、またコードが長くなりすぎるのを避けるために複数行にします。

from typing import List, Any

def slice_baskets(items: List[Any], maxbaskets: int) -> List[List[Any]]:
    n_baskets = min(maxbaskets, len(items))
    return [items[i::n_baskets] for i in range(n_baskets)]

そしてisliceitertools モジュールから、最初に質問で求められたような遅延反復アプローチが提供されます。

元のデータはすでにリストに完全に具体化されているため、ほとんどのユースケースで大きなメリットが得られるとは思いませんが、大規模なデータセットの場合、メモリ使用量をほぼ半分に節約できます。

from itertools import islice
from typing import List, Any, Generator
    
def yield_islice_baskets(items: List[Any], maxbaskets: int) -> Generator[List[Any], None, None]:
    n_baskets = min(maxbaskets, len(items))
    for i in range(n_baskets):
        yield islice(items, i, None, n_baskets)

結果を表示:

from pprint import pprint

items = list(range(10, 75))
pprint(cycle_baskets(items, 10))
pprint(slice_baskets(items, 10))
pprint([list(s) for s in yield_islice_baskets(items, 10)])

更新された以前のソリューション

これは、モジュロ演算子を使用する、過去に本番環境で使用した関数から適応された別のバランスの取れたソリューションです。

def baskets_from(items, maxbaskets=25):
    baskets = [[] for _ in range(maxbaskets)]
    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 range(baskets):
        yield [items[y_i] for y_i in range(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 range(baskets):
        length = ceiling if x_i < stepdown else floor
        yield [items.next() for _ in range(length)]

出力

それらをテストするには:

print(baskets_from(range(6), 8))
print(list(iter_baskets_from(range(6), 8)))
print(list(iter_baskets_contiguous(range(6), 8)))
print(baskets_from(range(22), 8))
print(list(iter_baskets_from(range(22), 8)))
print(list(iter_baskets_contiguous(range(22), 8)))
print(baskets_from('ABCDEFG', 3))
print(list(iter_baskets_from('ABCDEFG', 3)))
print(list(iter_baskets_contiguous('ABCDEFG', 3)))
print(baskets_from(range(26), 5))
print(list(iter_baskets_from(range(26), 5)))
print(list(iter_baskets_contiguous(range(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]]

contiguous ジェネレーターは、他の 2 つのジェネレーターと同じ長さのパターンでチャンクを提供しますが、項目はすべて順番どおりであり、個別の要素のリストを分割するのと同じくらい均等に分割されていることに注意してください。

于 2014-02-13T23:07:17.073 に答える
53

リストのサイズがわかっている場合:

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

後者の場合、シーケンスが常に指定されたサイズの整数のチャンクを含む (つまり、不完全な最後のチャンクがない) ことを確認できれば、より美しい方法で言い換えることができます。

于 2008-11-23T12:40:39.830 に答える
51

この質問の複製で最も素晴らしいPythonっぽい答えを見ました:

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)が失われます。上記では Python 3 を使用しています。Python 2 の場合は、 を使用しますizip_longest

于 2015-03-12T12:36:10.303 に答える
37
[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

py3 で範囲を拡張するには

(py3) >>> [list(AA[i:i+SS]) for i in range(len(AA))[::SS]]
[[10, 11, 12], [13, 14, 15], [16, 17, 18], [19, 20]]
于 2015-12-16T21:42:56.703 に答える
36

車輪を再発明しないでください。

与えられた

import itertools as it
import collections as ct

import more_itertools as mit


iterable = range(11)
n = 3

コード

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

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

(または、必要に応じて DIY)

標準ライブラリ

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

参考文献

+ itertools レシピなどを実装するサードパーティ ライブラリ。> pip install more_itertools

于 2018-08-26T01:40:11.330 に答える
28

たとえば、チャンク サイズが 3 の場合、次のようにできます。

zip(*[iterable[i::3] for i in range(3)]) 

ソース: http://code.activestate.com/recipes/303060-group-a-list-into-sequential-n-tuples/

チャンクサイズが「3」などの入力可能な固定数であり、決して変更されない場合にこれを使用します。

于 2011-04-19T05:27:19.640 に答える
27

toolzライブラリには、このための機能partitionがあります。

from toolz.itertoolz.core import partition

list(partition(2, [1, 2, 3, 4]))
[(1, 2), (3, 4)]
于 2013-11-20T20:55:22.177 に答える
21

私は tzot と JFSebastian によって提案された Python doc のバージョンがとても気に入っていますが、2 つの欠点があります。

  • それはあまり明確ではありません
  • 私は通常、最後のチャンクに値を入れたくありません

私は自分のコードでこれをよく使用しています:

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))
于 2013-10-09T06:17:29.367 に答える
18

コード:

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]]
于 2015-07-02T07:32:49.237 に答える
13

へー、一行バージョン

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]]
于 2008-11-23T12:51:16.863 に答える
13
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
于 2008-11-24T16:56:57.067 に答える
12

大きなリストに適したlen()を呼び出さずに:

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

そして、これは反復可能です:

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))
于 2010-02-16T05:49:47.980 に答える
12

別のより明示的なバージョン。

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
于 2015-02-28T20:05:03.317 に答える
11

このリファレンスを参照してください

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

Python3

于 2013-02-18T13:31:51.440 に答える
10

ここで誰もがイテレータについて話しているので。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.

于 2016-11-03T19:10:45.350 に答える
10
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]]'
于 2012-02-13T04:50:38.350 に答える
9

matplotlib.cbookピースの使用を検討してください

例えば:

import matplotlib.cbook as cbook
segments = cbook.pieces(np.arange(20), 3)
for s in segments:
     print s
于 2011-05-03T16:27:37.780 に答える
7
a = [1, 2, 3, 4, 5, 6, 7, 8, 9]
CHUNK = 4
[a[i*CHUNK:(i+1)*CHUNK] for i in xrange((len(a) + CHUNK - 1) / CHUNK )]
于 2015-07-15T23:27:19.153 に答える
5
>>> def f(x, n, acc=[]): return f(x[n:], n, acc+[(x[:n])]) if x else acc
>>> f("Hallo Welt", 3)
['Hal', 'lo ', 'Wel', 't']
>>> 

あなたが括弧に入っているなら - 私はErlangに関する本を手に入れました:)

于 2009-11-03T16:45:31.877 に答える
5

この質問は古い(Googleでつまずいた)ことを認識していますが、次のようなものは、非常に複雑な提案よりもはるかに単純で明確であり、スライスのみを使用しています。

def chunker(iterable, chunksize):
    for i,c in enumerate(iterable[::chunksize]):
        yield iterable[i*chunksize:(i+1)*chunksize]

>>> for chunk in chunker(range(0,100), 10):
...     print list(chunk)
... 
[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]
... etc ...
于 2012-08-27T22:58:05.640 に答える
5

私はこのオプションを見たとは思わないので、別のものを追加するだけです:)):

def chunks(iterable, chunk_size):
  i = 0;
  while i < len(iterable):
    yield iterable[i:i+chunk_size]
    i += chunk_size
于 2017-11-03T12:38:56.040 に答える
4
def chunk(lst):
    out = []
    for x in xrange(2, len(lst) + 1):
        if not len(lst) % x:
            factor = len(lst) / x
            break
    while lst:
        out.append([lst.pop(0) for x in xrange(factor)])
    return out
于 2008-11-26T07:24:49.110 に答える
4

この時点で、必須の匿名再帰関数が必要だと思います。

Y = lambda f: (lambda x: x(x))(lambda y: f(lambda *args: y(y)(*args)))
chunks = Y(lambda f: lambda n: [n[0][:n[1]]] + f((n[0][n[1]:], n[1])) if len(n[0]) > 0 else [])
于 2015-11-04T09:12:27.593 に答える
4

以下に機能する解決策が1つありますが、その解決策よりも重要なのは、他のアプローチに関するいくつかのコメントです。まず、適切なソリューションでは、サブ反復子を順番に 1 回ループする必要はありません。私が走れば

g = paged_iter(list(range(50)), 11))
i0 = next(g)
i1 = next(g)
list(i1)
list(i0)

最後のコマンドの適切な出力は次のとおりです。

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

いいえ

 []

ここの itertools ベースのソリューションのほとんどが戻ってきます。これは、反復子に順番にアクセスすることに関する通常の退屈な制限だけではありません。5 のブロックの適切な順序を逆にした不十分に入力されたデータをクリーンアップしようとしている消費者を想像してみてください。 A5 はサブリストではなく 5 つの要素のみです)。この消費者は、グループ化関数の要求された動作を見て、躊躇せずに次のようなループを記述します。

i = 0
out = []
for it in paged_iter(data,5)
    if (i % 2 == 0):
         swapped = it
    else: 
         out += list(it)
         out += list(swapped)
    i = i + 1

サブイテレータが常に完全に順番に使用されているとこっそりと仮定すると、不思議なほど間違った結果が生成されます。チャンクから要素をインターリーブしたい場合は、さらに悪化します。

第二に、提案されたソリューションのかなりの数は、イテレータが決定論的な順序を持っているという事実に暗黙のうちに依存しています (たとえば、設定されていません)。

第三に、 itertools grouper アプローチは機能しますが、レシピは公開された動作の一部ではない zip_longest (または zip) 関数の内部動作に依存しています。特に、zip_longest(i0...in) では、最初からやり直す前に次の関数が常に next(i0)、next(i1)、... next(in) の順序で呼び出されるため、グルーパー関数のみが機能します。grouper は同じ反復子オブジェクトの n 個のコピーを渡すため、この動作に依存します。

最後に、サブイテレータが順番にアクセスされ、この仮定なしで完全に熟読されるという上記で批判された仮定を行う場合、以下のソリューションを改善できますが、要素を暗黙的に (呼び出しチェーンを介して) または明示的に (deques または他のデータ構造を介して) 格納する必要があります。どこかのサブイテレータごとに。したがって、巧妙なトリックでこれを回避できると仮定して (私が行ったように) わざわざ時間を無駄にしないでください。

def paged_iter(iterat, n):
    itr = iter(iterat)
    deq = None
    try:
        while(True):
            deq = collections.deque(maxlen=n)
            for q in range(n):
                deq.append(next(itr))
            yield (i for i in deq)
    except StopIteration:
        yield (i for i in deq)
于 2017-01-11T09:18:53.907 に答える
4

リスト内包表記を使用します。

l = [1,2,3,4,5,6,7,8,9,10,11,12]
k = 5 #chunk size
print [tuple(l[x:y]) for (x, y) in [(x, x+k) for x in range(0, len(l), k)]]
于 2015-02-27T02:33:35.490 に答える
3

私はこの目的のために特別に小さなライブラリを作成しました。こちらから入手できます。ライブラリのchunked関数はgeneratorとして実装されているため特に効率的であり、特定の状況でかなりの量のメモリを節約できます。また、スライス表記に依存しないため、任意の反復子を使用できます。

import iterlib

print list(iterlib.chunked(xrange(1, 1000), 10))
# prints [(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), (11, 12, 13, 14, 15, 16, 17, 18, 19, 20), ...]
于 2014-03-03T04:30:24.130 に答える
2

私は一時的なリストオブジェクトを作成せずに次の解決策にたどり着きました。これは反復可能なオブジェクトで動作するはずです。Python 2.x のこのバージョンは次の点に注意してください。

def chunked(iterable, size):
    stop = []
    it = iter(iterable)
    def _next_chunk():
        try:
            for _ in xrange(size):
                yield next(it)
        except StopIteration:
            stop.append(True)
            return

    while not stop:
        yield _next_chunk()

for it in chunked(xrange(16), 4):
   print list(it)

出力:

[0, 1, 2, 3]
[4, 5, 6, 7]
[8, 9, 10, 11]
[12, 13, 14, 15] 
[]

ご覧のとおり、len(iterable) % size == 0 の場合、空のイテレータ オブジェクトが追加されます。しかし、それが大きな問題だとは思いません。

于 2015-09-18T17:54:39.500 に答える
2

@AaronHall のように、ほぼ均等なサイズのチャンクを探してここにたどり着きました。それにはさまざまな解釈があります。私の場合、目的のサイズが N の場合、各グループのサイズを >=N にしたいと考えています。したがって、上記のほとんどで作成されたオーファンは、他のグループに再配布する必要があります。

これは、次を使用して実行できます。

def nChunks(l, n):
    """ Yield n successive chunks from l.
    Works for lists,  pandas dataframes, etc
    """
    newn = int(1.0 * len(l) / n + 0.5)
    for i in xrange(0, n-1):
        yield l[i*newn:i*newn+newn]
    yield l[n*newn-newn:]

(リストをほぼ同じ長さの N 個の部分に分割することから) nChunks(l,l/n) または nChunks(l,floor(l/n)) として単に呼び出すことによって

于 2014-09-03T17:43:15.433 に答える
2
  • 任意のイテラブルで動作します
  • 内部データはジェネレーターオブジェクトです (リストではありません)
  • 一発ギャグ
[259]: get_in_chunks = lambda itr,n: ( (v for _,v in g) for _,g in itertools.groupby(enumerate(itr),lambda (ind,_): ind/n))

[260]: list(list(x) for x in get_in_chunks(range(30),7))
アウト[260]:
[[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]]
于 2013-09-13T19:11:44.557 に答える
2

これは v2/v3 で機能し、インライン化可能で、ジェネレーター ベースであり、標準ライブラリのみを使用します。

import itertools
def split_groups(iter_in, group_size):
    return ((x for _, x in item) for _, item in itertools.groupby(enumerate(iter_in), key=lambda x: x[0] // group_size))
于 2017-07-06T22:24:14.247 に答える
1

私はこのようなことをしなければならなかったので、ジェネレーターとバッチサイズを与えられた私の解決策は次のとおりです:

def pop_n_elems_from_generator(g, n):
    elems = []
    try:
        for idx in xrange(0, n):
            elems.append(g.next())
        return elems
    except StopIteration:
        return elems
于 2015-10-16T22:09:29.580 に答える
0

リストがlst

import math

# length of the list len(lst) is ln
# size of a chunk is size

for num in range ( math.ceil(ln/size) ):
    start, end = num*size, min((num+1)*size, ln)
    print(lst[start:end])
于 2022-01-31T14:53:24.967 に答える
-1

遅延読み込みバージョン

import pprint
pprint.pprint(list(chunks(range(10, 75), 10)))
[range(10, 20),
 range(20, 30),
 range(30, 40),
 range(40, 50),
 range(50, 60),
 range(60, 70),
 range(70, 75)]

この実装の結果に、受け入れられた回答の使用結果の例を提供します。

上記の関数の多くは、イテラブル全体の長さが事前にわかっているか、少なくとも計算が安価であることを前提としています。

一部のストリーミング オブジェクトでは、長さ情報を取得するために、最初に完全なデータをメモリにロードする (たとえば、ファイル全体をダウンロードする) ことを意味します。

ただし、フルサイズがまだわからない場合は、代わりに次のコードを使用できます。

def chunks(iterable, size):
    """
    Yield successive chunks from iterable, being `size` long.

    https://stackoverflow.com/a/55776536/3423324
    :param iterable: The object you want to split into pieces.
    :param size: The size each of the resulting pieces should have.
    """
    i = 0
    while True:
        sliced = iterable[i:i + size]
        if len(sliced) == 0:
            # to suppress stuff like `range(max, max)`.
            break
        # end if
        yield sliced
        if len(sliced) < size:
            # our slice is not the full length, so we must have passed the end of the iterator
            break
        # end if
        i += size  # so we start the next chunk at the right place.
    # end while
# end def

iterable の最後を渡した場合、slice コマンドが返す要素が少ない/ないため、これは機能します。

"abc"[0:2] == 'ab'
"abc"[2:4] == 'c'
"abc"[4:6] == ''

スライスの結果を使用して、生成されたチャンクの長さを計算します。予想よりも少ない場合は、反復を終了できることがわかります。

そうすれば、アクセスしない限りイテレータは実行されません。

于 2019-04-20T18:28:54.083 に答える
-1
def main():
  print(chunkify([1,2,3,4,5,6],2))

def chunkify(list, n):
  chunks = []
  for i in range(0, len(list), n):
    chunks.append(list[i:i+n])
  return chunks

main()

シンプルで、配列のチャンクを提供できると思います。

于 2020-04-14T16:18:52.350 に答える
-2

Python のリスト内包表記を使用する

[range(t,t+10) for t in range(1,1000,10)]

[[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],....
 ....[981, 982, 983, 984, 985, 986, 987, 988, 989, 990],
 [991, 992, 993, 994, 995, 996, 997, 998, 999, 1000]]

リスト内包表記について知るには、このリンクにアクセスしてください

于 2013-05-26T15:18:42.210 に答える
-2

はい、それは古い質問ですが、同様の質問よりも少し短いため、これを投稿する必要がありました。はい、結果はごちゃ混ぜに見えますが、ほぼ均等な長さであれば...

>>> n = 3 # number of groups
>>> biglist = range(30)
>>>
>>> [ biglist[i::n] for i in xrange(n) ]
[[0, 3, 6, 9, 12, 15, 18, 21, 24, 27],
 [1, 4, 7, 10, 13, 16, 19, 22, 25, 28],
 [2, 5, 8, 11, 14, 17, 20, 23, 26, 29]]
于 2013-11-26T21:58:03.577 に答える