将清单大致等份的最佳方法是什么?例如,如果列表有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个块。我可以简单地转置(遍历这个,取每列的第一个元素,称之为第一部分,然后取第二个元素,把它放在第二部分,等等),但这破坏了项目的顺序。


当前回答

如果你不介意顺序会改变,我建议你使用@job solution,否则,你可以使用这个:

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

    while last < len(seq):
        if len(seq) - (last + steps) < steps:
            until = len(seq)
            steps = len(seq) - last
        else:
            until = int(last + steps)
        out.append(seq[int(last): until])
        last += steps
return out

其他回答

如果你把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)]

另一个简单可读的chunker的尝试。

def chunk(iterable, count): # returns a *generator* that divides `iterable` into `count` of contiguous chunks of similar size
    assert count >= 1
    return (iterable[int(_*len(iterable)/count+0.5):int((_+1)*len(iterable)/count+0.5)] for _ in range(count))

print("Chunk count:  ", len(list(         chunk(range(105),10))))
print("Chunks:       ",     list(         chunk(range(105),10)))
print("Chunks:       ",     list(map(list,chunk(range(105),10))))
print("Chunk lengths:",     list(map(len, chunk(range(105),10))))

print("Testing...")
for iterable_length in range(100):
    for chunk_count in range(1,100):
        chunks = list(chunk(range(iterable_length),chunk_count))
        assert chunk_count == len(chunks)
        assert iterable_length == sum(map(len,chunks))
        assert all(map(lambda _:abs(len(_)-iterable_length/chunk_count)<=1,chunks))
print("Okay")

输出:

Chunk count:   10
Chunks:        [range(0, 11), range(11, 21), range(21, 32), range(32, 42), range(42, 53), range(53, 63), range(63, 74), range(74, 84), range(84, 95), range(95, 105)]
Chunks:        [[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, 56, 57, 58, 59, 60, 61, 62], [63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73], [74, 75, 76, 77, 78, 79, 80, 81, 82, 83], [84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94], [95, 96, 97, 98, 99, 100, 101, 102, 103, 104]]
Chunk lengths: [11, 10, 11, 10, 11, 10, 11, 10, 11, 10]
Testing...
Okay

这是另一种变体,它将“剩余”元素均匀地分布在所有块中,一次一个,直到一个都不剩。在这个实现中,较大的块出现在流程的开头。

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

例如,从14个元素的列表中生成4个块:

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

我的解决方案,很容易理解

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

这页上最短的一句话(我的女朋友写的)

def split(l, n):
    return [l[int(i*len(l)/n):int((i+1)*len(l)/n-1)] for i in range(n)]

将代码更改为产生n个块,而不是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()

这使:

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

这将把额外的元素分配给最后一组,这并不完美,但在你的“大致N相等部分”的规范范围内:-)我的意思是,56个元素作为(19,19,18)会更好,而这给出(18,18,20)。

你可以用下面的代码得到更均衡的输出:

#!/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()

输出:

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