Comment diviser une liste en morceaux de taille égale?
j'ai une liste de longueur arbitraire, et je dois la diviser en morceaux de taille égale et opérer dessus. Il existe des moyens évidents de le faire, comme garder un compteur et deux listes, et lorsque la deuxième liste est remplie, ajoutez-la à la première liste et videz la deuxième liste pour la prochaine série de données, mais cela peut être extrêmement coûteux.
je me demandais si quelqu'un avait une bonne solution à cela pour les listes de n'importe quelle longueur, par exemple en utilisant des générateurs.
je cherchais quelque chose d'utile dans itertools
mais je n'ai rien trouvé de manifestement utile. Pourrait avez manqué, cependant.
question connexe: Quelle est la façon la plus" pythonique " d'itérer une liste en morceaux?
28 réponses
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
comme dans cette réponse , la réponse la plus votée laisse un "avorton" à la fin. Voici ma solution pour obtenir des morceaux de taille aussi égale que vous le pouvez, sans avortons. Il essaie essentiellement de choisir exactement le point fractionnaire où il devrait diviser la liste, mais il arrondit juste à l'entier le plus proche:
from __future__ import division # not needed in Python 3
def n_even_chunks(l, n):
"""Yield n as even chunks as possible from l."""
last = 0
for i in range(1, n+1):
cur = int(round(i * (len(l) / n)))
yield l[last:cur]
last = cur
démonstration:
>>> pprint.pprint(list(n_even_chunks(list(range(100)), 9)))
[[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, 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]]
>>> pprint.pprint(list(n_even_chunks(list(range(100)), 11)))
[[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, 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]]
Comparez avec le premier voté chunks
réponse:
>>> pprint.pprint(list(chunks(list(range(100)), 100//9)))
[[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, 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]]
>>> pprint.pprint(list(chunks(list(range(100)), 100//11)))
[[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, 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]]
depuis que tout le monde ici parle d'itérateurs. boltons
a la méthode parfaite pour cela, appelé 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 le plein list
en premier lieu avec iterutils.chunked
.
vous pouvez utiliser la fonction array_split de num PY par exemple, np.array_split(np.array(data), 20)
pour se diviser en 20 morceaux de taille presque égale.
pour s'assurer que les morceaux sont exactement égaux en taille, utilisez np.split
.
>>> f = lambda x, n, acc=[]: f(x[n:], n, acc+[(x[:n])]) if x else acc
>>> f("Hallo Welt", 3)
['Hal', 'lo ', 'Wel', 't']
>>>
si vous êtes entre parenthèses - j'ai pris un livre sur Erlang:)
def chunk(lst):
out = []
for x in xrange(2, len(lst) + 1):
if not len(lst) % x:
factor = len(lst) / x
break
while lst:
out.append([lst.pop(0) for x in xrange(factor)])
return out
- Fonctionne avec n'importe quel itérable
- Intérieure de données est générateur d'objet (pas de liste)
- One liner
In [259]: get_in_chunks = lambda itr,n: ( (v for _,v in g) for _,g in itertools.groupby(enumerate(itr),lambda (ind,_): ind/n)) In [260]: list(list(x) for x in get_in_chunks(range(30),7)) Out[260]: [[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]]
j'ai écrit une petite bibliothèque expressément à cette fin, disponible ici . La fonction chunked
de la Bibliothèque est particulièrement efficace car elle est implémentée en tant que "générateur , de sorte qu'une quantité importante de mémoire peut être sauvegardée dans certaines situations. Il ne s'appuie pas non plus sur la notation slice, de sorte que n'importe quel itérateur arbitraire peut être utilisé.
import iterlib
print list(iterlib.chunked(xrange(1, 1000), 10))
# prints [(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), (11, 12, 13, 14, 15, 16, 17, 18, 19, 20), ...]
laisser r être la taille du morceau et L être la liste initiale, vous pouvez le faire.
chunkL = [ [i for i in L[r*k:r*(k+1)] ] for k in range(len(L)/r)]
n'est Pas exactement la même, mais toujours belle
def chunks(l, chunks):
return zip(*[iter(l)]*chunks)
l = range(1, 1000)
print chunks(l, 10) -> [ ( 1..10 ), ( 11..20 ), .., ( 991..999 ) ]
use liste comprehensions:
l = [1,2,3,4,5,6,7,8,9,10,11,12]
k = 5 #chunk size
print [tuple(l[x:y]) for (x, y) in [(x, x+k) for x in range(0, len(l), k)]]
la réponse ci-dessus (par koffein) a un petit problème: la liste est toujours divisée en un nombre égal de divisions, pas un nombre égal d'articles par partition. C'est ma version. Le "/ / chs + 1 " tient compte du fait que le nombre d'éléments peut ne pas être divisible exactement par la taille de la partition, de sorte que la dernière partition ne sera que partiellement remplie.
# Given 'l' is your list
chs = 12 # Your chunksize
partitioned = [ l[i*chs:(i*chs)+chs] for i in range((len(l) // chs)+1) ]
Voici une idée en utilisant itertools.groupby:
def chunks(l, n):
c = itertools.count()
return (it for _, it in itertools.groupby(l, lambda x: next(c)//n))
renvoie un générateur de générateurs. Si vous voulez une liste de listes, remplacez la dernière ligne par
return [list(it) for _, it in itertools.groupby(l, lambda x: next(c)//n)]
exemple de liste de retour:
>>> chunks('abcdefghij', 4)
[['a', 'b', 'c', 'd'], ['e', 'f', 'g', 'h'], ['i', 'j']]
(donc oui, cela souffre du" problème de l'avorton", qui peut être ou non un problème dans une situation donnée.)
Je ne pense pas avoir vu cette option, donc juste pour ajouter une autre:)):
def chunks(iterable, chunk_size):
i = 0;
while i < len(iterable):
yield iterable[i:i+chunk_size]
i += chunk_size
j'ai une solution ci-dessous qui fonctionne mais plus important que cette solution est quelques commentaires sur d'autres approches. Premièrement, une bonne solution ne devrait pas exiger qu'une boucle traverse les sous-itérateurs dans l'ordre. Si je cours
g = paged_iter(list(range(50)), 11))
i0 = next(g)
i1 = next(g)
list(i1)
list(i0)
la sortie appropriée pour la dernière commande est
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
pas
[]
comme la plupart des solutions itertools ICI RETOURNER. Ce n'est pas simplement l'habitude ennuyeux restriction sur l'accès aux itérateurs dans l'ordre. Imaginez un consommateur essayant de nettoyer des données mal entrées qui inversent l'ordre approprié des blocs de 5, c'est-à-dire que les données ressemblent à [B5, A5, D5, C5] et devraient ressembler à [A5, B5, C5, D5] (où A5 n'est que cinq éléments et non une sous-liste). Ce consommateur regarderait le comportement revendiqué de la fonction de regroupement et n'hésiterait pas à écrire une boucle comme
i = 0
out = []
for it in paged_iter(data,5)
if (i % 2 == 0):
swapped = it
else:
out += list(it)
out += list(swapped)
i = i + 1
cela produira des résultats mystérieusement erronés si vous supposez sournoisement que les sous-itérateurs sont toujours pleinement utilisés dans l'ordre. C'est encore pire si vous voulez séparer les éléments des morceaux.
deuxièmement, un nombre décent de solutions proposées reposent implicitement sur le fait que les itérateurs ont un ordre déterministe (ils ne sont pas par exemple définis) et bien que certaines des solutions utilisant islice puissent être correctes, cela m'inquiète.
Troisièmement, l'approche itertools grouper fonctionne mais la recette repose sur le comportement interne des fonctions zip_longest (ou zip) qui ne font pas partie de leur comportement publié. En particulier, la fonction grouper ne fonctionne que parce que dans zip_longest(i0...in) la fonction suivante est toujours appelée dans l'ordre suivant(i0), suivant(i1),... prochaine(en) avant de recommencer. Lorsque grouper passe n copies du même objet iterator, il s'appuie sur ce comportement.
enfin, bien que la solution ci-dessous peut être améliorée si vous faites l'hypothèse critiquée ci-dessus que les sous-itérateurs sont accessibles dans l'ordre et entièrement perused sans cette hypothèse on doit implicitement (via la chaîne d'appel) ou explicitement (via des deques ou d'autres structures de données) stocker des éléments pour chaque subiterator quelque part. Ne perdez donc pas de temps (comme je l'ai fait) en présumant que l'on pourrait s'en sortir avec une astuce.
def paged_iter(iterat, n):
itr = iter(iterat)
deq = None
try:
while(True):
deq = collections.deque(maxlen=n)
for q in range(n):
deq.append(next(itr))
yield (i for i in deq)
except StopIteration:
yield (i for i in deq)
comme @AaronHall je suis arrivé ici à la recherche de morceaux grossièrement de taille égale. Il y a différentes interprétations de cela. Dans mon cas, si la taille désirée est N, je voudrais que chaque groupe soit de taille> = N. Ainsi, les orphelins qui sont créés dans la plupart des ci-dessus devraient être redistribués à d'autres groupes.
cela peut être fait en utilisant:
def nChunks(l, n):
""" Yield n successive chunks from l.
Works for lists, pandas dataframes, etc
"""
newn = int(1.0 * len(l) / n + 0.5)
for i in xrange(0, n-1):
yield l[i*newn:i*newn+newn]
yield l[n*newn-newn:]
(de fractionnant une liste de en N parties de longueur approximativement égale ) par tout simplement en l'appelant comme nChunks(l,l/n) ou nChunks(l,étage(l/n))
je suis arrivé à la solution suivante sans création d'objet de liste temporaire, qui devrait fonctionner avec n'importe quel objet itérable. Veuillez noter que cette version pour Python 2.x:
def chunked(iterable, size):
stop = []
it = iter(iterable)
def _next_chunk():
try:
for _ in xrange(size):
yield next(it)
except StopIteration:
stop.append(True)
return
while not stop:
yield _next_chunk()
for it in chunked(xrange(16), 4):
print list(it)
sortie:
[0, 1, 2, 3]
[4, 5, 6, 7]
[8, 9, 10, 11]
[12, 13, 14, 15]
[]
comme vous pouvez voir si len(iterable) % size == 0 alors nous avons un objet iterator vide supplémentaire. Mais je ne pense pas que ce soit un gros problème.
depuis que j'ai dû faire quelque chose comme ça, voici ma solution avec un générateur et une taille de lot:
def pop_n_elems_from_generator(g, n):
elems = []
try:
for idx in xrange(0, n):
elems.append(g.next())
return elems
except StopIteration:
return elems
cela fonctionne en v2 / v3, est inlineable, basé sur le générateur et utilise seulement la bibliothèque standard:
import itertools
def split_groups(iter_in, group_size):
return ((x for _, x in item) for _, item in itertools.groupby(enumerate(iter_in), key=lambda x: x[0] // group_size))
Pas de magie, mais simple et correcte:
def chunks(iterable, n):
"""Yield successive n-sized chunks from iterable."""
values = []
for i, item in enumerate(iterable, 1):
values.append(item)
if i % n == 0:
yield values
values = []
if values:
yield values
Je n'aime pas l'idée de séparer les éléments par la taille du morceau, p.ex. script peut dévider 101 à 3 morceaux comme [50, 50, 1]. Pour mes besoins j'ai eu besoin de diviser proportionnellement, et de garder l'ordre même. J'ai d'abord écrit mon propre scénario, qui fonctionne très bien, et c'est très simple. Mais j'ai vu plus tard cette réponse , où le script est meilleur que le mien, je reccomend. Voici mon script:
def proportional_dividing(N, n):
"""
N - length of array (bigger number)
n - number of chunks (smaller number)
output - arr, containing N numbers, diveded roundly to n chunks
"""
arr = []
if N == 0:
return arr
elif n == 0:
arr.append(N)
return arr
r = N // n
for i in range(n-1):
arr.append(r)
arr.append(N-r*(n-1))
last_n = arr[-1]
# last number always will be r <= last_n < 2*r
# when last_n == r it's ok, but when last_n > r ...
if last_n > r:
# ... and if difference too big (bigger than 1), then
if abs(r-last_n) > 1:
#[2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 7] # N=29, n=12
# we need to give unnecessary numbers to first elements back
diff = last_n - r
for k in range(diff):
arr[k] += 1
arr[-1] = r
# and we receive [3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2]
return arr
def split_items(items, chunks):
arr = proportional_dividing(len(items), chunks)
splitted = []
for chunk_size in arr:
splitted.append(items[:chunk_size])
items = items[chunk_size:]
print(splitted)
return splitted
items = [1,2,3,4,5,6,7,8,9,10,11]
chunks = 3
split_items(items, chunks)
split_items(['a','b','c','d','e','f','g','h','i','g','k','l', 'm'], 3)
split_items(['a','b','c','d','e','f','g','h','i','g','k','l', 'm', 'n'], 3)
split_items(range(100), 4)
split_items(range(99), 4)
split_items(range(101), 4)
et sortie:
[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11]]
[['a', 'b', 'c', 'd'], ['e', 'f', 'g', 'h'], ['i', 'g', 'k', 'l', 'm']]
[['a', 'b', 'c', 'd', 'e'], ['f', 'g', 'h', 'i', 'g'], ['k', 'l', 'm', 'n']]
[range(0, 25), range(25, 50), range(50, 75), range(75, 100)]
[range(0, 25), range(25, 50), range(50, 75), range(75, 99)]
[range(0, 25), range(25, 50), range(50, 75), range(75, 101)]
def chunked(iterable, size):
chunk = ()
for item in iterable:
chunk += (item,)
if len(chunk) % size == 0:
yield chunk
chunk = ()
if chunk:
yield chunk
en utilisant la Liste des Compréhensions de python
[range(t,t+10) for t in range(1,1000,10)]
[[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],....
....[981, 982, 983, 984, 985, 986, 987, 988, 989, 990],
[991, 992, 993, 994, 995, 996, 997, 998, 999, 1000]]
visite ce lien savoir sur les Interprétations de la Liste
essayez cette
def split(arr, size):
L = len(arr)
assert 0 < size <= L
s, r = divmod(L, size)
t = s + 1
a = ([arr[p:p+t] for p in range(0, r*t, t)] + [arr[p:p+s] for p in range(r*t, L, s)])
return a
b = [2,8,9,0,7,7]
n = 2
print split(b,n)
ou do np.split
:
>>> import numpy as np
>>> l=[1,2,3,4]
>>> def chunks(l,n):
return [i.tolist() for i in np.split(np.array(l),n)]
>>> chunks(l,2)
[[1, 2], [3, 4]]
>>>
Voici une liste d'approches supplémentaires:
Donné
import itertools as it
import collections as ct
import more_itertools as mit
iterable = range(11)
n = 3
Code
De 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
( related post , poste ) -
setdefault
(commandé résultats nécessite Python 3.6+) -
collections.defaultdict
(commandé résultats nécessite Python 3.6+) -
more_itertools.chunked
( liées posté ) -
more_itertools.sliced
-
more_itertools.grouper
( related post ) -
more_itertools.windowed
(voir aussistagger
,zip_offset
)
+ une bibliothèque tierce qui met en œuvre itertools recipes et plus. > pip install more_itertools
Oui, c'est une vieille question, mais j'ai dû poster celle-ci, parce qu'elle est même un peu plus courte que les semblables. Oui, le résultat semble brouillé, mais s'il est à peu près de même longueur...
>>> n = 3 # number of groups
>>> biglist = range(30)
>>>
>>> [ biglist[i::n] for i in xrange(n) ]
[[0, 3, 6, 9, 12, 15, 18, 21, 24, 27],
[1, 4, 7, 10, 13, 16, 19, 22, 25, 28],
[2, 5, 8, 11, 14, 17, 20, 23, 26, 29]]
on N'utilisez tee fonction de() en vertu de itertools ?
http://docs.python.org/2/library/itertools.html#itertools.tee
>>> import itertools
>>> itertools.tee([1,2,3,4,5,6],3)
(<itertools.tee object at 0x02932DF0>, <itertools.tee object at 0x02932EB8>, <itertools.tee object at 0x02932EE0>)
cela divisera la liste en 3 itérateurs, boucle l'itérateur obtiendra la sous-liste avec la longueur égale