Division d'une liste en N parties d'une longueur à peu près égale

Quelle est la meilleure façon de diviser une liste en à peu près parties égales? Par exemple, si la liste contient 7 éléments et est divisé en 2 parties, nous voulons obtenir 3 éléments dans une partie, et l'autre doit avoir 4 éléments.

je cherche quelque chose comme even_split(L, n) qui casse L en n pièces.

def chunks(L, n):
    """ Yield successive n-sized chunks from L.
    """
    for i in xrange(0, len(L), n):
        yield L[i:i+n]

Le code ci-dessus donne des morceaux de 3, plutôt que 3 morceaux. Je pourrais tout simplement transpose (itération en cours et prendre le premier élément de chaque colonne, appel que la première partie, puis prendre la deuxième et de la mettre dans la partie deux, etc), mais qui détruit l'ordre des éléments.

91
demandé sur wim 2010-01-25 06:19:28

22 réponses

En voici un qui pourrait marcher:

def chunkIt(seq, num):
    avg = len(seq) / float(num)
    out = []
    last = 0.0

    while last < len(seq):
        out.append(seq[int(last):int(last + avg)])
        last += avg

    return out

test:

>>> chunkIt(range(10), 3)
[[0, 1, 2], [3, 4, 5], [6, 7, 8, 9]]
>>> chunkIt(range(11), 3)
[[0, 1, 2], [3, 4, 5, 6], [7, 8, 9, 10]]
>>> chunkIt(range(12), 3)
[[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]]
47
répondu Max Shawabkeh 2017-09-26 20:04:52

vous pouvez l'écrire assez simplement comme générateur de liste:

def split(a, n):
    k, m = divmod(len(a), n)
    return (a[i * k + min(i, m):(i + 1) * k + min(i + 1, m)] for i in xrange(n))

exemple:

>>> list(split(range(11), 3))
[[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10]]
82
répondu tixxit 2016-12-05 15:39:26

aussi longtemps que vous ne voulez rien de stupide comme des morceaux continus:

>>> def chunkify(lst,n):
...     return [lst[i::n] for i in xrange(n)]
... 
>>> chunkify(range(13), 3)
[[0, 3, 6, 9, 12], [1, 4, 7, 10], [2, 5, 8, 11]]
81
répondu job 2017-09-14 21:57:07

C'est la raison d'être pour numpy.array_split *:

>>> L
[0, 1, 2, 3, 4, 5, 6, 7]
>>> print(*np.array_split(L, 3))
[0 1 2] [3 4 5] [6 7]
>>> print(*np.array_split(range(10), 4))
[0 1 2] [3 4 5] [6 7] [8 9]

*crédit à Zéro Pirée dans la salle 6

55
répondu wim 2017-09-14 21:56:45

changer le code pour donner n morceaux plutôt que des morceaux de n :

def chunks(l, n):
    """ Yield n successive chunks from l.
    """
    newn = int(len(l) / n)
    for i in xrange(0, n-1):
        yield l[i*newn:i*newn+newn]
    yield l[n*newn-newn:]

l = range(56)
three_chunks = chunks (l, 3)
print three_chunks.next()
print three_chunks.next()
print three_chunks.next()

qui donne:

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

cela assignera les éléments supplémentaires au groupe final qui n'est pas parfait mais bien dans votre spécification de" à peu près n parties égales": -) par cela, je veux dire 56 éléments seraient mieux que (19,19,18) alors que cela donne (18,18,20).

vous pouvez obtenir la sortie plus équilibrée avec le code suivant:

#!/usr/bin/python
def chunks(l, n):
    """ Yield n successive chunks from l.
    """
    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:]

l = range(56)
three_chunks = chunks (l, 3)
print three_chunks.next()
print three_chunks.next()
print three_chunks.next()

qui produit:

[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]
14
répondu paxdiablo 2010-01-25 05:25:41

si vous divisez n éléments en gros k morceaux vous pouvez faire n % k Morceaux 1 élément plus grand que les autres morceaux pour distribuer les éléments supplémentaires.

le code suivant vous donnera la longueur des morceaux:

[(n // k) + (1 if i < (n % k) else 0) for i in range(k)]

exemple: n=11, k=3 résultats dans [4, 4, 3]

vous pouvez alors calculer facilement les indicateurs de départ pour les morceaux:

[i * (n // k) + min(i, n % k) for i in range(k)]

exemple: n=11, k=3 résultats dans [0, 4, 8]

en utilisant le i+1 TH morceau comme limite, nous obtenons que le i th morceau de la liste l avec len n est

l[i * (n // k) + min(i, n % k):(i+1) * (n // k) + min(i+1, n % k)]

comme dernière étape créez une liste à partir de tous les morceaux en utilisant la compréhension de liste:

[l[i * (n // k) + min(i, n % k):(i+1) * (n // k) + min(i+1, n % k)] for i in range(k)]

exemple: n=11, k=3, l=range(n) résultats dans [range(0, 4), range(4, 8), range(8, 11)]

5
répondu MaPePeR 2016-05-24 12:54:12

En voici un qui ajoute None pour rendre les listes de longueur égale

>>> from itertools import izip_longest
>>> def chunks(l, n):
    """ Yield n successive chunks from l. Pads extra spaces with None
    """
    return list(zip(*izip_longest(*[iter(l)]*n)))

>>> l=range(54)

>>> chunks(l,3)
[(0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51), (1, 4, 7, 10, 13, 16, 19, 22, 25, 28, 31, 34, 37, 40, 43, 46, 49, 52), (2, 5, 8, 11, 14, 17, 20, 23, 26, 29, 32, 35, 38, 41, 44, 47, 50, 53)]

>>> chunks(l,4)
[(0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52), (1, 5, 9, 13, 17, 21, 25, 29, 33, 37, 41, 45, 49, 53), (2, 6, 10, 14, 18, 22, 26, 30, 34, 38, 42, 46, 50, None), (3, 7, 11, 15, 19, 23, 27, 31, 35, 39, 43, 47, 51, None)]

>>> chunks(l,5)
[(0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50), (1, 6, 11, 16, 21, 26, 31, 36, 41, 46, 51), (2, 7, 12, 17, 22, 27, 32, 37, 42, 47, 52), (3, 8, 13, 18, 23, 28, 33, 38, 43, 48, 53), (4, 9, 14, 19, 24, 29, 34, 39, 44, 49, None)]
3
répondu John La Rooy 2010-01-25 03:42:29

regarder numpy.split :

>>> a = numpy.array([1,2,3,4])
>>> numpy.split(a, 2)
[array([1, 2]), array([3, 4])]
2
répondu dalloliogm 2010-02-10 09:19:37

voici ma solution:

def chunks(l, amount):
    if amount < 1:
        raise ValueError('amount must be positive integer')
    chunk_len = len(l) // amount
    leap_parts = len(l) % amount
    remainder = amount // 2  # make it symmetrical
    i = 0
    while i < len(l):
        remainder += leap_parts
        end_index = i + chunk_len
        if remainder >= amount:
            remainder -= amount
            end_index += 1
        yield l[i:end_index]
        i = end_index

produit

    >>> list(chunks([1, 2, 3, 4, 5, 6, 7], 3))
    [[1, 2], [3, 4, 5], [6, 7]]
2
répondu leotrubach 2017-03-13 13:32:45

voici un générateur qui peut gérer n'importe quel nombre (entier) positif de morceaux. Si le nombre de morceaux est plus grand que la liste d'entrées longueur de certains morceaux seront vides. Cet algorithme alterne entre des morceaux courts et des morceaux longs plutôt que de les séparer.

j'ai aussi inclus du code pour tester la fonction ragged_chunks .

''' Split a list into "ragged" chunks

    The size of each chunk is either the floor or ceiling of len(seq) / chunks

    chunks can be > len(seq), in which case there will be empty chunks

    Written by PM 2Ring 2017.03.30
'''

def ragged_chunks(seq, chunks):
    size = len(seq)
    start = 0
    for i in range(1, chunks + 1):
        stop = i * size // chunks
        yield seq[start:stop]
        start = stop

# test

def test_ragged_chunks(maxsize):
    for size in range(0, maxsize):
        seq = list(range(size))
        for chunks in range(1, size + 1):
            minwidth = size // chunks
            #ceiling division
            maxwidth = -(-size // chunks)
            a = list(ragged_chunks(seq, chunks))
            sizes = [len(u) for u in a]
            deltas = all(minwidth <= u <= maxwidth for u in sizes)
            assert all((sum(a, []) == seq, sum(sizes) == size, deltas))
    return True

if test_ragged_chunks(100):
    print('ok')

nous pouvons rendre ce légèrement plus efficace en exportant la multiplication dans l'appel range , mais je pense que la version précédente est plus lisible (et plus sèche).

def ragged_chunks(seq, chunks):
    size = len(seq)
    start = 0
    for i in range(size, size * chunks + 1, size):
        stop = i // chunks
        yield seq[start:stop]
        start = stop
2
répondu PM 2Ring 2017-03-30 10:04:49

voir more_itertools.divide :

n = 2

[list(x) for x in mit.divide(n, range(5, 11))]
# [[5, 6, 7], [8, 9, 10]]

[list(x) for x in mit.divide(n, range(5, 12))]
# [[5, 6, 7, 8], [9, 10, 11]]

installer via > pip install more_itertools .

2
répondu pylang 2018-07-16 20:41:47

la Mise en oeuvre utilisation de numpy.méthode linspace.

spécifiez juste le nombre de parties dans lesquelles vous voulez que le tableau soit divisé.Les divisions seront de taille équivalente.

exemple :

import numpy as np   
a=np.arange(10)
print "Input array:",a 
parts=3
i=np.linspace(np.min(a),np.max(a)+1,parts+1)
i=np.array(i,dtype='uint16') # Indices should be floats
split_arr=[]
for ind in range(i.size-1):
    split_arr.append(a[i[ind]:i[ind+1]]
print "Array split in to %d parts : "%(parts),split_arr

donne:

Input array: [0 1 2 3 4 5 6 7 8 9]
Array split in to 3 parts :  [array([0, 1, 2]), array([3, 4, 5]), array([6, 7, 8, 9])]
1
répondu amit12690 2015-04-21 05:25:40

à l'Aide de la liste de compréhension:

def divide_list_to_chunks(list_, n):
    return [list_[start::n] for start in range(n)]
1
répondu liscju 2015-11-14 15:06:48

cela fera la division par une seule expression:

>>> myList = range(18)
>>> parts = 5
>>> [myList[(i*len(myList))//parts:((i+1)*len(myList))//parts] for i in range(parts)]
[[0, 1, 2], [3, 4, 5, 6], [7, 8, 9], [10, 11, 12, 13], [14, 15, 16, 17]]

la liste dans cet exemple a la taille 18 et est divisée en 5 parties. La taille des pièces diffère pas de plus d'un élément.

1
répondu bitagoras 2017-11-20 16:43:31

ma solution, facile à comprendre

def split_list(lst, n):
    splitted = []
    for i in reversed(range(1, n + 1)):
        split_point = len(lst)//i
        splitted.append(lst[:split_point])
        lst = lst[split_point:]
    return splitted

et la plus courte Doublure sur cette page (écrite par ma fille)

def split(l, n):
    return [l[int(i*len(l)/n):int((i+1)*len(l)/n-1)] for i in range(n)]
1
répondu Chłop Z Lasu 2018-02-07 21:56:49

une autre façon serait quelque chose comme cela, l'idée ici est d'utiliser grouper, mais se débarrasser de None . Dans ce cas, nous aurons tous "small_parts" formé à partir d'éléments lors de la première partie de la liste, et "larger_parts' de la fin de la liste. Longueur de "grandes parties" est len(small_parts) + 1. Nous devons considérer x comme deux sous-parties différentes.

from itertools import izip_longest

import numpy as np

def grouper(n, iterable, fillvalue=None): # This is grouper from itertools
    "grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    return izip_longest(fillvalue=fillvalue, *args)

def another_chunk(x,num):
    extra_ele = len(x)%num #gives number of parts that will have an extra element 
    small_part = int(np.floor(len(x)/num)) #gives number of elements in a small part

    new_x = list(grouper(small_part,x[:small_part*(num-extra_ele)]))
    new_x.extend(list(grouper(small_part+1,x[small_part*(num-extra_ele):])))

    return new_x

La façon dont je l'ai mis en place renvoie une liste de tuples:

>>> x = range(14)
>>> another_chunk(x,3)
[(0, 1, 2, 3), (4, 5, 6, 7, 8), (9, 10, 11, 12, 13)]
>>> another_chunk(x,4)
[(0, 1, 2), (3, 4, 5), (6, 7, 8, 9), (10, 11, 12, 13)]
>>> another_chunk(x,5)
[(0, 1), (2, 3, 4), (5, 6, 7), (8, 9, 10), (11, 12, 13)]
>>> 
0
répondu Akavall 2012-04-22 15:04:14

voici une autre variante qui répand les éléments" restants " également parmi tous les morceaux, un à la fois jusqu'à ce qu'il n'y en ait plus aucun. Dans cette implémentation, les plus gros morceaux se produisent au début du processus.

def chunks(l, k):
  """ Yield k successive chunks from l."""
  if k < 1:
    yield []
    raise StopIteration
  n = len(l)
  avg = n/k
  remainders = n % k
  start, end = 0, avg
  while start < n:
    if remainders > 0:
      end = end + 1
      remainders = remainders - 1
    yield l[start:end]
    start, end = end, end+avg

par exemple, générer 4 morceaux à partir d'une liste de 14 éléments:

>>> list(chunks(range(14), 4))
[[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10], [11, 12, 13]]
>>> map(len, list(chunks(range(14), 4)))
[4, 4, 3, 3]
0
répondu jreyes 2013-09-22 19:44:39

la même chose que job réponse, mais prend en compte des listes de taille inférieure au nombre de tronçonneuses.

def chunkify(lst,n):
    [ lst[i::n] for i in xrange(n if n < len(lst) else len(lst)) ]

si n (nombre de morceaux) est de 7, et lst (la liste de fracture) est [1, 2, 3] les morceaux sont [[0], [1], [2]] au lieu de [[0], [1], [2], [], [], [], []]

0
répondu Ilya Tuvschev 2017-05-23 11:54:50

vous pouvez également utiliser:

split=lambda x,n: x if not x else [x[:n]]+[split([] if not -(len(x)-n) else x[-(len(x)-n):],n)][0]

split([1,2,3,4,5,6,7,8,9],2)

[[1, 2], [3, 4], [5, 6], [7, 8], [9]]
0
répondu Carlos del Ojo 2017-03-13 13:16:24
#!/usr/bin/python


first_names = ['Steve', 'Jane', 'Sara', 'Mary','Jack','Bob', 'Bily', 'Boni', 'Chris','Sori', 'Will', 'Won','Li']

def chunks(l, n):
for i in range(0, len(l), n):
    # Create an index range for l of n items:
    yield l[i:i+n]

result = list(chunks(first_names, 5))
print result

tiré de ce lien , et c'est ce qui m'a aidé. J'ai eu une liste pré-définie.

0
répondu swateek 2018-09-05 04:28:24

dis que tu veux te diviser en 5 parties:

p1, p2, p3, p4, p5 = np.split(df, 5)
0
répondu Danil 2018-10-03 16:36:30

arrondir l'espace lin et l'utiliser comme un index est une solution plus facile que ce que propose amit12690.

function chunks=chunkit(array,num)

index = round(linspace(0,size(array,2),num+1));

chunks = cell(1,num);

for x = 1:num
chunks{x} = array(:,index(x)+1:index(x+1));
end
end
-1
répondu galliwuzz 2016-07-20 13:29:18