116

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

iterableを入力として受け取り、iterableのiterableを返す「バッチ」関数が見つからなかったことに驚いています。

例えば:

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]

だから、私は何かを逃しました、そしてこれはおそらくPythonジェネレーターの私の完全な理解の欠如を示しています。誰かが私を正しい方向に向けることを気にかけますか?

[編集:最終的に、上記の動作は、Python自体ではなくipython内でこれを実行した場合にのみ発生することに気付きました]

4

20 に答える 20

162

これはおそらくより効率的です(高速です)

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]

新しいリストの作成を回避します。

于 2011-11-28T01:12:42.920 に答える
71

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)]
于 2011-11-28T01:09:29.467 に答える
34

他の人が指摘したように、あなたが与えたコードはあなたが望むことを正確に行います。を使用した別のアプローチについては、次のレシピの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)
于 2011-11-28T01:13:20.293 に答える
13

len関数を定義せず、使い果たされる iterables を使用している場合の Python 3.8 の解決策:

from itertools import islice

def batcher(iterable, batch_size):
    iterator = iter(iterable)
    while batch := list(islice(iterator, 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]

もちろん、セイウチ オペレータなしでも実装できます。

于 2020-07-15T11:29:56.123 に答える
10

奇妙なことに、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]
于 2011-11-28T01:05:15.260 に答える
10

これは私が知っている非常に短いコード スニペットlenで、Python 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)))
于 2019-07-29T03:07:44.150 に答える
2

これは私のプロジェクトで使用するものです。イテラブルまたはリストを可能な限り効率的に処理します。

def chunker(iterable, size):
    if not hasattr(iterable, "__len__"):
        # generators don't have len, so fall back to slower
        # method that works with generators
        for chunk in chunker_gen(iterable, size):
            yield chunk
        return

    it = iter(iterable)
    for i in range(0, len(iterable), size):
        yield [k for k in islice(it, size)]


def chunker_gen(generator, size):
    iterator = iter(generator)
    for first in iterator:

        def chunk():
            yield first
            for more in islice(iterator, size - 1):
                yield more

        yield [k for k in chunk()]
于 2019-01-29T23:55:44.433 に答える
0
from itertools import *

class SENTINEL: pass

def batch(iterable, n):
    return (tuple(filterfalse(lambda x: x is SENTINEL, group)) for group in zip_longest(fillvalue=SENTINEL, *[iter(iterable)] * n))

print(list(range(10), 3)))
# outputs: [(0, 1, 2), (3, 4, 5), (6, 7, 8), (9,)]
print(list(batch([None]*10, 3)))
# outputs: [(None, None, None), (None, None, None), (None, None, None), (None,)]
于 2020-05-27T12:54:42.900 に答える