将列表拆分为大约相等长度的N个部分

134 python list chunks

将列表划分为大致相等的部分的最佳方法是什么?例如,如果列表有7个元素并将其拆分为2个部分,我们希望在一个部分中获得3个元素,而另一个应该具有4个元素.

我正在寻找像even_split(L, n)这样的东西L分成n几部分.

def chunks(L, n):
    """ Yield successive n-sized chunks from L.
    """
    for i in xrange(0, len(L), n):
        yield L[i:i+n]
Run Code Online (Sandbox Code Playgroud)

上面的代码给出了3个块,而不是3个块.我可以简单地转置(迭代它并获取每列的第一个元素,调用第一部分,然后取第二部分并将其放入第二部分等),但这会破坏项目的顺序.

tix*_*xit 163

您可以将其简单地编写为列表生成器:

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))
Run Code Online (Sandbox Code Playgroud)

例:

>>> list(split(range(11), 3))
[[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10]]
Run Code Online (Sandbox Code Playgroud)

  • 在Python 3中,`range`而不是`xrange`可以工作。 (6认同)
  • 在SO的N个答案中,这是唯一通过我所有测试的答案。j! (3认同)
  • /sf/answers/2618988081/ 使用相同的方法,但也适用于空列表和 0 拆分计数器。 (2认同)
  • 效果很好!为了方便起见,我个人将其更改为生成器: `def split(lst, n): k, m = divmod(len(lst), n) for i in range(n): yield lst[i*k+min(i , m):(i+1)*k+min(i+1, m)] ` (2认同)

wim*_*wim 117

这就是存在的理由numpy.array_split*:

>>> import numpy as np
>>> print(*np.array_split(range(10), 3))
[0 1 2 3] [4 5 6] [7 8 9]
>>> print(*np.array_split(range(10), 4))
[0 1 2] [3 4 5] [6 7] [8 9]
>>> print(*np.array_split(range(10), 5))
[0 1] [2 3] [4 5] [6 7] [8 9]
Run Code Online (Sandbox Code Playgroud)

*在6号房间入住Zero Piraeus

  • 这应该是最好的答案 (17认同)
  • `print` 中的 `*` 是做什么用的? (2认同)
  • 嘿@yuqli,它将某物的列表转换为函数的单个参数。尝试`print(L)`和`print(* L)。另请参阅/sf/answers/2583591/或搜索“ python使用星号”。 (2认同)

job*_*job 113

只要你不想要任何像连续块一样愚蠢的东西:

>>> 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]]
Run Code Online (Sandbox Code Playgroud)

  • 我不会说连续的块是愚蠢的.也许你想要对块进行排序(例如,chunk [0] <chunk [1]). (12认同)
  • 将此输出发送到'zip'会为您提供有序列表:`zip(*chunkify(range(13),3))`结果为`[(0,1,2),(3,4,5),(6 ,7,8),(9,10,11)]` (8认同)
  • 我是开玩笑的。但如果你真的不在乎,这种列表理解的方式是很好而且简洁的。 (3认同)
  • 这是订阅*步幅n* (3认同)
  • 该解决方案效果很好,直到您需要保持列表顺序为止。 (2认同)

Max*_*keh 57

这是一个可以工作的:

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
Run Code Online (Sandbox Code Playgroud)

测试:

>>> 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]]
Run Code Online (Sandbox Code Playgroud)

  • 新访客:**请不要使用或支持此代码**,它已被破坏.例如`chunkIt(range(10),9)`应该返回9个部分,但它不会. (17认同)
  • 你的例子不适用于```>>> chunkIt(range(8),6)```=>```[[0],[1],[2,3],[4],[ 5],[6],[7]]``` (8认同)
  • 由于答案已被多次编辑,因此评论主题确实令人困惑.这是一个很好的答案吗?不是一个好的答案? (3认同)
  • @conchoecia不是一个好的答案,继续向下滚动.到目前为止,这只编辑了一次,它只是一个简单的编辑(2个空格缩进改为4).不幸的是,OP"user248237dfsf"在网站上已经有3年没有出现过了,所以没有希望让接受的答案发生变化. (3认同)
  • 请不要使用此功能。这是不对的! (2认同)

pax*_*blo 17

更改代码以产生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()
Run Code Online (Sandbox Code Playgroud)

这使:

[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]
Run Code Online (Sandbox Code Playgroud)

这将把额外的元素分配给最终的组,这不是完美的,但在你的"大致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()
Run Code Online (Sandbox Code Playgroud)

哪个输出:

[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]
Run Code Online (Sandbox Code Playgroud)


MaP*_*PeR 9

如果将n元素划分为大致的k块,则可以使n % k块大于其他块的1个元素以分配额外的元素.

以下代码将为您提供块的长度:

[(n // k) + (1 if i < (n % k) else 0) for i in range(k)]
Run Code Online (Sandbox Code Playgroud)

示例:n=11, k=3结果[4, 4, 3]

然后,您可以轻松计算块的起始位置:

[i * (n // k) + min(i, n % k) for i in range(k)]
Run Code Online (Sandbox Code Playgroud)

示例:n=11, k=3结果[0, 4, 8]

使用i+1个块为界,我们得到的是i列表的第一块l与LEN n

l[i * (n // k) + min(i, n % k):(i+1) * (n // k) + min(i+1, n % k)]
Run Code Online (Sandbox Code Playgroud)

最后一步使用list comprehension从所有块创建一个列表:

[l[i * (n // k) + min(i, n % k):(i+1) * (n // k) + min(i+1, n % k)] for i in range(k)]
Run Code Online (Sandbox Code Playgroud)

示例:n=11, k=3, l=range(n)结果[range(0, 4), range(4, 8), range(8, 11)]


pyl*_*ang 8

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]]
Run Code Online (Sandbox Code Playgroud)

通过> pip install more_itertools.


小智 7

使用列表理解:

def divide_list_to_chunks(list_, n):
    return [list_[start::n] for start in range(n)]
Run Code Online (Sandbox Code Playgroud)


PM *_*ing 7

这是一个可以处理任何正数(整数)块的生成器。如果块的数量大于输入列表长度,则某些块将为空。该算法在短块和长块之间交替而不是分离它们。

我还包含了一些用于测试该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')
Run Code Online (Sandbox Code Playgroud)

我们可以通过将乘法导出到调用中来稍微range提高效率,但我认为以前的版本更具可读性(并且更干燥)。

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
Run Code Online (Sandbox Code Playgroud)


Ali*_*jad 7

假设您想将列表[1, 2, 3, 4, 5, 6, 7, 8]拆分为 3 个元素列表

例如[[1,2,3], [4, 5, 6], [7, 8]],如果剩下的最后一个元素小于 3,则它们被分组在一起。

my_list = [1, 2, 3, 4, 5, 6, 7, 8]
my_list2 = [my_list[i:i+3] for i in range(0, len(my_list), 3)]
print(my_list2)
Run Code Online (Sandbox Code Playgroud)

输出: [[1,2,3],[4,5,6],[7,8]]

其中一个部分的长度为 3。将 3 替换为您自己的块大小。


小智 6

这是我的解决方案:

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
Run Code Online (Sandbox Code Playgroud)

生产

    >>> list(chunks([1, 2, 3, 4, 5, 6, 7], 3))
    [[1, 2], [3, 4, 5], [6, 7]]
Run Code Online (Sandbox Code Playgroud)


bit*_*ras 6

这将在保持顺序的同时将单个表达式分成相等的部分:

myList = list(range(18))  # given list
N = 5  # desired number of parts

[myList[(i*len(myList))//N:((i+1)*len(myList))//N] for i in range(N)]
# [[0, 1, 2], [3, 4, 5, 6], [7, 8, 9], [10, 11, 12, 13], [14, 15, 16, 17]]
Run Code Online (Sandbox Code Playgroud)

这些部分的不同之处不超过一个。将 18 分成 5 份的结果是 3 + 4 + 3 + 4 + 4 = 18。


Sha*_*yev 6

其他解决方案似乎有点长。这是使用列表理解和 NumPy 函数的单行代码array_splitarray_split(list, n)将简单地将其分成listn部分。

[x.tolist() for x in np.array_split(range(10), 3)]
Run Code Online (Sandbox Code Playgroud)

  • 答案很棒。但对于最佳实践,请提供解释(诸如“这里”或“优雅”或“尝试这个”之类的说法不算在内)。你只发布代码会让OP和未来的商业者复制并粘贴你的答案,而不理解答案背后的逻辑。请提供答案并进行一些解释。谢谢你! (3认同)

Joh*_*ooy 5

这是一个None使列表长度相等的加法

>>> 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)]
Run Code Online (Sandbox Code Playgroud)


dal*_*ogm 5

看看numpy.split

>>> a = numpy.array([1,2,3,4])
>>> numpy.split(a, 2)
[array([1, 2]), array([3, 4])]
Run Code Online (Sandbox Code Playgroud)

  • 如果数组大小不能被分割数整除,这将不起作用。 (12认同)
  • 而 numpy.array_split() 就更合适了,因为它粗略地分裂了。 (5认同)
  • 这是错误的答案,您的解决方案返回 ndarrays 列表,而不是列表列表 (2认同)