I know this question has been covered many times but my requirement is different.
I have a list like: range(1, 26)
. I want to divide this list into a fi
The solution(s) below have many advantages:
def chunks(l, n):
"""Yield n number of striped chunks from l."""
for i in range(0, n):
yield l[i::n]
The code above produces the below output for l = range(16)
and n = 6
:
[0, 6, 12]
[1, 7, 13]
[2, 8, 14]
[3, 9, 15]
[4, 10]
[5, 11]
If you need the chunks to be sequential instead of striped use this:
def chunks(l, n):
"""Yield n number of sequential chunks from l."""
d, r = divmod(len(l), n)
for i in range(n):
si = (d+1)*(i if i < r else r) + d*(0 if i < r else i - r)
yield l[si:si+(d+1 if i < r else d)]
Which for l = range(16)
and n = 6
produces:
[0, 1, 2]
[3, 4, 5]
[6, 7, 8]
[9, 10, 11]
[12, 13]
[14, 15]
See this stackoverflow link for more information on the advantages of generators.
In cases, where your list contains elements of different types or iterable objects that store values of different types (f.e. some elements are integers, and some are strings), if you use array_split
function from numpy
package to split it, you will get chunks with elements of same type:
import numpy as np
data1 = [(1, 2), ('a', 'b'), (3, 4), (5, 6), ('c', 'd'), ('e', 'f')]
chunks = np.array_split(data1, 3)
print(chunks)
# [array([['1', '2'],
# ['a', 'b']], dtype='<U11'), array([['3', '4'],
# ['5', '6']], dtype='<U11'), array([['c', 'd'],
# ['e', 'f']], dtype='<U11')]
data2 = [1, 2, 'a', 'b', 3, 4, 5, 6, 'c', 'd', 'e', 'f']
chunks = np.array_split(data2, 3)
print(chunks)
# [array(['1', '2', 'a', 'b'], dtype='<U11'), array(['3', '4', '5', '6'], dtype='<U11'),
# array(['c', 'd', 'e', 'f'], dtype='<U11')]
If you would like to have initial types of elements in chunks after splitting of list, you can modify source code of array_split
function from numpy
package or use this implementation:
from itertools import accumulate
def list_split(input_list, num_of_chunks):
n_total = len(input_list)
n_each_chunk, extras = divmod(n_total, num_of_chunks)
chunk_sizes = ([0] + extras * [n_each_chunk + 1] + (num_of_chunks - extras) * [n_each_chunk])
div_points = list(accumulate(chunk_sizes))
sub_lists = []
for i in range(num_of_chunks):
start = div_points[i]
end = div_points[i + 1]
sub_lists.append(input_list[start:end])
return (sub_list for sub_list in sub_lists)
result = list(list_split(data1, 3))
print(result)
# [[(1, 2), ('a', 'b')], [(3, 4), (5, 6)], [('c', 'd'), ('e', 'f')]]
result = list(list_split(data2, 3))
print(result)
# [[1, 2, 'a', 'b'], [3, 4, 5, 6], ['c', 'd', 'e', 'f']]
Here take my 2 cents..
from math import ceil
size = 3
seq = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
chunks = [
seq[i * size:(i * size) + size]
for i in range(ceil(len(seq) / size))
]
# [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11]]
This solution is based on the zip "grouper" pattern from the Python 3 docs. The small addition is that if N does not divide the list length evenly, all the extra items are placed into the first chunk.
import itertools
def segment_list(l, N):
chunk_size, remainder = divmod(len(l), N)
first, rest = l[:chunk_size + remainder], l[chunk_size + remainder:]
return itertools.chain([first], zip(*[iter(rest)] * chunk_size))
Example usage:
>>> my_list = list(range(10))
>>> segment_list(my_list, 2)
[[0, 1, 2, 3, 4], (5, 6, 7, 8, 9)]
>>> segment_list(my_list, 3)
[[0, 1, 2, 3], (4, 5, 6), (7, 8, 9)]
>>>
The advantages of this solution are that it preserves the order of the original list, and is written in a functional style that lazily evaluates the list only once when called.
Note that because it returns an iterator, the result can only be consumed once. If you want the convenience of a non-lazy list, you can wrap the result in list
:
>>> x = list(segment_list(my_list, 2))
>>> x
[[0, 1, 2, 3, 4], (5, 6, 7, 8, 9)]
>>> x
[[0, 1, 2, 3, 4], (5, 6, 7, 8, 9)]
>>>
Hint:
k is number of chunks
n = len(x)/k
[x[i:i+n] for i in range(0, len(x), n)]