如何将列表拆分为大小均匀的块?

jes*_*ern 2068 python split list chunks

我有一个任意长度的列表,我需要将它分成相同大小的块并对其进行操作.有一些明显的方法可以做到这一点,比如保留一个计数器和两个列表,当第二个列表填满时,将它添加到第一个列表并清空下一轮数据的第二个列表,但这可能非常昂贵.

我想知道是否有人对任何长度的列表都有一个很好的解决方案,例如使用生成器.

我一直在寻找有用的东西,itertools但我找不到任何明显有用的东西.但是可能会错过它.

相关问题:在块中迭代列表的最"pythonic"方法是什么?

Ned*_*der 2841

这是一个产生你想要的块的生成器:

def chunks(lst, n):
    """Yield successive n-sized chunks from lst."""
    for i in range(0, len(lst), n):
        yield lst[i:i + n]
Run Code Online (Sandbox Code Playgroud)
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]]
Run Code Online (Sandbox Code Playgroud)

如果您使用的是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]
Run Code Online (Sandbox Code Playgroud)

您也可以简单地使用列表理解而不是编写函数.Python 3:

[lst[i:i + n] for i in range(0, len(lst), n)]
Run Code Online (Sandbox Code Playgroud)

Python 2版本:

[lst[i:i + n] for i in xrange(0, len(lst), n)]
Run Code Online (Sandbox Code Playgroud)

  • 如果我们无法分辨清单的长度会怎样?在itertools.repeat([1,2,3])上试试这个,例如 (63认同)
  • 这是一个有趣的问题扩展,但最初的问题清楚地询问了如何在列表上进行操作. (42认同)
  • 现在,Python 3.12 中已将分块添加为“itertools.batched(iterable, chunk_size)”,请参阅[此处](https://docs.python.org/3.12/library/itertools.html#itertools.batched)。 (21认同)
  • 这个功能需要在该死的标准库中 (14认同)
  • 大多数人会将此用于批处理和速率限制,因此最后一个块是否较小通常并不重要 (7认同)
  • 你的 chunks 方法应该添加到 stdlib 恕我直言 (4认同)
  • @Calimo:您有什么建议?我递给你一份包含47个要素的清单。您想如何将其拆分为“均匀大小的块”?OP接受了答案,因此显然可以接受最后一个不同大小的块。也许英语短语不准确? (3认同)
  • @NedBatchelder 我同意这个问题定义不明确,但是您可以将 47 个元素的列表分成 9、9、9、10 和 10 个元素的 5 个块,而不是 7、10、10、10 和 10。它是不完全均匀,但这就是我在谷歌搜索“均匀大小的块”关键字时所想到的。这意味着您需要 n 来定义块的数量,而不是它们的大小。下面的另一个答案提出了一种实际操作的方法。您的回答与链接的“相关问题”中的回答基本相同。 (3认同)
  • @jespern我想用无限长或无限长的列表你去[相关问题](http://stackoverflow.com/questions/434287/)[JF Sebastian](http://stackoverflow.com/users/ 4279/jf-sebastian)链接:[以块为单位迭代列表的最"pythonic"方式是什么?](http://stackoverflow.com/questions/434287/) (2认同)
  • 除了列表理解之外,我还会添加一个生成器表达式示例。(只需使用`()`而不是`[]`) (2认同)
  • 请不要命名您的变量l,它看起来完全像1并且令人困惑。人们正在复制您的代码,并认为可以。 (2认同)

小智 533

如果你想要一些超级简单的事:

def chunks(l, n):
    n = max(1, n)
    return (l[i:i+n] for i in xrange(0, len(l), n))
Run Code Online (Sandbox Code Playgroud)

range()而不是xrange()在Python 3.x的情况下使用

  • 或者(如果我们对此特定函数进行不同的表示),您可以通过以下方式定义lambda函数:lambda x,y:[x [i:i + y] for i in range(0,len(x),y​​) ].我喜欢这种列表理解方法! (6认同)
  • 返回后必须[,不是( (3认同)
  • @alwbtc - 不,它是正确的它是一个发电机 (3认同)
  • “超级简单”意味着不必调试无限循环-max()的荣誉。 (2认同)

tzo*_*zot 276

直接来自(旧的)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)
Run Code Online (Sandbox Code Playgroud)

当前版本,由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)
Run Code Online (Sandbox Code Playgroud)

我猜Guido的时间机器工作 - 工作 - 将工作 - 将工作 - 再次工作.

这些解决方案起作用是因为[iter(iterable)]*n(或早期版本中的等价物)在列表中重复创建一个迭代器n.izip_longest然后有效地执行"每个"迭代器的循环; 因为这是相同的迭代器,所以每个这样的调用都会使它前进,从而导致每个这样的zip-roundrobin生成一个元组元组n.

  • 与简单而天真的纯python实现相比,花式`itertools`功能方法产生一些难以理解的污泥的典型例子 (73认同)
  • @wim鉴于此答案始于Python文档的片段,我建议您在http://bugs.python.org/上打开一个问题. (13认同)
  • 赞成这一点,因为它适用于生成器(没有len)并使用通常更快的itertools模块. (12认同)

Moj*_*Moj 184

我知道这有点旧,但我不知道为什么没有人提到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])]
Run Code Online (Sandbox Code Playgroud)

  • +1这是我最喜欢的解决方案,因为它将数组分成*均匀*大小的数组,而其他解决方案则没有(在我看过的所有其他解决方案中,最后一个数组可能是任意小的). (20认同)
  • 这允许您设置块的总数,而不是每个块的元素数. (8认同)
  • 你可以自己做数学.如果您有10个元素,则可以将它们分组为2个,5个元素块或5个2个元素块 (6认同)

sen*_*rle 121

我很惊讶,没有人想到使用iter双参数形式:

from itertools import islice

def chunk(it, size):
    it = iter(it)
    return iter(lambda: tuple(islice(it, size)), ())
Run Code Online (Sandbox Code Playgroud)

演示:

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

这适用于任何可迭代的并且懒惰地产生输出.它返回元组而不是迭代器,但我认为它有一定的优雅.它也不垫; 如果你想要填充,上面的一个简单的变化就足够了:

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

演示:

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

izip_longest基于解决方案的解决方案一样,上述内容始终如一.据我所知,没有一行或两行的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)
Run Code Online (Sandbox Code Playgroud)

演示:

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

我相信这是提出可选填充的最短的chunker.

正如Tomasz Gandor所观察到的,如果两个填充块遇到一长串填充值,它们将意外停止.以下是以合理方式解决该问题的最终变体:

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

演示:

>>> list(chunk([1, 2, (), (), 5], 2))
[(1, 2), ((), ()), (5,)]
>>> list(chunk([1, 2, None, None, 5], 2, None))
[(1, 2), (None, None), (5, None)]
Run Code Online (Sandbox Code Playgroud)

  • 很棒,你的简单版本是我的最爱.其他人也提出了基本的`islice(it,size)`表达式并将其嵌入(就像我已经完成的)循环结构中.只有你想到了`iter()`的两个版本(我完全没有意识到),这使它超级优雅(并且可能是最具性能效果的).在给定哨兵时,我不知道`iter`的第一个参数变为0参数函数.你返回一个(pot.无限)块的迭代器,可以使用一个(pot.无限)迭代器作为输入,没有`len()`而没有数组切片.真棒! (6认同)
  • 单行版本: ``` 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,)] ``` (3认同)

Mar*_*rot 88

这是一个可以处理任意迭代的生成器:

def split_seq(iterable, size):
    it = iter(iterable)
    item = list(itertools.islice(it, size))
    while item:
        yield item
        item = list(itertools.islice(it, size))
Run Code Online (Sandbox Code Playgroud)

例:

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


Tom*_*cki 51

def chunk(input, size):
    return map(None, *([iter(input)] * size))
Run Code Online (Sandbox Code Playgroud)

  • 关闭,但最后一块没有元素可填充。这可能是缺陷,也可能不是缺陷。真的很酷的模式。 (2认同)

小智 46

简约而优雅

l = range(1, 1000)
print [l[x:x+10] for x in xrange(0, len(l), 10)]
Run Code Online (Sandbox Code Playgroud)

或者如果您愿意:

chunks = lambda l, n: [l[x: x+n] for x in xrange(0, len(l), n)]
chunks(l, 10)
Run Code Online (Sandbox Code Playgroud)

  • 你不应该在阿拉伯数字的相似性中复制变量.在某些字体中,"1"和"l"是无法区分的.和`0`和'O`一样.有时甚至是"我"和"1". (16认同)
  • Lambdas旨在用作未命名的函数.没有必要像这样使用它们.此外,它使调试更加困难,因为如果出现错误,回溯将报告"in <lambda>"而不是"in chunks".如果你有一大堆这些,我希望你找到问题的运气:) (13认同)
  • @Alfe字体有缺陷.人们不应该使用这样的字体.不是为了编程而是为****. (12认同)

Noi*_*ich 37

我在这个问题的副本中看到了最棒的Python-ish答案:

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

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

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

如果列表均匀分配,则可以替换zip_longestzip,否则三元组(13, 14, None)将丢失.上面使用了Python 3.对于Python 2,请使用izip_longest.

  • `zip(i,i,i,... i)`和zip()的"chunk_size"参数可以写成`zip(*[i]*chunk_size)`这是否是一个好主意是有争议的,课程. (9认同)

Aar*_*all 35

批评其他答案:

这些答案中没有一个是大小均匀的块,它们最后都留下了一个小块,所以它们并没有完全平衡.如果你使用这些功能来分配工作,你已经内置了一个可能在其他人之前完成的前景,所以当其他人继续努力工作时,它会无所事事.

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

[60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
[70, 71, 72, 73, 74]]
Run Code Online (Sandbox Code Playgroud)

我最后讨厌那个小矮人!

其他人,如此list(grouper(3, xrange(7))),chunk(xrange(7), 3)都回归:[(0, 1, 2), (3, 4, 5), (6, None, None)].这None只是填充,在我看来相当不优雅.它们不是均匀地分块迭代.

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

我的解决方案

这里有一个平衡的解决方案,改编自我已经在生产中使用的功能(在Python 3注,以取代xrangerange):

def baskets_from(items, maxbaskets=25):
    baskets = [[] for _ in xrange(maxbaskets)] # in Python 3 use range
    for i, item in enumerate(items):
        baskets[i % maxbaskets].append(item)
    return filter(None, baskets) 
Run Code Online (Sandbox Code Playgroud)

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

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 xrange(baskets):
        yield [items[y_i] for y_i in xrange(x_i, item_count, baskets)]
Run Code Online (Sandbox Code Playgroud)

最后,因为我看到所有上述函数都以连续的顺序返回元素(如给出的那样):

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 xrange(baskets):
        length = ceiling if x_i < stepdown else floor
        yield [items.next() for _ in xrange(length)]
Run Code Online (Sandbox Code Playgroud)

产量

测试它们:

print(baskets_from(xrange(6), 8))
print(list(iter_baskets_from(xrange(6), 8)))
print(list(iter_baskets_contiguous(xrange(6), 8)))
print(baskets_from(xrange(22), 8))
print(list(iter_baskets_from(xrange(22), 8)))
print(list(iter_baskets_contiguous(xrange(22), 8)))
print(baskets_from('ABCDEFG', 3))
print(list(iter_baskets_from('ABCDEFG', 3)))
print(list(iter_baskets_contiguous('ABCDEFG', 3)))
print(baskets_from(xrange(26), 5))
print(list(iter_baskets_from(xrange(26), 5)))
print(list(iter_baskets_contiguous(xrange(26), 5)))
Run Code Online (Sandbox Code Playgroud)

打印出来:

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

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

  • 你提出了这个问题(没有明确地做,所以我现在就这样做)是否同样大小的块(除了最后一个,如果不可能)或者是否平衡(尽可能好)结果更经常需要什么.你认为平衡的解决方案是喜欢的; 如果您编程的内容接近现实世界(例如模拟纸牌游戏的纸牌处理算法),这可能是真的.在其他情况下(比如用文字填充线条),人们宁愿保持尽可能多的线条.所以我真的不喜欢一个而不是另一个; 它们仅适用于不同的用例. (3认同)

atz*_*tzz 29

如果您知道列表大小:

def SplitList(mylist, chunk_size):
    return [mylist[offs:offs+chunk_size] for offs in range(0, len(mylist), chunk_size)]
Run Code Online (Sandbox Code Playgroud)

如果你不(迭代器):

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

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


nir*_*msu 21

使用Python 3.8 中的赋值表达式,它变得非常好:

import itertools

def batch(iterable, size):
    it = iter(iterable)
    while item := list(itertools.islice(it, size)):
        yield item
Run Code Online (Sandbox Code Playgroud)

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

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


nin*_*cko 17

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

zip(*[iterable[i::3] for i in range(3)]) 
Run Code Online (Sandbox Code Playgroud)

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

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

  • 如果len(可迭代)%3!= 0,则不起作用.将不返回最后一个(短)数字组. (11认同)

zac*_*ach 16

图尔茨库具有partition此功能:

from toolz.itertoolz.core import partition

list(partition(2, [1, 2, 3, 4]))
[(1, 2), (3, 4)]
Run Code Online (Sandbox Code Playgroud)


nik*_*ore 15

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

更新:懒人块版本:

from itertools import chain, islice

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


小智 12

码:

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

结果:

[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]
Run Code Online (Sandbox Code Playgroud)


maz*_*res 12

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

在python 2中:

def chunks(li, n):
    if li == []:
        return
    yield li[:n]
    for e in chunks(li[n:], n):
        yield e
Run Code Online (Sandbox Code Playgroud)

在python 3中:

def chunks(li, n):
    if li == []:
        return
    yield li[:n]
    yield from chunks(li[n:], n)
Run Code Online (Sandbox Code Playgroud)

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

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


Ale*_*x T 12

我很好奇不同方法的表现,这里是:

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

结果:

slice
31.18285083770752

index
0.02184295654296875

batches 1
0.03503894805908203

batches 2
0.22681021690368652

chunks
0.019841909408569336

grouper
0.006506919860839844
Run Code Online (Sandbox Code Playgroud)

  • 当我们有`timeit`模块时,使用`time`库进行基准测试并不是一个好主意。 (2认同)

Ria*_*zvi 11

[AA[i:i+SS] for i in range(len(AA))[::SS]]
Run Code Online (Sandbox Code Playgroud)

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


Moi*_*dri 11

您也可以使用库的get_chunks功能utilspie:

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

你可以utilspie通过pip 安装:

sudo pip install utilspie
Run Code Online (Sandbox Code Playgroud)

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


sla*_*nic 7

嘿,一行版

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

  • 请使用"def chunk"而不是"chunk = lambda".它的工作原理相同.一条线.相同的功能.n00bz更容易阅读和理解. (32认同)
  • 由`def chunk`而不是`chunk = lambda`产生的函数对象具有.__ name__属性'chunk'而不是'<lambda>'.特定名称在回溯中更有用. (14认同)
  • @ S.Lott:如果n00bz来自scheme:P,这不是一个真正的问题.谷歌甚至还有一个关键词!还有哪些其他功能显示我们为了n00bz而避免?我认为产量不是必要的/ c-足以使n00b友好. (4认同)

Cor*_*erg 7

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

用法:

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

for seq in split_seq(seq, 3):
    print seq
Run Code Online (Sandbox Code Playgroud)


小智 7

不调用对大型列表有用的len():

def splitter(l, n):
    i = 0
    chunk = l[:n]
    while chunk:
        yield chunk
        i += n
        chunk = l[i:i+n]
Run Code Online (Sandbox Code Playgroud)

这是针对迭代的:

def isplitter(l, n):
    l = iter(l)
    chunk = list(islice(l, n))
    while chunk:
        yield chunk
        chunk = list(islice(l, n))
Run Code Online (Sandbox Code Playgroud)

以上的功能性风味:

def isplitter2(l, n):
    return takewhile(bool,
                     (tuple(islice(start, n))
                            for start in repeat(iter(l))))
Run Code Online (Sandbox Code Playgroud)

要么:

def chunks_gen_sentinel(n, seq):
    continuous_slices = imap(islice, repeat(iter(seq)), repeat(0), repeat(n))
    return iter(imap(tuple, continuous_slices).next,())
Run Code Online (Sandbox Code Playgroud)

要么:

def chunks_gen_filter(n, seq):
    continuous_slices = imap(islice, repeat(iter(seq)), repeat(0), repeat(n))
    return takewhile(bool,imap(tuple, continuous_slices))
Run Code Online (Sandbox Code Playgroud)

  • 在大型列表中没有理由避免使用`len()`; 这是一个固定时间的操作. (16认同)

小智 7

另一个更明确的版本.

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


Ана*_*нин 6

还有一个解决方案

def make_chunks(data, chunk_size): 
    while data:
        chunk, data = data[:chunk_size], data[chunk_size:]
        yield chunk

>>> for chunk in make_chunks([1, 2, 3, 4, 5, 6, 7], 2):
...     print chunk
... 
[1, 2]
[3, 4]
[5, 6]
[7]
>>> 
Run Code Online (Sandbox Code Playgroud)


Ray*_*ger 6

itertools 模块中的配方提供了两种方法来执行此操作,具体取决于您希望如何处理最终的奇数大小的批次(保留它、用填充值填充它、忽略它或引发异常):

from itertools import islice, izip_longest

def batched(iterable, n):
    "Batch data into tuples of length n. The last batch may be shorter."
    # batched('ABCDEFG', 3) --> ABC DEF G
    it = iter(iterable)
    while True:
        batch = tuple(islice(it, n))
        if not batch:
            return
        yield batch

def grouper(iterable, n, *, incomplete='fill', fillvalue=None):
    "Collect data into non-overlapping fixed-length chunks or blocks"
    # grouper('ABCDEFG', 3, fillvalue='x') --> ABC DEF Gxx
    # grouper('ABCDEFG', 3, incomplete='strict') --> ABC DEF ValueError
    # grouper('ABCDEFG', 3, incomplete='ignore') --> ABC DEF
    args = [iter(iterable)] * n
    if incomplete == 'fill':
        return zip_longest(*args, fillvalue=fillvalue)
    if incomplete == 'strict':
        return zip(*args, strict=True)
    if incomplete == 'ignore':
        return zip(*args)
    else:
        raise ValueError('Expected fill, strict, or ignore')
Run Code Online (Sandbox Code Playgroud)


hcv*_*vst 5

>>> def f(x, n, acc=[]): return f(x[n:], n, acc+[(x[:n])]) if x else acc
>>> f("Hallo Welt", 3)
['Hal', 'lo ', 'Wel', 't']
>>> 
Run Code Online (Sandbox Code Playgroud)

如果你在括号中 - 我拿起一本关于 Erlang 的书:)


小智 5

考虑使用matplotlib.cbook片段

例如:

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


rob*_*ing 5

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


小智 5

我意识到这个问题很老(在谷歌上偶然发现),但肯定比任何巨大的复杂建议更简单和清晰,并且只使用切片:

def chunker(iterable, chunksize):
    for i,c in enumerate(iterable[::chunksize]):
        yield iterable[i*chunksize:(i+1)*chunksize]

>>> for chunk in chunker(range(0,100), 10):
...     print list(chunk)
... 
[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]
... etc ...
Run Code Online (Sandbox Code Playgroud)


mac*_*acm 5

看到这个参考

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

Python3

  • 很好,但是如果大小与整数块不匹配,则在最后删除元素,例如zip(* [iter(range(7))] * 3)`仅返回[[0,1,2),( 3,4,5)]`,并从输入中忘记6。 (3认同)

Sak*_*rma 5

使用列表理解:

l = [1,2,3,4,5,6,7,8,9,10,11,12]
k = 5 #chunk size
print [tuple(l[x:y]) for (x, y) in [(x, x+k) for x in range(0, len(l), k)]]
Run Code Online (Sandbox Code Playgroud)


Adv*_*ser 5

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


Jul*_*ard 5

在这一点上,我认为我们需要强制性的匿名递归功能.

Y = lambda f: (lambda x: x(x))(lambda y: f(lambda *args: y(y)(*args)))
chunks = Y(lambda f: lambda n: [n[0][:n[1]]] + f((n[0][n[1]:], n[1])) if len(n[0]) > 0 else [])
Run Code Online (Sandbox Code Playgroud)

  • @JulienPalard哦,是的,可读性是此回复的全部内容。 (4认同)

Cla*_*diu 5

根据这个答案,最高投票的答案最后会留下一个“矮个子”。这是我的解决方案,可以真正获得尽可能均匀大小的块,没有小块。它基本上试图准确选择应该拆分列表的小数点,但只是将其四舍五入到最接近的整数:

from __future__ import division  # not needed in Python 3
def n_even_chunks(l, n):
    """Yield n as even chunks as possible from l."""
    last = 0
    for i in range(1, n+1):
        cur = int(round(i * (len(l) / n)))
        yield l[last:cur]
        last = cur
Run Code Online (Sandbox Code Playgroud)

示范:

>>> pprint.pprint(list(n_even_chunks(list(range(100)), 9)))
[[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]]
>>> pprint.pprint(list(n_even_chunks(list(range(100)), 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, 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]]
Run Code Online (Sandbox Code Playgroud)

与最高投票的chunks答案相比:

>>> pprint.pprint(list(chunks(list(range(100)), 100//9)))
[[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]]
>>> pprint.pprint(list(chunks(list(range(100)), 100//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, 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]]
Run Code Online (Sandbox Code Playgroud)


vis*_*ell 5

既然大家都在谈论迭代器.boltons有完美的方法,称为iterutils.chunked_iter.

from boltons import iterutils

list(iterutils.chunked_iter(list(range(50)), 11))
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]]
Run Code Online (Sandbox Code Playgroud)

但是如果你不想怜悯记忆,你可以使用旧方式并将其全部存储list起来iterutils.chunked.


Ale*_*exG 5

例如,您可以使用 numpy 的 array_split 函数np.array_split(np.array(data), 20)将其分成 20 个几乎相等大小的块。

为了确保块的大小完全相等,请使用np.split.


Geo*_*e B 5

我认为我没有看到这个选项,所以只需添加另一个:)):

def chunks(iterable, chunk_size):
  i = 0;
  while i < len(iterable):
    yield iterable[i:i+chunk_size]
    i += chunk_size
Run Code Online (Sandbox Code Playgroud)


pyl*_*ang 5

以下是其他方法的列表:

给定

import itertools as it
import collections as ct

import more_itertools as mit


iterable = range(11)
n = 3
Run Code Online (Sandbox Code Playgroud)

标准图书馆

list(it.zip_longest(*[iter(iterable)] * n))
# [(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, None)]
Run Code Online (Sandbox Code Playgroud)

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

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

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

参考文献

+一个实现itertools配方等的第三方库。> pip install more_itertools


Rav*_*and 5

pythonpydash包可能是一个不错的选择。

from pydash.arrays import chunk
ids = ['22', '89', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '1']
chunk_ids = chunk(ids,5)
print(chunk_ids)
# output: [['22', '89', '2', '3', '4'], ['5', '6', '7', '8', '9'], ['10', '11', '1']]
Run Code Online (Sandbox Code Playgroud)

更多结帐pydash块列表