web-dev-qa-db-fra.com

Fractionner une liste en N parties de longueur approximativement égale

Quel est le meilleur moyen de diviser une liste en à peu près parties égales? Par exemple, si la liste contient 7 éléments et est scindée en 2 parties, nous souhaitons obtenir 3 éléments dans une partie et l’autre devrait en comporter 4.

Je cherche quelque chose comme even_split(L, n) qui divise L en n parties.

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 simplement transposer (itérer dessus et prendre le premier élément de chaque colonne, appeler cette première partie, puis prendre le second et le mettre dans la deuxième partie, etc.), mais cela détruit l'ordre des éléments.

100
user248237dfsf

En voici un qui pourrait fonctionner:

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

Essai:

>>> 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]]
56
Max Shawabkeh

Vous pouvez l'écrire assez simplement en tant que 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]]
118
tixxit

Tant 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]]
93
job

Ceci 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 à Zero Piraeus in room 6

71
wim

Modification du code pour générer des fragments n au lieu de 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 affectera les éléments supplémentaires au groupe final, ce qui n'est pas parfait mais correspond bien à votre spécification "à peu près N parties égales" :-) (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]
15
paxdiablo

Si vous divisez des éléments n en gros morceaux k, vous pouvez créer des éléments n % k plus gros qu'un élément plus grand que les autres afin de répartir les éléments supplémentaires. 

Le code suivant vous donnera la longueur pour les morceaux:

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

Exemple: n=11, k=3 a pour résultat [4, 4, 3]

Vous pouvez alors facilement calculer le début indiqué pour les morceaux:

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

Exemple: n=11, k=3 a pour résultat [0, 4, 8]

En utilisant le bloc i+1th comme limite, nous obtenons que le ith fragment 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)]

Enfin, 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) entraîne [range(0, 4), range(4, 8), range(8, 11)]

5
MaPePeR

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

Installez via > pip install more_itertools .

3
pylang

En voici une qui ajoute None pour rendre les listes égales

>>> 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
John La Rooy

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
leotrubach

Implémentation utilisant la méthode numpy.linspace. 

Spécifiez simplement le nombre de parties dans lesquelles vous souhaitez diviser le tableau. Les divisions auront une taille presque égale.

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])]
2
amit12690

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

J'ai également 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 cette 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 DRYer).

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
PM 2Ring

Cela fera la scission 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 ne diffère pas plus d'un élément.

2
bitagoras

Regardez numpy.split :

>>> a = numpy.array([1,2,3,4])
>>> numpy.split(a, 2)
[array([1, 2]), array([3, 4])]
2
dalloliogm

dites que vous voulez diviser en 5 parties:

p1, p2, p3, p4, p5 = np.split(df, 5)
1
Danil

Utilisation de la compréhension de liste:

def divide_list_to_chunks(list_, n):
    return [list_[start::n] for start in range(n)]
1
liscju

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 le plus court one-line sur cette page (écrit 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
Chłop Z Lasu

Voici une autre variante qui répartit les éléments "restants" de manière uniforme entre tous les morceaux, un par un jusqu'à ce qu'il n'en reste plus. Dans cette mise en œuvre, les 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érez 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
jreyes

Identique à job's answer, mais prend en compte les listes dont la taille est inférieure au nombre de tronçons.

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 7 et lst (la liste à diviser) est [1, 2, 3], les morceaux sont [[0], [1], [2]] au lieu de [[0], [1 ], [2], [], [], [], []]

0
Ilya Tuvschev

Vous pouvez aussi 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
Carlos del Ojo

Une autre façon serait comme ceci, l’idée ici est d’utiliser le mérou, mais de se débarrasser de None. Dans ce cas, toutes les «petites pièces» seront formées à partir d'éléments de la première partie de la liste et les «plus grandes pièces» de la dernière partie de la liste. La longueur des '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 configuré retourne 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
Akavall

ce code fonctionne pour moi (compatible Python3):

def chunkify(tab, num):
    return [tab[i*num: i*num+num] for i in range(len(tab)//num+(1 if len(tab)%num else 0))]

exemple (pour bytearray tapez, mais cela fonctionne aussi pour list s):

b = bytearray(b'\x01\x02\x03\x04\x05\x06\x07\x08')
>>> chunkify(b,3)
[bytearray(b'\x01\x02\x03'), bytearray(b'\x04\x05\x06'), bytearray(b'\x07\x08')]
>>> chunkify(b,4)
[bytearray(b'\x01\x02\x03\x04'), bytearray(b'\x05\x06\x07\x08')]
0
grafi71

Celui-ci fournit des morceaux de longueur <= n,> = 0

def

 chunkify(lst, n):
    num_chunks = int(math.ceil(len(lst) / float(n))) if n < len(lst) else 1
    return [lst[n*i:n*(i+1)] for i in range(num_chunks)]

par exemple

>>> chunkify(range(11), 3)
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10]]
>>> chunkify(range(11), 8)
[[0, 1, 2, 3, 4, 5, 6, 7], [8, 9, 10]]
0
#!/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

Choisi dans ce link , et c’est ce qui m’a aidé. J'ai eu une liste prédéfinie.

0
swateek

J'ai essayé la plupart des solutions, mais elles ne fonctionnaient pas pour mon cas. Je crée donc une nouvelle fonction qui fonctionne dans la plupart des cas et pour tout type de tableau:

import math

def chunkIt(seq, num):
    seqLen = len(seq)
    total_chunks = math.ceil(seqLen / num)
    items_per_chunk = num
    out = []
    last = 0

    while last < seqLen:
        out.append(seq[last:(last + items_per_chunk)])
        last += items_per_chunk

    return out
0
Ângelo Polotto

J'ai écrit du code dans ce cas moi-même:

def chunk_ports(port_start, port_end, portions):
    if port_end < port_start:
        return None

    total = port_end - port_start + 1

    fractions = int(math.floor(float(total) / portions))

    results = []

    # No enough to chuck.
    if fractions < 1:
        return None

    # Reverse, so any additional items would be in the first range.
    _e = port_end
    for i in range(portions, 0, -1):
        print "i", i

        if i == 1:
            _s = port_start
        else:
            _s = _e - fractions + 1

        results.append((_s, _e))

        _e = _s - 1

    results.reverse()

    return results

divide_ports (1, 10, 9) reviendrait

[(1, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7), (8, 8), (9, 9), (10, 10)]
0
Cipher.Chen