Come si divide un elenco in blocchi di dimensioni uniformi?
Domanda
Ho un elenco di lunghezza arbitraria e ho bisogno di dividerlo in blocchi di uguali dimensioni e operare su di esso. Esistono alcuni modi ovvi per farlo, come mantenere un contatore e due elenchi e quando si riempie il secondo elenco, aggiungerlo al primo elenco e svuotare il secondo elenco per il prossimo round di dati, ma questo è potenzialmente estremamente costoso.
Mi chiedevo se qualcuno avesse una buona soluzione a questo per elenchi di qualsiasi lunghezza, ad es. utilizzando generatori.
Stavo cercando qualcosa di utile in itertools
ma non sono riuscito a trovare nulla di ovviamente utile. Potrei averlo perso, però.
Domanda correlata: Qual è il modo più “pitonico” per scorrere su un elenco in blocchi?
Soluzione
Ecco un generatore che produce i pezzi che vuoi:
def chunks(l, n):
"""Yield successive n-sized chunks from l."""
for i in range(0, len(l), n):
yield l[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]]
Se stai usando Python 2, dovresti usare xrange ()
invece di range ()
:
def chunks(l, n):
"""Yield successive n-sized chunks from l."""
for i in xrange(0, len(l), n):
yield l[i:i + n]
Inoltre puoi semplicemente usare la comprensione dell'elenco invece di scrivere una funzione, anche se è una buona idea incapsulare operazioni come questa nelle funzioni nominate in modo che il tuo codice sia più facile da capire. Python 3:
[l[i:i + n] for i in range(0, len(l), n)]
Versione Python 2:
[l[i:i + n] for i in xrange(0, len(l), n)]
Altri suggerimenti
Se vuoi qualcosa di super semplice:
def chunks(l, n):
n = max(1, n)
return (l[i:i+n] for i in xrange(0, len(l), n))
Usa range ()
invece di xrange ()
nel caso di Python 3.x
Direttamente dalla (vecchia) documentazione di Python (ricette per 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)
La versione attuale, come suggerito da J.F.Sebastian:
#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)
Suppongo che la macchina del tempo di Guido funzioni & # 8212; ha funzionato & # 8212; funzionerà & # 8212; avrà funzionato & # 8212; funzionerà di nuovo.
Queste soluzioni funzionano perché [iter (iterable)] * n
(o l'equivalente nella versione precedente) crea uno iteratore, ripetuto n
volte nell'elenco. izip_longest
esegue quindi in modo efficace un round robin di " ciascuno " iterator; poiché questo è lo stesso iteratore, è avanzato da ciascuna di queste chiamate, con il risultato che ciascuna di queste zip-roundrobin genera una tupla di n
elementi
So che questo è un po 'vecchio ma non so perché nessuno abbia menzionato numpy.array_split
:
lst = range(50)
In [26]: np.array_split(lst,5)
Out[26]:
[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])]
Sono sorpreso che nessuno abbia pensato di usare iter
modulo a due argomenti :
from itertools import islice
def chunk(it, size):
it = iter(it)
return iter(lambda: tuple(islice(it, size)), ())
Demo:
>>> list(chunk(range(14), 3))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13)]
Funziona con qualsiasi iterabile e produce pigramente output. Restituisce tuple anziché iteratori, ma penso che abbia comunque una certa eleganza. Inoltre non pad; se vuoi il padding, basterà una semplice variazione di quanto sopra:
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)
Demo:
>>> 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')]
Come le soluzioni basate su izip_longest
, i pad sempre sopra. Per quanto ne so, non esiste una ricetta itertools a una o due righe per una funzione che opzionalmente esegue il pad. Combinando i due approcci precedenti, questo si avvicina abbastanza:
_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)
Demo:
>>> 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')]
Credo che questo sia il pezzo più corto proposto che offre imbottitura opzionale.
As Tomasz Gandor osservato , i due blocchi di imbottitura si fermeranno inaspettatamente se incontrano una lunga sequenza di valori di pad. Ecco una variante finale che risolve il problema in modo ragionevole:
_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))
Demo:
>>> list(chunk([1, 2, (), (), 5], 2))
[(1, 2), ((), ()), (5,)]
>>> list(chunk([1, 2, None, None, 5], 2, None))
[(1, 2), (None, None), (5, None)]
Ecco un generatore che funziona su iterabili arbitrari:
def split_seq(iterable, size):
it = iter(iterable)
item = list(itertools.islice(it, size))
while item:
yield item
item = list(itertools.islice(it, size))
Esempio:
>>> 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]]
def chunk(input, size):
return map(None, *([iter(input)] * size))
Semplice ma elegante
l = range(1, 1000)
print [l[x:x+10] for x in xrange(0, len(l), 10)]
o se preferisci:
chunks = lambda l, n: [l[x: x+n] for x in xrange(0, len(l), n)]
chunks(l, 10)
Ho visto la più fantastica risposta di Python in un duplicato di questa domanda:
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)]
Puoi creare n-tupla per qualsiasi n. Se a = range (1, 15)
, il risultato sarà:
[(1, 2, 3), (4, 5, 6), (7, 8, 9), (10, 11, 12), (13, 14, None)]
Se l'elenco è diviso equamente, è possibile sostituire zip_longest
con zip
, altrimenti la tripletta (13, 14, None)
sarebbe essere perso. Python 3 è usato sopra. Per Python 2, usa izip_longest
.
Critica ad altre risposte qui:
Nessuna di queste risposte è costituita da blocchi di dimensioni uniformi, alla fine lasciano un pezzo di sfregamento, quindi non sono completamente bilanciati. Se stavi usando queste funzioni per distribuire il lavoro, hai incorporato la prospettiva che uno probabilmente finisca molto prima degli altri, quindi rimarrebbe seduto senza fare nulla mentre gli altri continuano a lavorare sodo.
Ad esempio, l'attuale risposta principale termina con:
[60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
[70, 71, 72, 73, 74]]
Odio quel brontolio alla fine!
Altri, come list (grouper (3, xrange (7)))
e chunk (xrange (7), 3)
entrambi restituiscono: [ (0, 1, 2), (3, 4, 5), (6, None, None)]
. I None
sono solo imbottiture e, a mio avviso, piuttosto ineleganti. NON stanno distribuendo uniformemente gli iterabili.
Perché non possiamo dividerli meglio?
Le mie soluzioni
Ecco una soluzione bilanciata, adattata da una funzione che ho usato in produzione (Nota in Python 3 per sostituire xrange
con 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)
E ho creato un generatore che fa lo stesso se lo metti in un elenco:
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)]
E infine, poiché vedo che tutte le funzioni sopra riportate restituiscono gli elementi in un ordine contiguo (come sono stati dati):
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)]
uscita ??h2>
Per provarli:
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)))
Che stampa:
[[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]]
Si noti che il generatore contiguo fornisce blocchi con gli stessi schemi di lunghezza degli altri due, ma gli elementi sono tutti in ordine e sono equamente divisi come si potrebbe dividere un elenco di elementi discreti.
Se conosci le dimensioni dell'elenco:
def SplitList(mylist, chunk_size):
return [mylist[offs:offs+chunk_size] for offs in range(0, len(mylist), chunk_size)]
In caso contrario (un iteratore):
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
In quest'ultimo caso, può essere riformulato in un modo più bello se si può essere sicuri che la sequenza contenga sempre un numero intero di blocchi di dimensioni specifiche (cioè non c'è un ultimo blocco incompleto).
Se avessi una dimensione del blocco di 3, ad esempio, potresti fare:
zip(*[iterable[i::3] for i in range(3)])
fonte: http://code.activestate.com/recipes/303060-group -a-list-in-sequenziale-n-tuple /
Lo userei quando la mia dimensione del blocco è un numero fisso che posso digitare, ad es. "3" e non cambierebbe mai.
La libreria toolz ha la funzione partition
per questo:
from toolz.itertoolz.core import partition
list(partition(2, [1, 2, 3, 4]))
[(1, 2), (3, 4)]
Mi piace molto la versione del documento di Python proposta da tzot e J.F.Sebastian, ma ha due carenze:
- non è molto esplicito
- Di solito non voglio un valore di riempimento nell'ultimo pezzo
Lo sto usando molto nel mio codice:
from itertools import islice
def chunks(n, iterable):
iterable = iter(iterable)
while True:
yield tuple(islice(iterable, n)) or iterable.next()
AGGIORNAMENTO: una versione di blocchi pigri:
from itertools import chain, islice
def chunks(n, iterable):
iterable = iter(iterable)
while True:
yield chain([next(iterable)], islice(iterable, n-1))
A questo punto, penso che abbiamo bisogno di un generatore ricorsivo , nel caso ...
In python 2:
def chunks(li, n):
if li == []:
return
yield li[:n]
for e in chunks(li[n:], n):
yield e
In python 3:
def chunks(li, n):
if li == []:
return
yield li[:n]
yield from chunks(li[n:], n)
Inoltre, in caso di massiccia invasione aliena, un generatore ricorsivo decorato potrebbe tornare utile:
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
Puoi anche utilizzare la funzione get_chunks
della utilspie
come:
>>> 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]]
Puoi installare utilspie
tramite pip:
sudo pip install utilspie
Disclaimer: sono il creatore di utilspie library .
Ero curioso di vedere l'esecuzione di diversi approcci ed eccolo qui:
Testato su 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)
Risultati:
slice
31.18285083770752
index
0.02184295654296875
batches 1
0.03503894805908203
batches 2
0.22681021690368652
chunks
0.019841909408569336
grouper
0.006506919860839844
[AA[i:i+SS] for i in range(len(AA))[::SS]]
Dove AA è un array, SS ha una dimensione di blocco. Ad esempio:
>>> 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
codice:
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)
Risultati:
[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]
heh, versione a una riga
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]]
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
utilizzo:
seq = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for seq in split_seq(seq, 3):
print seq
Un'altra versione più esplicita.
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
Senza chiamare len () che è buono per elenchi di grandi dimensioni:
def splitter(l, n):
i = 0
chunk = l[:n]
while chunk:
yield chunk
i += n
chunk = l[i:i+n]
E questo è per iterables:
def isplitter(l, n):
l = iter(l)
chunk = list(islice(l, n))
while chunk:
yield chunk
chunk = list(islice(l, n))
Il sapore funzionale di quanto sopra:
def isplitter2(l, n):
return takewhile(bool,
(tuple(islice(start, n))
for start in repeat(iter(l))))
O:
def chunks_gen_sentinel(n, seq):
continuous_slices = imap(islice, repeat(iter(seq)), repeat(0), repeat(n))
return iter(imap(tuple, continuous_slices).next,())
O:
def chunks_gen_filter(n, seq):
continuous_slices = imap(islice, repeat(iter(seq)), repeat(0), repeat(n))
return takewhile(bool,imap(tuple, continuous_slices))
Un'altra soluzione
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]
>>>
Valuta l'utilizzo di matplotlib.cbook pezzi
ad esempio:
import matplotlib.cbook as cbook
segments = cbook.pieces(np.arange(20), 3)
for s in segments:
print s
Vedi questo riferimento
>>> 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
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 )]
A questo punto, penso che abbiamo bisogno della funzione obbligatoria anonima-ricorsiva.
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 [])
Dal momento che tutti qui parlano di iteratori. boltons
ha un metodo perfetto per questo, chiamato iterutils.chunked_iter
.
from boltons import iterutils
list(iterutils.chunked_iter(list(range(50)), 11))
Output:
[[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]]
Ma se non vuoi essere misericordioso nella memoria, puoi usare il vecchio modo e memorizzare l'elenco completo
in primo luogo con iterutils.chunked
.
Ecco un elenco di approcci aggiuntivi:
Data ??strong>
import itertools as it
import collections as ct
import more_itertools as mit
iterable = range(11)
n = 3
Codice
La libreria standard
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]]
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)]
Riferimenti
-
zip_longest
(< a href = "https://stackoverflow.com/questions/312443/how-do-you-split-a-list-into-evenly-sized-chunks/3125186#3125186"> post correlato , post correlato ) -
setdefault
(ordinato i risultati richiedono Python 3.6+) -
collections.defaultdict
(i risultati ordinati richiedono Python 3.6+) -
more_itertools.chunked
( correlati correlati ) -
more_itertools.sliced ??
-
more_itertools.grouper
( post correlato ) -
more_itertools.windowed
(vedi anchestagger
,zip_offset
)
+ Una libreria di terze parti che implementa ricette itertools e altro ancora. > pip installa more_itertools