What is the best way to divide a list into roughly equal parts? For example, if the list has 7 elements and is split it into 2 parts, we want to get 3 elements in o
Here's a generator that can handle any positive (integer) number of chunks. If the number of chunks is greater than the input list length some chunks will be empty. This algorithm alternates between short and long chunks rather than segregating them.
I've also included some code for testing the ragged_chunks
function.
''' 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')
We can make this slightly more efficient by exporting the multiplication into the range
call, but I think the previous version is more readable (and 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
My solution, easy to understand
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
And shortest one-liner on this page(written by my girl)
def split(l, n):
return [l[int(i*len(l)/n):int((i+1)*len(l)/n-1)] for i in range(n)]
The same as job's answer, but takes into account lists with size smaller than the number of chuncks.
def chunkify(lst,n):
[ lst[i::n] for i in xrange(n if n < len(lst) else len(lst)) ]
if n (number of chunks) is 7 and lst (the list to divide) is [1, 2, 3] the chunks are [[0], [1], [2]] instead of [[0], [1], [2], [], [], [], []]
See 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]]
Install via > pip install more_itertools.
This will do the split by a single 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]]
The list in this example has the size 18 and is divided into 5 parts. The size of the parts differs in no more than one element.
Here is one that adds None
to make the lists equal length
>>> 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)]