ChatGPT解决这个技术问题 Extra ChatGPT

如何将列表分成大小相等的块?

如何将任意长度的列表拆分为大小相等的块?

相关问题: How to iterate over a list in chunks

在发布新答案之前,请考虑此问题已有 60 多个答案。请确保您的答案提供的信息不在现有答案中。
此问题的字符串等效项:Split string every nth character?(虽然一些答案重叠并适用于两者,但每个答案都有一些唯一性)

M
Mateen Ulhaq

这是一个生成大小均匀的块的生成器:

def chunks(lst, n):
    """Yield successive n-sized chunks from lst."""
    for i in range(0, len(lst), n):
        yield lst[i:i + n]
import pprint
pprint.pprint(list(chunks(range(10, 75), 10)))
[[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]]

对于 Python 2,使用 xrange 而不是 range

def chunks(lst, n):
    """Yield successive n-sized chunks from lst."""
    for i in xrange(0, len(lst), n):
        yield lst[i:i + n]

下面是一个列表理解单行。不过,上面的方法更可取,因为使用命名函数会使代码更容易理解。对于 Python 3:

[lst[i:i + n] for i in range(0, len(lst), n)]

对于 Python 2:

[lst[i:i + n] for i in xrange(0, len(lst), n)]

如果我们不能告诉列表的长度会发生什么?在 itertools.repeat([ 1, 2, 3 ]) 上试试这个,例如
这是对该问题的一个有趣的扩展,但原始问题清楚地询问了有关在列表上操作的问题。
这个函数需要在该死的标准库中
@Calimo:你有什么建议?我给你一个包含 47 个元素的列表。你想如何将它分成“大小均匀的块”? OP 接受了答案,因此他们显然可以接受最后一个不同大小的块。也许英文短语不精确?
大多数人会将此视为批处理和速率限制,因此最后一个块是否更小通常无关紧要
M
Mateen Ulhaq

超级简单的东西:

def chunks(xs, n):
    n = max(1, n)
    return (xs[i:i+n] for i in range(0, len(xs), n))

对于 Python 2,使用 xrange() 而不是 range()


或者(如果我们对这个特定函数进行不同的表示)你可以通过以下方式定义一个 lambda 函数: lambda x,y: [ x[i:i+y] for i in range(0,len(x),y) ] .我喜欢这种列表理解方法!
使用短路,len(l) or 1 处理空列表。
# 从列表 l 返回大小为 n 的块的生成器
M
Mateen Ulhaq

我知道这有点老了,但还没有人提到 numpy.array_split

import numpy as np

lst = range(50)
np.array_split(lst, 5)

结果:

[array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),
 array([10, 11, 12, 13, 14, 15, 16, 17, 18, 19]),
 array([20, 21, 22, 23, 24, 25, 26, 27, 28, 29]),
 array([30, 31, 32, 33, 34, 35, 36, 37, 38, 39]),
 array([40, 41, 42, 43, 44, 45, 46, 47, 48, 49])]

这允许您设置块的总数,而不是每个块的元素数。
不难解决 x... : np.array_split(lst, int(len(lst)/5)) 得到一个列表,其中每个子列表的长度为 5 或更少。
如果您的数字低于 0,那么使用 @PéterSzabó-tóth 方法会出错,这很糟糕。
@PéterSzabó-tóth int() 将块数向下舍入,因此每个块将包含 5 个或更多项。例如,当 lst = range(49) 时,您将获得 4 个 6 个项目的块和 5 个 5 个项目的块。
t
tzot

直接来自(旧)Python 文档(itertools 的食谱):

from itertools import izip, chain, repeat

def grouper(n, iterable, padvalue=None):
    "grouper(3, 'abcdefg', 'x') --> ('a','b','c'), ('d','e','f'), ('g','x','x')"
    return izip(*[chain(iterable, repeat(padvalue, n-1))]*n)

JFSebastian 建议的当前版本:

#from itertools import izip_longest as zip_longest # for Python 2.x
from itertools import zip_longest # for Python 3.x
#from six.moves import zip_longest # for both (uses the six compat library)

def grouper(n, iterable, padvalue=None):
    "grouper(3, 'abcdefg', 'x') --> ('a','b','c'), ('d','e','f'), ('g','x','x')"
    return zip_longest(*[iter(iterable)]*n, fillvalue=padvalue)

我猜Guido 的时间机器工作——工作——将工作——将工作——又工作了。

这些解决方案之所以有效,是因为 [iter(iterable)]*n(或早期版本中的等效项)创建了 one 迭代器,在列表中重复了 n 次。 izip_longest 然后有效地执行“每个”迭代器的循环;因为这是同一个迭代器,所以每次这样的调用都会推进它,导致每个这样的 zip-roundrobin 生成一个包含 n 项的元组。


对此表示赞成,因为它适用于生成器(无 len)并使用通常更快的 itertools 模块。
与简单天真的纯 python 实现相比,花哨的 itertools 函数方法产生了一些不可读的污泥的经典示例
@wim 鉴于此答案始于 Python 文档的一个片段,我建议您在 bugs.python.org 上打开一个问题。
s
senderle

我很惊讶没有人想到使用 itertwo-argument form

from itertools import islice

def chunk(it, size):
    it = iter(it)
    return iter(lambda: tuple(islice(it, size)), ())

演示:

>>> list(chunk(range(14), 3))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13)]

这适用于任何可迭代对象并延迟生成输出。它返回元组而不是迭代器,但我认为它仍然具有一定的优雅性。它也不垫;如果你想要填充,上面的简单变化就足够了:

from itertools import islice, chain, repeat

def chunk_pad(it, size, padval=None):
    it = chain(iter(it), repeat(padval))
    return iter(lambda: tuple(islice(it, size)), (padval,) * size)

演示:

>>> list(chunk_pad(range(14), 3))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, None)]
>>> list(chunk_pad(range(14), 3, 'a'))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 'a')]

与基于 izip_longest 的解决方案一样,上述 always 垫。据我所知,可选填充的函数没有单行或两行 itertools 配方。通过结合上述两种方法,这个方法非常接近:

_no_padding = object()

def chunk(it, size, padval=_no_padding):
    if padval == _no_padding:
        it = iter(it)
        sentinel = ()
    else:
        it = chain(iter(it), repeat(padval))
        sentinel = (padval,) * size
    return iter(lambda: tuple(islice(it, size)), sentinel)

演示:

>>> list(chunk(range(14), 3))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13)]
>>> list(chunk(range(14), 3, None))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, None)]
>>> list(chunk(range(14), 3, 'a'))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 'a')]

我相信这是提供可选填充的最短分块器。

与 Tomasz Gandor observed 一样,如果两个填充分块器遇到一长串填充值,它们将意外停止。这是以合理方式解决该问题的最终变体:

_no_padding = object()
def chunk(it, size, padval=_no_padding):
    it = iter(it)
    chunker = iter(lambda: tuple(islice(it, size)), ())
    if padval == _no_padding:
        yield from chunker
    else:
        for ch in chunker:
            yield ch if len(ch) == size else ch + (padval,) * (size - len(ch))

演示:

>>> list(chunk([1, 2, (), (), 5], 2))
[(1, 2), ((), ()), (5,)]
>>> list(chunk([1, 2, None, None, 5], 2, None))
[(1, 2), (None, None), (5, None)]

太好了,你的简单版本是我的最爱。其他人也提出了基本的 islice(it, size) 表达式并将它(就像我所做的那样)嵌入到循环结构中。只有你想到了 iter() 的两个参数版本(我完全不知道),这使它超级优雅(并且可能是最高效的)。我不知道 iter 的第一个参数在给定哨兵时会更改为 0 参数函数。您返回一个(pot.infinite)块迭代器,可以使用(pot.infinite)迭代器作为输入,没有 len() 也没有数组切片。惊人的!
单行版本:``` from itertools import islice from functools import partial seq = [1,2,3,4,5,6,7] size = 3 result = list(iter(partial(lambda it: tuple(islice (it, size)), iter(seq)), ())) 断言结果 == [(1, 2, 3), (4, 5, 6), (7,)] ```
T
ThiefMaster

这是一个适用于任意迭代的生成器:

def split_seq(iterable, size):
    it = iter(iterable)
    item = list(itertools.islice(it, size))
    while item:
        yield item
        item = list(itertools.islice(it, size))

例子:

>>> import pprint
>>> pprint.pprint(list(split_seq(xrange(75), 10)))
[[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]]

k
kevinarpe

简单而不失优雅

L = range(1, 1000)
print [L[x:x+10] for x in xrange(0, len(L), 10)]

或者,如果您愿意:

def chunks(L, n): return [L[x: x+n] for x in xrange(0, len(L), n)]
chunks(L, 10)

不得以阿拉伯数字的形式为变量配音。在某些字体中,1l 无法区分。 0O 也是如此。有时甚至是 I1
@Alfe 有缺陷的字体。人们不应该使用这样的字体。不是为了编程,不是为了任何东西。
Lambda 旨在用作未命名的函数。像这样使用它们是没有意义的。此外,它使调试更加困难,因为在出现错误时回溯将报告“in ”而不是“in chunks”。如果你有一大堆这些,我希望你能找到问题:)
T
ThiefMaster
def chunk(input, size):
    return map(None, *([iter(input)] * size))

在 Python 3.8 中不起作用,是否适用于 2.x?
对于 Python 3.x:return map(lambda *x: x, *([iter(input)] * size))。然而,如果它不能被分成相等的块,它会丢弃列表的尾部
R
Russia Must Remove Putin

你如何将列表分成大小均匀的块?

对我来说,“大小均匀的块”意味着它们的长度都相同,或者除非该选项,否则它们的长度差异最小。例如,21 个项目的 5 个篮子可能会产生以下结果:

>>> import statistics
>>> statistics.variance([5,5,5,5,1]) 
3.2
>>> statistics.variance([5,4,4,4,4]) 
0.19999999999999998

更喜欢后一种结果的一个实际原因是:如果您使用这些功能来分配工作,那么您已经内置了一个可能会在其他人之前完成的前景,因此它会在其他人继续努力工作时无所事事。

在这里批评其他答案

当我最初写这个答案时,其他答案都不是大小均匀的块——它们最后都会留下一个小块,所以它们的平衡性不好,并且长度的变化比必要的要大。

例如,当前的最佳答案以:

[60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
[70, 71, 72, 73, 74]]

其他,如 list(grouper(3, range(7)))chunk(range(7), 3) 都返回:[(0, 1, 2), (3, 4, 5), (6, None, None)]None 只是填充,在我看来相当不雅。他们没有均匀地分块迭代。

为什么我们不能更好地划分这些?

循环解决方案

使用 itertools.cycle 的高级平衡解决方案,我今天可能会这样做。这是设置:

from itertools import cycle
items = range(10, 75)
number_of_baskets = 10

现在我们需要我们的列表来填充元素:

baskets = [[] for _ in range(number_of_baskets)]

最后,我们将要分配的元素与篮子循环一起压缩,直到我们用完元素,从语义上讲,这正是我们想要的:

for element, basket in zip(items, cycle(baskets)):
    basket.append(element)

结果如下:

>>> from pprint import pprint
>>> pprint(baskets)
[[10, 20, 30, 40, 50, 60, 70],
 [11, 21, 31, 41, 51, 61, 71],
 [12, 22, 32, 42, 52, 62, 72],
 [13, 23, 33, 43, 53, 63, 73],
 [14, 24, 34, 44, 54, 64, 74],
 [15, 25, 35, 45, 55, 65],
 [16, 26, 36, 46, 56, 66],
 [17, 27, 37, 47, 57, 67],
 [18, 28, 38, 48, 58, 68],
 [19, 29, 39, 49, 59, 69]]

为了生产这个解决方案,我们编写了一个函数,并提供类型注释:

from itertools import cycle
from typing import List, Any

def cycle_baskets(items: List[Any], maxbaskets: int) -> List[List[Any]]:
    baskets = [[] for _ in range(min(maxbaskets, len(items)))]
    for item, basket in zip(items, cycle(baskets)):
        basket.append(item)
    return baskets

在上面,我们获取了我们的项目列表,以及篮子的最大数量。我们创建一个空列表列表,以循环方式在其中附加每个元素。

切片

另一个优雅的解决方案是使用切片 - 特别是切片的不太常用的 step 参数。 IE:

start = 0
stop = None
step = number_of_baskets

first_basket = items[start:stop:step]

这特别优雅,因为切片不关心数据的长度——结果,我们的第一个篮子,只要它需要的长度。我们只需要增加每个篮子的起点。

事实上,这可能是单行代码,但我们将使用多行代码以提高可读性并避免代码行过长:

from typing import List, Any

def slice_baskets(items: List[Any], maxbaskets: int) -> List[List[Any]]:
    n_baskets = min(maxbaskets, len(items))
    return [items[i::n_baskets] for i in range(n_baskets)]

itertools 模块中的 islice 将提供一种惰性迭代方法,就像问题中最初要求的那样。

我不认为大多数用例会受益匪浅,因为原始数据已经完全物化在列表中,但对于大型数据集,它可以节省近一半的内存使用量。

from itertools import islice
from typing import List, Any, Generator
    
def yield_islice_baskets(items: List[Any], maxbaskets: int) -> Generator[List[Any], None, None]:
    n_baskets = min(maxbaskets, len(items))
    for i in range(n_baskets):
        yield islice(items, i, None, n_baskets)

查看结果:

from pprint import pprint

items = list(range(10, 75))
pprint(cycle_baskets(items, 10))
pprint(slice_baskets(items, 10))
pprint([list(s) for s in yield_islice_baskets(items, 10)])

更新了先前的解决方案

这是另一个平衡的解决方案,改编自我过去在生产中使用的一个函数,它使用了模运算符:

def baskets_from(items, maxbaskets=25):
    baskets = [[] for _ in range(maxbaskets)]
    for i, item in enumerate(items):
        baskets[i % maxbaskets].append(item)
    return filter(None, baskets) 

我创建了一个生成器,如果你把它放到一个列表中,它也会做同样的事情:

def iter_baskets_from(items, maxbaskets=3):
    '''generates evenly balanced baskets from indexable iterable'''
    item_count = len(items)
    baskets = min(item_count, maxbaskets)
    for x_i in range(baskets):
        yield [items[y_i] for y_i in range(x_i, item_count, baskets)]
    

最后,因为我看到所有上述函数都以连续的顺序返回元素(因为它们是给定的):

def iter_baskets_contiguous(items, maxbaskets=3, item_count=None):
    '''
    generates balanced baskets from iterable, contiguous contents
    provide item_count if providing a iterator that doesn't support len()
    '''
    item_count = item_count or len(items)
    baskets = min(item_count, maxbaskets)
    items = iter(items)
    floor = item_count // baskets 
    ceiling = floor + 1
    stepdown = item_count % baskets
    for x_i in range(baskets):
        length = ceiling if x_i < stepdown else floor
        yield [items.next() for _ in range(length)]

输出

要测试它们:

print(baskets_from(range(6), 8))
print(list(iter_baskets_from(range(6), 8)))
print(list(iter_baskets_contiguous(range(6), 8)))
print(baskets_from(range(22), 8))
print(list(iter_baskets_from(range(22), 8)))
print(list(iter_baskets_contiguous(range(22), 8)))
print(baskets_from('ABCDEFG', 3))
print(list(iter_baskets_from('ABCDEFG', 3)))
print(list(iter_baskets_contiguous('ABCDEFG', 3)))
print(baskets_from(range(26), 5))
print(list(iter_baskets_from(range(26), 5)))
print(list(iter_baskets_contiguous(range(26), 5)))

打印出来:

[[0], [1], [2], [3], [4], [5]]
[[0], [1], [2], [3], [4], [5]]
[[0], [1], [2], [3], [4], [5]]
[[0, 8, 16], [1, 9, 17], [2, 10, 18], [3, 11, 19], [4, 12, 20], [5, 13, 21], [6, 14], [7, 15]]
[[0, 8, 16], [1, 9, 17], [2, 10, 18], [3, 11, 19], [4, 12, 20], [5, 13, 21], [6, 14], [7, 15]]
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11], [12, 13, 14], [15, 16, 17], [18, 19], [20, 21]]
[['A', 'D', 'G'], ['B', 'E'], ['C', 'F']]
[['A', 'D', 'G'], ['B', 'E'], ['C', 'F']]
[['A', 'B', 'C'], ['D', 'E'], ['F', 'G']]
[[0, 5, 10, 15, 20, 25], [1, 6, 11, 16, 21], [2, 7, 12, 17, 22], [3, 8, 13, 18, 23], [4, 9, 14, 19, 24]]
[[0, 5, 10, 15, 20, 25], [1, 6, 11, 16, 21], [2, 7, 12, 17, 22], [3, 8, 13, 18, 23], [4, 9, 14, 19, 24]]
[[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]]

请注意,连续生成器提供的块的长度模式与其他两个相同,但项目都是有序的,并且它们被均匀划分,就像一个可以划分离散元素的列表一样。


您说以上都没有提供大小均匀的块。但是 this onethis one 一样。
@senderle,第一个 list(grouper(3, xrange(7))) 和第二个 chunk(xrange(7), 3) 都返回:[(0, 1, 2), (3, 4, 5), (6, None, None)]None 只是填充,在我看来相当不雅。他们没有均匀地分块迭代。感谢您的投票!
您提出了问题(没有明确地这样做,所以我现在在这里这样做)是否相同大小的块(除了最后一个,如果不可能的话)或者是否更经常需要平衡(尽可能好)的结果。您假设平衡的解决方案是首选;如果您的程序接近真实世界(例如模拟纸牌游戏的发牌算法),这可能是正确的。在其他情况下(比如用单词填充线条),人们宁愿让线条尽可能完整。所以我不能真的更喜欢一个。它们仅适用于不同的用例。
@ChristopherBarrington-Leigh 好点,对于 DataFrames,您可能应该使用切片,因为我相信 DataFrame 对象通常不会在切片时复制,例如 import pandas as pd; [pd.DataFrame(np.arange(7))[i::3] for i in xrange(3)]
@AaronHall 哎呀。我删除了我的评论,因为我对我的评论进行了第二次猜测,但你很快就抽签了。谢谢!事实上,我认为它不适用于数据帧的说法是正确的。如果 items 是一个数据框,只需使用 yield items[range(x_i, item_count, baskets)] 作为最后一行。我提供了一个单独的(又一个)答案,您可以在其中指定所需的(最小)组大小。
c
cardamom

如果您知道列表大小:

def SplitList(mylist, chunk_size):
    return [mylist[offs:offs+chunk_size] for offs in range(0, len(mylist), chunk_size)]

如果你不(迭代器):

def IterChunks(sequence, chunk_size):
    res = []
    for item in sequence:
        res.append(item)
        if len(res) >= chunk_size:
            yield res
            res = []
    if res:
        yield res  # yield the last, incomplete, portion

在后一种情况下,如果您可以确定序列始终包含给定大小的整数块(即没有不完整的最后一个块),则可以以更漂亮的方式对其进行改写。


0
0 _

我在这个问题的 duplicate 中看到了最棒的 Python 式答案:

from itertools import zip_longest

a = range(1, 16)
i = iter(a)
r = list(zip_longest(i, i, i))
>>> print(r)
[(1, 2, 3), (4, 5, 6), (7, 8, 9), (10, 11, 12), (13, 14, 15)]

您可以为任何 n 创建 n 元组。如果 a = range(1, 15),那么结果将是:

[(1, 2, 3), (4, 5, 6), (7, 8, 9), (10, 11, 12), (13, 14, None)]

如果列表被平均划分,那么您可以将 zip_longest 替换为 zip,否则三元组 (13, 14, None) 将丢失。上面使用了 Python 3。对于 Python 2,使用 izip_longest


如果您的列表和块很短,那很好,但是您如何调整它以将您的列表分成 1000 个块?你不会编码 zip(i,i,i,i,i,i,i,i,i,i.....i=1000)
zip() 带有“chunk_size”参数的 zip(i, i, i, ... i) 可以写成 zip(*[i]*chunk_size) 当然,这是否是一个好主意是有争议的。
这样做的缺点是,如果您不均匀划分,您将删除元素,因为 zip 停止在最短的可迭代 - & izip_longest 将添加默认元素。
应使用 zip_longest,如:stackoverflow.com/a/434411/1959808
range(1, 15) 的答案已经缺少元素,因为 range(1, 15) 中有 14 个元素,而不是 15 个。
p
pylang

不要重新发明轮子。

给定

import itertools as it
import collections as ct

import more_itertools as mit


iterable = range(11)
n = 3

代码

more_itertools+

list(mit.chunked(iterable, n))
# [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10]]

list(mit.sliced(iterable, n))
# [range(0, 3), range(3, 6), range(6, 9), range(9, 11)]

list(mit.grouper(n, iterable))
# [(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, None)]

list(mit.windowed(iterable, len(iterable)//n, step=n))
# [(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, None)]

list(mit.chunked_even(iterable, n))
# [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10]]

(或DIY,如果你愿意)

标准库

list(it.zip_longest(*[iter(iterable)] * n))
# [(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, None)]
d = {}
for i, x in enumerate(iterable):
    d.setdefault(i//n, []).append(x)
    

list(d.values())
# [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10]]
dd = ct.defaultdict(list)
for i, x in enumerate(iterable):
    dd[i//n].append(x)
    

list(dd.values())
# [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10]]

参考

more_itertools.chunked(相关贴)

more_itertools.sliced

more_itertools.grouper(相关帖子)

more_itertools.windowed(另见交错,zip_offset)

more_itertools.chunked_even

zip_longest(相关帖子,相关帖子)

setdefault(排序结果需要 Python 3.6+)

collections.defaultdict(排序结果需要 Python 3.6+)

+ 实现 itertools recipes 等的第三方库。 > pip install more_itertools


R
Riaz Rizvi
[AA[i:i+SS] for i in range(len(AA))[::SS]]

其中 AA 是数组,SS 是块大小。例如:

>>> AA=range(10,21);SS=3
>>> [AA[i:i+SS] for i in range(len(AA))[::SS]]
[[10, 11, 12], [13, 14, 15], [16, 17, 18], [19, 20]]
# or [range(10, 13), range(13, 16), range(16, 19), range(19, 21)] in py3

要扩展 py3 中的范围,请执行

(py3) >>> [list(AA[i:i+SS]) for i in range(len(AA))[::SS]]
[[10, 11, 12], [13, 14, 15], [16, 17, 18], [19, 20]]

n
ninjagecko

例如,如果您的块大小为 3,则可以执行以下操作:

zip(*[iterable[i::3] for i in range(3)]) 

来源:http://code.activestate.com/recipes/303060-group-a-list-into-sequential-n-tuples/

当我的块大小是我可以输入的固定数字时,我会使用它,例如“3”,并且永远不会改变。


如果 len(iterable)%3 != 0 则这不起作用。不会返回最后一组(短)数字。
@sherbang itertools 中有 zip_longestdocs.python.org/3/library/itertools.html#itertools.zip_longest
z
zach

toolz 库为此提供了 partition 函数:

from toolz.itertoolz.core import partition

list(partition(2, [1, 2, 3, 4]))
[(1, 2), (3, 4)]

n
nirvana-msu

在 Python 3.8 中使用 Assignment Expressions 它变得非常好:

import itertools

def batch(iterable, size):
    it = iter(iterable)
    while item := list(itertools.islice(it, size)):
        yield item

这适用于任意迭代,而不仅仅是列表。

>>> import pprint
>>> pprint.pprint(list(batch(range(75), 10)))
[[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]]

看起来 islice() 是这里更大的明星。
A
Alex T

我对不同方法的性能很好奇,这里是:

在 Python 3.5.1 上测试

import time
batch_size = 7
arr_len = 298937

#---------slice-------------

print("\r\nslice")
start = time.time()
arr = [i for i in range(0, arr_len)]
while True:
    if not arr:
        break

    tmp = arr[0:batch_size]
    arr = arr[batch_size:-1]
print(time.time() - start)

#-----------index-----------

print("\r\nindex")
arr = [i for i in range(0, arr_len)]
start = time.time()
for i in range(0, round(len(arr) / batch_size + 1)):
    tmp = arr[batch_size * i : batch_size * (i + 1)]
print(time.time() - start)

#----------batches 1------------

def batch(iterable, n=1):
    l = len(iterable)
    for ndx in range(0, l, n):
        yield iterable[ndx:min(ndx + n, l)]

print("\r\nbatches 1")
arr = [i for i in range(0, arr_len)]
start = time.time()
for x in batch(arr, batch_size):
    tmp = x
print(time.time() - start)

#----------batches 2------------

from itertools import islice, chain

def batch(iterable, size):
    sourceiter = iter(iterable)
    while True:
        batchiter = islice(sourceiter, size)
        yield chain([next(batchiter)], batchiter)


print("\r\nbatches 2")
arr = [i for i in range(0, arr_len)]
start = time.time()
for x in batch(arr, batch_size):
    tmp = x
print(time.time() - start)

#---------chunks-------------
def chunks(l, n):
    """Yield successive n-sized chunks from l."""
    for i in range(0, len(l), n):
        yield l[i:i + n]
print("\r\nchunks")
arr = [i for i in range(0, arr_len)]
start = time.time()
for x in chunks(arr, batch_size):
    tmp = x
print(time.time() - start)

#-----------grouper-----------

from itertools import zip_longest # for Python 3.x
#from six.moves import zip_longest # for both (uses the six compat library)

def grouper(iterable, n, padvalue=None):
    "grouper(3, 'abcdefg', 'x') --> ('a','b','c'), ('d','e','f'), ('g','x','x')"
    return zip_longest(*[iter(iterable)]*n, fillvalue=padvalue)

arr = [i for i in range(0, arr_len)]
print("\r\ngrouper")
start = time.time()
for x in grouper(arr, batch_size):
    tmp = x
print(time.time() - start)

结果:

slice
31.18285083770752

index
0.02184295654296875

batches 1
0.03503894805908203

batches 2
0.22681021690368652

chunks
0.019841909408569336

grouper
0.006506919860839844

n
nikipore

我非常喜欢 tzot 和 JFSebastian 提出的 Python doc 版本,但它有两个缺点:

它不是很明确

我通常不希望最后一个块中的填充值

我在我的代码中经常使用这个:

from itertools import islice

def chunks(n, iterable):
    iterable = iter(iterable)
    while True:
        yield tuple(islice(iterable, n)) or iterable.next()

更新:一个懒惰的块版本:

from itertools import chain, islice

def chunks(n, iterable):
   iterable = iter(iterable)
   while True:
       yield chain([next(iterable)], islice(iterable, n-1))

M
Moinuddin Quadri

您还可以将 utilspie 库的 get_chunks 函数用作:

>>> from utilspie import iterutils
>>> a = [1, 2, 3, 4, 5, 6, 7, 8, 9]

>>> list(iterutils.get_chunks(a, 5))
[[1, 2, 3, 4, 5], [6, 7, 8, 9]]

您可以通过 pip 安装 utilspie

sudo pip install utilspie

免责声明:我是 utilspie 库的创建者


b
buhtz

代码:

def split_list(the_list, chunk_size):
    result_list = []
    while the_list:
        result_list.append(the_list[:chunk_size])
        the_list = the_list[chunk_size:]
    return result_list

a_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

print split_list(a_list, 3)

结果:

[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]

m
mazieres

在这一点上,我认为我们需要一个递归生成器,以防万一......

在python 2中:

def chunks(li, n):
    if li == []:
        return
    yield li[:n]
    for e in chunks(li[n:], n):
        yield e

在python 3中:

def chunks(li, n):
    if li == []:
        return
    yield li[:n]
    yield from chunks(li[n:], n)

此外,在外星人大规模入侵的情况下,装饰的递归生成器可能会变得很方便:

def dec(gen):
    def new_gen(li, n):
        for e in gen(li, n):
            if e == []:
                return
            yield e
    return new_gen

@dec
def chunks(li, n):
    yield li[:n]
    for e in chunks(li[n:], n):
        yield e

s
slav0nic

呵呵,一行版本

In [48]: chunk = lambda ulist, step:  map(lambda i: ulist[i:i+step],  xrange(0, len(ulist), step))

In [49]: chunk(range(1,100), 10)
Out[49]: 
[[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]]

请使用“def chunk”而不是“chunk = lambda”。它的工作原理相同。一条线。相同的功能。 n00bz 更容易阅读和理解。
@S.Lott:如果 n00bz 来自方案则不是:P 这不是一个真正的问题。甚至还有一个关键字来谷歌!为了n00bz,我们避免了哪些其他功能?我想 yield 也不是必要的/类似 c 的,也不足以对 n00b 友好。
def chunk 而不是 chunk=lambda 产生的函数对象具有 .__name__ 属性“chunk”而不是“<lambda>”。具体名称在回溯中更有用。
C
Corey Goldberg
def split_seq(seq, num_pieces):
    start = 0
    for i in xrange(num_pieces):
        stop = start + len(seq[i::num_pieces])
        yield seq[start:stop]
        start = stop

用法:

seq = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

for seq in split_seq(seq, 3):
    print seq

R
Ranaivo

另一个更明确的版本。

def chunkList(initialList, chunkSize):
    """
    This function chunks a list into sub lists 
    that have a length equals to chunkSize.

    Example:
    lst = [3, 4, 9, 7, 1, 1, 2, 3]
    print(chunkList(lst, 3)) 
    returns
    [[3, 4, 9], [7, 1, 1], [2, 3]]
    """
    finalList = []
    for i in range(0, len(initialList), chunkSize):
        finalList.append(initialList[i:i+chunkSize])
    return finalList

(2016 年 9 月 12 日)这个答案是最独立的语言和最容易阅读的。
p
parity3

不调用 len() 这对大型列表有好处:

def splitter(l, n):
    i = 0
    chunk = l[:n]
    while chunk:
        yield chunk
        i += n
        chunk = l[i:i+n]

这是针对可迭代的:

def isplitter(l, n):
    l = iter(l)
    chunk = list(islice(l, n))
    while chunk:
        yield chunk
        chunk = list(islice(l, n))

以上的功能风味:

def isplitter2(l, n):
    return takewhile(bool,
                     (tuple(islice(start, n))
                            for start in repeat(iter(l))))

或者:

def chunks_gen_sentinel(n, seq):
    continuous_slices = imap(islice, repeat(iter(seq)), repeat(0), repeat(n))
    return iter(imap(tuple, continuous_slices).next,())

或者:

def chunks_gen_filter(n, seq):
    continuous_slices = imap(islice, repeat(iter(seq)), repeat(0), repeat(n))
    return takewhile(bool,imap(tuple, continuous_slices))

没有理由在大型列表中避免使用 len();这是一个恒定时间的操作。
B
BomberMan

请参阅this reference

>>> orange = range(1, 1001)
>>> otuples = list( zip(*[iter(orange)]*10))
>>> print(otuples)
[(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), ... (991, 992, 993, 994, 995, 996, 997, 998, 999, 1000)]
>>> olist = [list(i) for i in otuples]
>>> print(olist)
[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], ..., [991, 992, 993, 994, 995, 996, 997, 998, 999, 1000]]
>>> 

Python3


不错,但如果大小与块的整数不匹配,则在最后删除元素,例如 zip(*[iter(range(7))]*3) 仅返回 [(0, 1, 2), (3, 4, 5)] 并忘记输入中的 6
OP 写道:“我有一个任意长度的列表,我需要将它分成大小相等的块并对其进行操作。”。也许我错过了一些东西,但是如何从任意长度的列表中获取“相等大小的块”而不丢弃比“相等大小”更短的块
R
Rusty Rob
def chunks(iterable,n):
    """assumes n is an integer>0
    """
    iterable=iter(iterable)
    while True:
        result=[]
        for i in range(n):
            try:
                a=next(iterable)
            except StopIteration:
                break
            else:
                result.append(a)
        if result:
            yield result
        else:
            break

g1=(i*i for i in range(10))
g2=chunks(g1,3)
print g2
'<generator object chunks at 0x0337B9B8>'
print list(g2)
'[[0, 1, 4], [9, 16, 25], [36, 49, 64], [81]]'

虽然这可能看起来不像许多基于 itertools 的响应那么短或漂亮,但如果您想在访问第一个子列表之前打印出第二个子列表,那么这个实际上可以工作,即您可以设置 i0=next(g2); i1=下一个(g2);并在使用 i0 之前使用 i1 并且它不会损坏!
v
vishes_shell

因为这里的每个人都在谈论迭代器。 boltons 有一个完美的方法,称为 iterutils.chunked_iter

from boltons import iterutils

list(iterutils.chunked_iter(list(range(50)), 11))

输出:

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

但是,如果您不想对内存留情,您可以使用旧方法并将完整的 list 首先与 iterutils.chunked 一起存储。


而这个实际上不管顺序如何都可以工作,看看子迭代器!
s
schwartrer

考虑使用 matplotlib.cbook

例如:

import matplotlib.cbook as cbook
segments = cbook.pieces(np.arange(20), 3)
for s in segments:
     print s

看起来您不小心创建了两个帐户。您可以contact the team将它们合并,这样您就可以重新获得对您的贡献的直接编辑权限。
A
AdvilUser
a = [1, 2, 3, 4, 5, 6, 7, 8, 9]
CHUNK = 4
[a[i*CHUNK:(i+1)*CHUNK] for i in xrange((len(a) + CHUNK - 1) / CHUNK )]

你能解释一下你的答案吗?
逆向工作: (len(a) + CHUNK -1) / CHUNK 为您提供最终将获得的块数。然后,对于索引 i 处的每个块,我们生成原始数组的子数组,如下所示: a[ i * CHUNK : (i + 1) * CHUNK ] 其中,i * CHUNK 是第一个元素的索引放入子数组,并且 (i + 1) * CHUNK 是要放入子数组的最后一个元素的 1。此解决方案使用列表理解,因此对于大型数组可能更快。