将清单大致等份的最佳方法是什么?例如,如果列表有7个元素,并将其分为2部分,我们希望其中一部分有3个元素,而另一部分应该有4个元素。
我正在寻找类似even_split(L, n)的东西,它将L分解为n部分。
def chunks(L, n):
""" Yield successive n-sized chunks from L.
"""
for i in range(0, len(L), n):
yield L[i:i+n]
上面的代码给出了3个块,而不是3个块。我可以简单地转置(遍历这个,取每列的第一个元素,称之为第一部分,然后取第二个元素,把它放在第二部分,等等),但这破坏了项目的顺序。
如果你把n个元素分成大约k个块,你可以让n % k个块1个元素比其他块大,以分配额外的元素。
下面的代码将给出块的长度:
[(n // k) + (1 if i < (n % k) else 0) for i in range(k)]
示例:n=11, k=3结果为[4,4,3]
然后你可以很容易地计算块的起始索引:
[i * (n // k) + min(i, n % k) for i in range(k)]
示例:n=11, k=3结果为[0,4,8]
使用第i+1块作为边界,我们得到列表l的第i块(len n)是
l[i * (n // k) + min(i, n % k):(i+1) * (n // k) + min(i+1, n % k)]
作为最后一步,使用列表理解从所有块创建一个列表:
[l[i * (n // k) + min(i, n % k):(i+1) * (n // k) + min(i+1, n % k)] for i in range(k)]
示例:n=11, k=3, l=range(n) results in [range(0,4), range(4,8), range(8,11)]
看到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]]
通过> pip Install more_itertools安装。
这里有一个生成器,可以处理任何正(整数)数量的块。如果块的数量大于输入列表的长度,一些块将为空。该算法在短块和长块之间交替,而不是将它们分开。
我还包含了一些用于测试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')
我们可以通过将乘法导出到range调用中来稍微提高效率,但我认为以前的版本更易于阅读(和dry)。
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