可能性のある複製:
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]
だから、私は何かを見逃しており、これはおそらくpythonジェネレーターの理解の完全な欠如を示しています。
[編集:最終的に、上記の動作はpython自体]ではなく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
新しいリストの作成を回避します。
FWIW、 itertoolsモジュールのレシピ はこの例を提供します:
def grouper(n, iterable, fillvalue=None):
"grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
args = [iter(iterable)] * n
return izip_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)
奇妙な、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]
ただ一つ答えました。しかし、今では、新しいソリューションを作成しないことが最善の解決策だと感じています。 More-itertools には多くの追加ツールが含まれており、chunked
もその中に含まれています。
これは私のプロジェクトで使用するものです。イテラブルまたはリストを可能な限り効率的に処理します。
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()]
以下は、reduce
関数を使用したアプローチです。
ワンライナー:
from functools import reduce
reduce(lambda cumulator,item: cumulator[-1].append(item) or cumulator if len(cumulator[-1]) < batch_size else cumulator + [[item]], input_array, [[]])
またはより読みやすいバージョン:
from functools import reduce
def batch(input_list, batch_size):
def reducer(cumulator, item):
if len(cumulator[-1]) < batch_size:
cumulator[-1].append(item)
return cumulator
else:
cumulator.append([item])
return cumulator
return reduce(reducer, input_list, [[]])
テスト:
>>> batch([1,2,3,4,5,6,7], 3)
[[1, 2, 3], [4, 5, 6], [7]]
>>> batch(a, 8)
[[1, 2, 3, 4, 5, 6, 7]]
>>> batch([1,2,3,None,4], 3)
[[1, 2, 3], [None, 4]]
これは私が知っている非常に短いコードスニペットであり(私の作成ではありません)、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)))
def batch(iterable, n):
iterable=iter(iterable)
while True:
chunk=[]
for i in range(n):
try:
chunk.append(next(iterable))
except StopIteration:
yield chunk
return
yield chunk
list(batch(range(10), 3))
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]
これは、反復可能であれば機能します。
from itertools import Zip_longest, filterfalse
def batch_iterable(iterable, batch_size=2):
args = [iter(iterable)] * batch_size
return (Tuple(filterfalse(lambda x: x is None, group)) for group in Zip_longest(fillvalue=None, *args))
次のように機能します。
>>>list(batch_iterable(range(0,5)), 2)
[(0, 1), (2, 3), (4,)]
PS:iterableの値がNoneの場合、機能しません。