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?

1651
demandé sur Community 2008-11-23 15:15:52

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

4
répondu macm 2014-11-14 09:48:42

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]]
4
répondu Claudiu 2017-05-23 12:18:29

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 .

4
répondu vishes_shell 2016-11-03 19:10:45

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 .

4
répondu AlexG 2016-11-20 04:32:29
>>> 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:)

3
répondu hcvst 2009-11-03 17:10:08
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
3
répondu J.T. Hurley 2012-06-23 15:19:32
  • 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]]
3
répondu balki 2013-09-13 19:17:08

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)] 
3
répondu Be Wake Pandey 2014-12-09 03:54:49

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 ) ]
3
répondu Moss 2015-01-13 12:58:57

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)]]
3
répondu Saksham Varma 2015-02-27 02:33:35

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) ]
3
répondu Flo 2015-04-17 18:48:35

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.)

3
répondu itub 2017-03-08 17:03:46

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
3
répondu George B 2017-11-03 12:38:56

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)
3
répondu Peter Gerdes 2017-12-19 11:04:34

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))

2
répondu CPBL 2017-05-23 12:02:59

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.

2
répondu Mikhail Lyundin 2015-09-18 17:54:39

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
2
répondu Evan Zamir 2015-10-16 22:09:29

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))
2
répondu Andrey Cizov 2017-07-06 22:24:14

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
2
répondu guyskk 2017-10-30 08:13:46

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)]
1
répondu Arthur Sult 2018-03-23 18:27:35
def chunked(iterable, size):
    chunk = ()

    for item in iterable:
        chunk += (item,)
        if len(chunk) % size == 0:
            yield chunk
            chunk = ()

    if chunk:
        yield chunk
0
répondu rectangletangle 2013-09-23 23:57:43

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

0
répondu Uday Kumar 2014-11-14 09:47:28

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)
0
répondu Adeojo Emmanuel IMM 2018-04-17 01:32:01

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]]
>>> 
0
répondu U9-Forward 2018-08-07 01:14:06

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]]

more_itertools +

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

+ une bibliothèque tierce qui met en œuvre itertools recipes et plus. > pip install more_itertools

0
répondu pylang 2018-08-26 01:40:11

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]]
-1
répondu koffein 2013-11-26 21:58:03

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

-3
répondu Shawn Zhang 2012-12-07 03:16:51