将Python列表分成n个块

59

我知道这个问题已经被讨论过很多次,但我的要求是不同的。

我有一个列表,如:range(1, 26)。我想将此列表分成固定数量的n部分。假设n = 6。

>>> x
[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]
>>> l = [ x [i:i + 6] for i in range(0, len(x), 6) ]
>>> l
[[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]]

正如您所看到的,我没有得到6个块(由原始列表元素组成的六个子列表)。我该如何将一个列表分成恰好 n 个块,这些块可以是偶数或奇数呢?


更通用,功能相同: [ np.array(x)[i:i + chunk_size,...] for i in range(0, len(x), chunk_size) ] - Nir
22个回答

74

使用numpy

>>> import numpy
>>> x = range(25)
>>> l = numpy.array_split(numpy.array(x),6)
或者
>>> import numpy
>>> x = numpy.arange(25)
>>> l = numpy.array_split(x,6);

你也可以使用numpy.split,但如果长度不能被整除,它会抛出一个错误。


2
返回一个numpy数组列表,应该明确注明(或显示)。 - scharfmn
4
这只适用于numpy支持的数值类型,不是通用解决方案。 - user5915738
2
我不会接受这个,因为它需要一个导入。这可以用很多其他方法来完成。但是一如既往,这取决于数据的拆分方式。Map、[:]等等。 - Angry 84
1
谢谢。我在这个问题的更流行版本中找到了一个非Numpy的答案。 - Xunnamius
超级易读,反正也没人导入numpy。 - jsta
1
这是一个糟糕的答案,它期望用户拥有空间或访问numpy,而现今许多环境都在争夺字节,numpy本身就已经占用了90MB的空间。 仅仅为了将列表分成6个块而添加90MB的依赖关系并不是一个好的做法,教导人们“只需添加新的依赖项”来解决简单问题是一种非常糟糕的方式。 - Lucas Coppio

50

以下解决方案具有许多优点:

  • 使用生成器以产生结果。
  • 无需导入任何内容。
  • 列表是平衡的(如果您将长度为17的列表分成5个列表,则永远不会出现大小为4的4个列表和大小为1的一个列表)。
def chunks(l, n):
    """Yield n number of striped chunks from l."""
    for i in range(0, n):
        yield l[i::n]

对于 l = range(16)n = 6,上面的代码将产生以下输出:

[0, 6, 12]
[1, 7, 13]
[2, 8, 14]
[3, 9, 15]
[4, 10]
[5, 11]

如果您需要块是连续而不是分散的,请使用以下方法:

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

l = range(16)n = 6 时,会产生以下结果:

[0, 1, 2]
[3, 4, 5]
[6, 7, 8]
[9, 10, 11]
[12, 13]
[14, 15]

查看此stackoverflow链接,了解生成器的优点。


2
不错的顺序解决方案。我要指出list(chunks(list(range(5)),6))会产生[[0], [1], [2], [3], [4], []],这是公平的。 - rado

37
如果顺序不重要:
def chunker_list(seq, size):
    return (seq[i::size] for i in range(size))

print(list(chunker_list([1, 2, 3, 4, 5], 2)))
>>> [[1, 3, 5], [2, 4]]

print(list(chunker_list([1, 2, 3, 4, 5], 3)))
>>> [[1, 4], [2, 5], [3]]

print(list(chunker_list([1, 2, 3, 4, 5], 4)))
>>> [[1, 5], [2], [3], [4]]

print(list(chunker_list([1, 2, 3, 4, 5], 5)))
>>> [[1], [2], [3], [4], [5]]

print(list(chunker_list([1, 2, 3, 4, 5], 6)))
>>> [[1], [2], [3], [4], [5], []]

14

more_itertools.divide 是解决这个问题的一种方法:

import more_itertools as mit


iterable = range(1, 26)
[list(c) for c in mit.divide(6, iterable)]

输出

[[ 1,  2,  3,  4, 5],                       # remaining item
 [ 6,  7,  8,  9],
 [10, 11, 12, 13],
 [14, 15, 16, 17],
 [18, 19, 20, 21],
 [22, 23, 24, 25]]

如图所示,如果可迭代对象不是均匀可分的,则剩余的项目从第一个到最后一个块中分布。

了解有关more_itertools库的更多信息,请点击此处


7
我想出了以下解决方案:
l = [x[i::n] for i in range(n)]

例如:
n = 6
x = list(range(26))

l = [x[i::n] for i in range(n)]
print(l)

输出:

[[0, 6, 12, 18, 24], [1, 7, 13, 19, 25], [2, 8, 14, 20], [3, 9, 15, 21], [4, 10, 16, 22], [5, 11, 17, 23]]

如您所见,输出由大约相同数量的元素组成的 n 个块组成。

它是如何工作的?

技巧在于使用列表切片步长(两个分号后面的数字),并 增加步进切片的偏移量。首先,从第一个元素开始,它获取每个n 个元素,然后从第二个元素开始获取每个n个元素,以此类推。这完成了任务。


6

这里给你献上我的两分意见...

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


6

我的建议是简单地使用Python内置的Slice:

# Assume x is our list which we wish to slice
x = range(1, 26)
# Assume we want to slice it to 6 equal chunks
result = []
for i in range(0, len(x), 6):
    slice_item = slice(i, i + 6, 1)
    result.append(x[slice_item])

# Result would be equal to 

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

这是一个包含5个子数组的列表,每个子数组包含了一些数字。该数据结构用于在编程中组织和存储数据。

4
这并没有回答问题。它将内容分成大小为6的块,而不是6个块。 - Jdog

4

试试这个:

from __future__ import division

import math

def chunked(iterable, n):
    """ Split iterable into ``n`` iterables of similar size

    Examples::
        >>> l = [1, 2, 3, 4]
        >>> list(chunked(l, 4))
        [[1], [2], [3], [4]]

        >>> l = [1, 2, 3]
        >>> list(chunked(l, 4))
        [[1], [2], [3], []]

        >>> l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
        >>> list(chunked(l, 4))
        [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]

    """
    chunksize = int(math.ceil(len(iterable) / n))
    return (iterable[i * chunksize:i * chunksize + chunksize]
            for i in range(n))

它返回一个迭代器而不是列表,以提高效率(我假设你想循环遍历这些块),但如果你想要的话,可以用列表推导式来替换它。当项目数不能被块数整除时,最后一个块会比其他块小。
编辑:修正第二个示例以显示它无法处理一个边缘情况。

3

Hint:

  • x is the string to be split.
  • k is number of chunks

    n = len(x)/k
    
    [x[i:i+n] for i in range(0, len(x), n)]
    

2

如果您希望块尽可能均匀大小:

def chunk_ranges(items: int, chunks: int) -> List[Tuple[int, int]]:
    """
    Split the items by best effort into equally-sized chunks.
    
    If there are fewer items than chunks, each chunk contains an item and 
    there are fewer returned chunk indices than the argument `chunks`.

    :param items: number of items in the batch.
    :param chunks: number of chunks
    :return: list of (chunk begin inclusive, chunk end exclusive)
    """
    assert chunks > 0, \
        "Unexpected non-positive chunk count: {}".format(chunks)

    result = []  # type: List[Tuple[int, int]]
    if items <= chunks:
        for i in range(0, items):
            result.append((i, i + 1))
        return result

    chunk_size, extras = divmod(items, chunks)

    start = 0
    for i in range(0, chunks):
        if i < extras:
            end = start + chunk_size + 1
        else:
            end = start + chunk_size

        result.append((start, end))
        start = end

    return result

测试案例:

def test_chunk_ranges(self):
    self.assertListEqual(chunk_ranges(items=8, chunks=1),
                         [(0, 8)])

    self.assertListEqual(chunk_ranges(items=8, chunks=2),
                         [(0, 4), (4, 8)])

    self.assertListEqual(chunk_ranges(items=8, chunks=3),
                         [(0, 3), (3, 6), (6, 8)])

    self.assertListEqual(chunk_ranges(items=8, chunks=5),
                         [(0, 2), (2, 4), (4, 6), (6, 7), (7, 8)])

    self.assertListEqual(chunk_ranges(items=8, chunks=6),
                         [(0, 2), (2, 4), (4, 5), (5, 6), (6, 7), (7, 8)])

    self.assertListEqual(
        chunk_ranges(items=8, chunks=7),
        [(0, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 8)])

    self.assertListEqual(
        chunk_ranges(items=8, chunks=9),
        [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 8)])

因为没有使用短变量和测试用例,所以给予点赞。 - Niklas B

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接