Mögliches Duplizieren:
Wie teilt man eine Liste in Python in gleich große Teile auf?
Ich bin überrascht, dass ich keine "Batch" -Funktion finden konnte, die als Eingabe eine iterable und eine iterable von iterables zurückgibt.
Zum Beispiel:
for i in batch(range(0,10), 1): print i
[0]
[1]
...
[9]
oder:
for i in batch(range(0,10), 3): print i
[0,1,2]
[3,4,5]
[6,7,8]
[9]
Jetzt schrieb ich, was ich für einen ziemlich einfachen Generator hielt:
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
Aber das Obige gibt mir nicht das, was ich erwartet hätte:
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]
Ich habe also etwas verpasst und dies zeigt wahrscheinlich mein völliges Unverständnis für Python-Generatoren. Möchte mich jemand in die richtige Richtung weisen?
[Bearbeiten: Ich habe schließlich festgestellt, dass das obige Verhalten nur auftritt, wenn ich dies in ipython und nicht in python selbst ausführe]
batch = (tuple(filterfalse(lambda x: x is None, group)) for group in zip_longest(fillvalue=None, *[iter(iterable)] * n))
Antworten:
Dies ist wahrscheinlich effizienter (schneller)
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
Beispiel mit Liste
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]
Es wird vermieden, neue Listen zu erstellen.
quelle
min()
Code ist das völlig richtig!len()
, Sequenzen habenlen()
FWIW, die Rezepte im Modul itertools bieten dieses Beispiel:
def grouper(n, iterable, fillvalue=None): "grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx" args = [iter(iterable)] * n return zip_longest(fillvalue=fillvalue, *args)
Es funktioniert so:
>>> list(grouper(3, range(10))) [(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, None, None)]
quelle
izip_longest
durchizip
, wodurch nicht die letzten Einträge aufgefüllt werden, sondern Einträge abgeschnitten werden, wenn einige der Elemente ausgehen.for x, y in enumerate(grouper(3, xrange(10))): print(x,y)
der Tat keine Werte, sondern löscht nur das unvollständige Segment insgesamt.list(zip(*[iter(iterable)] * n))
. Dies muss das sauberste Stück Python-Code sein, das ich je gesehen habe.Wie andere angemerkt haben, macht der von Ihnen angegebene Code genau das, was Sie wollen. Für einen anderen Ansatz mit
itertools.islice
könnte man eine sehen Beispiel für das folgende Rezept sehen:from itertools import islice, chain def batch(iterable, size): sourceiter = iter(iterable) while True: batchiter = islice(sourceiter, size) yield chain([batchiter.next()], batchiter)
quelle
next()
zu bewirken, dass einStopIteration
einmalsourceiter
erschöpft ist, wodurch der Iterator beendet wird. Ohne den Aufrufnext
würde es weiterhin unbegrenzt leere Iteratoren zurückgeben.batchiter.next()
mitnext(batchiter)
zu der obigen Code Arbeit in Python zu machen 3.map(list, batch(xrange(10), 3))
. Tun:list(batch(xrange(10), 3)
führt zu unerwarteten Ergebnissen..next()
muss geändert werdennext(..)
, undlist(batch(range(0,10),3))
wirftRuntimeError: generator raised StopIteration
while
Schleife intry:
/except StopIteration: return
, um das letztere Problem zu beheben.Ich habe nur eine Antwort gegeben. Jetzt bin ich jedoch der Meinung, dass die beste Lösung darin besteht, keine neuen Funktionen zu schreiben. More-itertools enthält viele zusätzliche Tools und
chunked
gehört dazu.quelle
ichunked
iterable Antworten.Seltsam, scheint für mich in Python 2.x gut zu funktionieren
>>> 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]
quelle
Dies ist ein sehr kurzes Code-Snippet, das ich kenne und das nicht
len
unter Python 2 und 3 (nicht meiner Erstellung) verwendet und funktioniert:def chunks(iterable, size): from itertools import chain, islice iterator = iter(iterable) for first in iterator: yield list(chain([first], islice(iterator, size - 1)))
quelle
Lösung für Python 3.8, wenn Sie mit Iterables arbeiten, die keine
len
Funktion definieren , und erschöpft sind:def batcher(iterable, batch_size): while batch := list(islice(iterable, batch_size)): yield batch
Anwendungsbeispiel:
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]
Könnte natürlich auch ohne den Walross-Operator implementiert werden.
quelle
batcher
Akzeptiert in der aktuellen Version einen Iterator, keinen iterierbaren. Dies würde beispielsweise zu einer Endlosschleife mit einer Liste führen.iterator = iter(iterable)
Vor dem Starten derwhile
Schleife sollte wahrscheinlich eine Zeile vorhanden sein .Das verwende ich in meinem Projekt. Es behandelt Iterables oder Listen so effizient wie möglich.
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()]
quelle
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]]
quelle
Dies würde für jedes iterable funktionieren.
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))
Es würde so funktionieren:
>>>list(batch_iterable(range(0,5)), 2) [(0, 1), (2, 3), (4,)]
PS: Es würde nicht funktionieren, wenn iterable None-Werte hat.
quelle
Hier ist ein Ansatz mit
reduce
Funktion.Einzeiler:
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, [[]])
Oder besser lesbare Version:
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, [[]])
Prüfung:
>>> 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]]
quelle
Eine funktionsfähige Version ohne neue Funktionen in Python 3.8, angepasst an die Antwort von @Atra Azami.
import itertools def batch_generator(iterable, batch_size=1): iterable = iter(iterable) while True: batch = list(itertools.islice(iterable, batch_size)) if len(batch) > 0: yield batch else: break for x in batch_generator(range(0, 10), 3): print(x)
Ausgabe:
[0, 1, 2] [3, 4, 5] [6, 7, 8] [9]
quelle
Sie können iterierbare Elemente einfach nach ihrem Stapelindex gruppieren.
def batch(items: Iterable, batch_size: int) -> Iterable[Iterable]: # enumerate items and group them by batch index enumerated_item_groups = itertools.groupby(enumerate(items), lambda t: t[0] // batch_size) # extract items from enumeration tuples item_batches = ((t[1] for t in enumerated_items) for key, enumerated_items in enumerated_item_groups) return item_batches
Dies ist häufig der Fall, wenn Sie innere Iterables sammeln möchten. Hier finden Sie eine erweiterte Version.
def batch_advanced(items: Iterable, batch_size: int, batches_mapper: Callable[[Iterable], Any] = None) -> Iterable[Iterable]: enumerated_item_groups = itertools.groupby(enumerate(items), lambda t: t[0] // batch_size) if batches_mapper: item_batches = (batches_mapper(t[1] for t in enumerated_items) for key, enumerated_items in enumerated_item_groups) else: item_batches = ((t[1] for t in enumerated_items) for key, enumerated_items in enumerated_item_groups) return item_batches
Beispiele:
print(list(batch_advanced([1, 9, 3, 5, 2, 4, 2], 4, tuple))) # [(1, 9, 3, 5), (2, 4, 2)] print(list(batch_advanced([1, 9, 3, 5, 2, 4, 2], 4, list))) # [[1, 9, 3, 5], [2, 4, 2]]
quelle
Zugehörige Funktionen, die Sie möglicherweise benötigen:
def batch(size, i): """ Get the i'th batch of the given size """ return slice(size* i, size* i + size)
Verwendung:
>>> [1,2,3,4,5,6,7,8,9,10][batch(3, 1)] >>> [4, 5, 6]
Es erhält den i-ten Stapel aus der Sequenz und kann auch mit anderen Datenstrukturen wie pandas dataframes (
df.iloc[batch(100,0)]
) oder numpy array (array[batch(100,0)]
) arbeiten.quelle
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,)]
quelle
ich benutze
def batchify(arr, batch_size): num_batches = math.ceil(len(arr) / batch_size) return [arr[i*batch_size:(i+1)*batch_size] for i in range(num_batches)]
quelle
Nehmen Sie (höchstens) n Elemente, bis sie aufgebraucht sind.
def chop(n, iterable): iterator = iter(iterable) while chunk := list(take(n, iterator)): yield chunk def take(n, iterable): iterator = iter(iterable) for i in range(n): try: yield next(iterator) except StopIteration: return
quelle