Comment diviser une liste en morceaux de taille égale?
Question
J'ai une liste de longueur arbitraire, et j'ai besoin de la scinder en morceaux de taille égale et de l'exploiter. Il existe des moyens évidents de le faire, comme garder un compteur et deux listes, et lorsque la deuxième liste est pleine, ajoutez-le à la première liste et videz la deuxième liste pour le prochain cycle de données, mais cela peut être extrêmement coûteux.
Je me demandais si quelqu'un avait une bonne solution à cela pour des listes de n'importe quelle longueur, par exemple. en utilisant des générateurs.
Je recherchais quelque chose d'utile dans itertools
mais je ne trouvais rien qui soit manifestement utile. Cela aurait peut-être manqué, cependant.
Question liée: Quel est le plus & # 8220; pythonique & # 8221; moyen de parcourir une liste en morceaux?
La solution
Voici un générateur qui fournit les morceaux que vous voulez:
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]]
Si vous utilisez Python 2, vous devez utiliser xrange ()
au lieu de 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]
Vous pouvez également utiliser simplement la compréhension de liste au lieu d’écrire une fonction, bien que c’est une bonne idée d’encapsuler de telles opérations dans des fonctions nommées afin que votre code soit plus facile à comprendre. Python 3:
[l[i:i + n] for i in range(0, len(l), n)]
Version Python 2:
[l[i:i + n] for i in xrange(0, len(l), n)]
Autres conseils
Si vous voulez quelque chose de très simple:
def chunks(l, n):
n = max(1, n)
return (l[i:i+n] for i in xrange(0, len(l), n))
Utilisez range ()
au lieu de xrange ()
dans le cas de Python 3.x
Directement à partir de la (ancienne) documentation Python (recettes pour 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 version actuelle, suggérée par 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)
Je suppose que la machine à remonter le temps de Guido fonctionne & # 8212; & # 8212; fonctionnera & # 8212; aura fonctionné & # 8212; fonctionnait à nouveau.
Ces solutions fonctionnent car [iter (iterable)] * n
(ou l'équivalent dans la version précédente) crée un itérateur, répété n
fois dans la liste. izip_longest
effectue ensuite un va-et-vient alternatif de "chaque". itérateur; Comme il s’agit du même itérateur, il est avancé à chaque appel, ce qui a pour résultat que chaque zip-roundrobin génère un tuple d’éléments n
.
Je sais que c'est un peu vieux, mais je ne comprends pas pourquoi personne n'a mentionné 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])]
Je suis surpris que personne n'ait pensé à utiliser le formulaire à deux arguments :
from itertools import islice
def chunk(it, size):
it = iter(it)
return iter(lambda: tuple(islice(it, size)), ())
Démo:
>>> list(chunk(range(14), 3))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13)]
Ceci fonctionne avec n’importe quelle version et est reproductible paresseusement. Il retourne des tuples plutôt que des itérateurs, mais je pense qu'il a néanmoins une certaine élégance. De plus, il ne couvre pas; si vous souhaitez un remplissage, une simple variation de ce qui précède suffira:
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)
Démo:
>>> 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')]
Comme les solutions basées sur izip_longest
, les pads ci-dessus précédents. Pour autant que je sache, il n’existe pas de recette d’outils d’itert à une ou deux lignes pour une fonction que éventuellement remplit. En combinant les deux approches ci-dessus, celle-ci est assez proche:
_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)
Démo:
>>> 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')]
Je crois que c’est le chunker le plus court proposé offrant un remplissage optionnel.
En tant que Tomasz Gandor "> observé , les deux chunkers de remplissage s’arrêteront inopinément s’ils rencontrent une longue séquence de valeurs de pad. Voici une dernière variante qui résout ce problème de manière raisonnable:_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))
Démo:
>>> list(chunk([1, 2, (), (), 5], 2))
[(1, 2), ((), ()), (5,)]
>>> list(chunk([1, 2, None, None, 5], 2, None))
[(1, 2), (None, None), (5, None)]
Voici un générateur qui fonctionne sur des itérables arbitraires:
def split_seq(iterable, size):
it = iter(iterable)
item = list(itertools.islice(it, size))
while item:
yield item
item = list(itertools.islice(it, size))
Exemple:
>>> 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))
Simple mais élégant
l = range(1, 1000)
print [l[x:x+10] for x in xrange(0, len(l), 10)]
ou si vous préférez:
chunks = lambda l, n: [l[x: x+n] for x in xrange(0, len(l), n)]
chunks(l, 10)
J'ai vu la réponse la plus impressionnante en python dans un dupliquer de cette question:
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)]
Vous pouvez créer n-tuple pour n’importe quel n. Si a = range (1, 15)
, le résultat sera:
[(1, 2, 3), (4, 5, 6), (7, 8, 9), (10, 11, 12), (13, 14, None)]
Si la liste est divisée de manière égale, vous pouvez remplacer zip_longest
par zip
, sinon le triplet (13, 14, Aucun)
être perdu. Python 3 est utilisé ci-dessus. Pour Python 2, utilisez izip_longest
.
Critique d'autres réponses ici:
Aucune de ces réponses ne sont des morceaux de taille égale, ils laissent tous un morceau à la fin, donc ils ne sont pas complètement équilibrés. Si vous utilisiez ces fonctions pour répartir le travail, vous avez intégré la perspective que l’une finisse probablement bien avant les autres, de sorte qu’elle resterait à ne rien faire pendant que les autres continuent de travailler dur.
Par exemple, la réponse principale actuelle se termine par:
[60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
[70, 71, 72, 73, 74]]
Je déteste ce crétin à la fin!
D'autres, comme list (groupeur (3, xrange (7)))
et chunk (xrange (7), 3)
renvoient tous deux: [ (0, 1, 2), (3, 4, 5), (6, Aucun, Aucun)]]
. Les None
ne sont que du remplissage, et plutôt inélégants à mon avis. Ils ne divisent pas les itérables de manière uniforme.
Pourquoi ne pouvons-nous pas mieux les partager?
Mes solutions
Voici une solution équilibrée, adaptée d'une fonction que j'avais utilisée en production (remarque en Python 3 visant à remplacer xrange
par plage
):
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)
Et j'ai créé un générateur qui fait la même chose si vous le mettez dans une liste:
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)]
Et enfin, puisque je vois que toutes les fonctions ci-dessus renvoient des éléments dans un ordre contigu (comme ils ont été donnés):
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)]
Sortie
Pour les tester:
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)))
Qui imprime:
[[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]]
Notez que le générateur contigu fournit des fragments de la même longueur que les deux autres, mais que les éléments sont dans l’ordre et qu’ils sont divisés de la même manière qu’il est possible de diviser une liste d’éléments discrets.
Si vous connaissez la taille de la liste:
def SplitList(mylist, chunk_size):
return [mylist[offs:offs+chunk_size] for offs in range(0, len(mylist), chunk_size)]
Si vous ne le faites pas (un itérateur):
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
Dans ce dernier cas, elle peut être reformulée de manière plus belle si vous pouvez être sûr que la séquence contient toujours un nombre entier de morceaux de taille donnée (c'est-à-dire qu'il n'y a pas de dernier morceau incomplet).
Si vous aviez une taille de morceau de 3 par exemple, vous pourriez faire:
zip(*[iterable[i::3] for i in range(3)])
source: http://code.activestate.com/recipes/303060-group -a-list-into-sequential-n-tuples /
J'utiliserais cela lorsque ma taille de bloc est un nombre fixe que je peux taper, par exemple. '3' et ne changera jamais.
La bibliothèque toolz dispose de la fonction partition
:
from toolz.itertoolz.core import partition
list(partition(2, [1, 2, 3, 4]))
[(1, 2), (3, 4)]
J'aime beaucoup la version du document Python proposée par tzot et J.F.Sebastian, mais il a deux inconvénients:
- ce n'est pas très explicite
- Je ne veux généralement pas de valeur de remplissage dans le dernier bloc
J'utilise beaucoup celui-ci dans mon code:
from itertools import islice
def chunks(n, iterable):
iterable = iter(iterable)
while True:
yield tuple(islice(iterable, n)) or iterable.next()
UPDATE: une version paresseuse:
from itertools import chain, islice
def chunks(n, iterable):
iterable = iter(iterable)
while True:
yield chain([next(iterable)], islice(iterable, n-1))
À ce stade, je pense qu'il nous faut un générateur récursif , juste au cas où ...
En python 2:
def chunks(li, n):
if li == []:
return
yield li[:n]
for e in chunks(li[n:], n):
yield e
En python 3:
def chunks(li, n):
if li == []:
return
yield li[:n]
yield from chunks(li[n:], n)
De même, en cas d’invasion massive par des extraterrestres, un générateur récursif décoré pourrait devenir pratique:
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
Vous pouvez également utiliser la fonction get_chunks
. de la utilspie
bibliothèque en tant que:
>>> 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]]
Vous pouvez installer utilspie
via pip:
sudo pip install utilspie
Clause de non-responsabilité: je suis le créateur de la bibliothèque utilspie .
>J'étais curieux de la performance de différentes approches et la voici:
Testé sur 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)
Résultats:
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]]
Où AA est un tableau, SS est la taille du bloc. Par exemple:
>>> 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
code:
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)
résultat:
[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]
heh, version en une ligne
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
utilisation:
seq = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for seq in split_seq(seq, 3):
print seq
Une autre version plus explicite.
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
Sans appeler len (), ce qui convient aux grandes listes:
def splitter(l, n):
i = 0
chunk = l[:n]
while chunk:
yield chunk
i += n
chunk = l[i:i+n]
Et ceci est pour iterables:
def isplitter(l, n):
l = iter(l)
chunk = list(islice(l, n))
while chunk:
yield chunk
chunk = list(islice(l, n))
La saveur fonctionnelle de ce qui précède:
def isplitter2(l, n):
return takewhile(bool,
(tuple(islice(start, n))
for start in repeat(iter(l))))
OU:
def chunks_gen_sentinel(n, seq):
continuous_slices = imap(islice, repeat(iter(seq)), repeat(0), repeat(n))
return iter(imap(tuple, continuous_slices).next,())
OU:
def chunks_gen_filter(n, seq):
continuous_slices = imap(islice, repeat(iter(seq)), repeat(0), repeat(n))
return takewhile(bool,imap(tuple, continuous_slices))
Une solution de plus
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]
>>>
Envisagez d'utiliser les matplotlib.cbook morceaux
.par exemple:
import matplotlib.cbook as cbook
segments = cbook.pieces(np.arange(20), 3)
for s in segments:
print s
Voir cette référence
>>> 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 )]
À ce stade, je pense que nous avons besoin de la fonction obligatoire anonyme / récursive.
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 [])
Puisque tout le monde ici parle d’itérateurs. La boltons
possède une méthode parfaite, appelée iterutils.chunked_iter
.
from boltons import iterutils
list(iterutils.chunked_iter(list(range(50)), 11))
Sortie:
[[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]]
Mais si vous ne voulez pas avoir pitié de la mémoire, vous pouvez utiliser old-way et stocker la liste complète
en premier lieu avec iterutils.chunked
.
Voici une liste d'approches supplémentaires:
Étant donné
import itertools as it
import collections as ct
import more_itertools as mit
iterable = range(11)
n = 3
Code
La bibliothèque 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)]
Références
-
zip_longest
(< a href = "https://stackoverflow.com/questions/312443/how-do-you-split-a-list-into-evenly-sized-chunks/3125186#3125186"> message lié , publication connexe ) -
setdeffault
(commandé résultats nécessite Python 3.6 +) -
collections.defaultdict
(les résultats commandés nécessitent Python 3.6 +) -
plus_itertools.chunked
a> ( lié ??posté ) -
plus_itertools.sliced ??
a> -
plus_itertools.grouper
a> ( article lié ) -
plus_itertools.windowed
a> (voir aussiéchelonné
,zip_offset
)
+ Une bibliothèque tierce qui implémente recettes d'itertools et plus. > pip installer more_itertools