【问题标题】:How do I split a list into equally-sized chunks?如何将列表分成大小相等的块?
【发布时间】:2022-10-29 04:21:48
【问题描述】:

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


如果数据结果将直接用于循环,请参见How to iterate over a list in chunks,不需要存储。

对于带有字符串输入的相同问题,请参阅Split string every nth character?。相同的技术通常适用,尽管有一些变化。

【问题讨论】:

  • 在发布新答案之前,请考虑此问题已有 60 多个答案。请确保您的答案提供的信息不在现有答案中。

标签: python list split chunks


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

    超级简单的东西:

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

    【讨论】:

    • 使用短路,len(l) or 1 处理空列表。
    • 它太棒了!
    【解决方案3】:

    我知道这有点老了,但还没有人提到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])]
    

    【讨论】:

    • 这允许您设置块的总数,而不是每个块的元素数。
    • 此方法更改元素的类型 [ ['a', 1] , ['b', 2] ] 块 1 可能变为 [ ['a', '1'] , ['b', '2'] ]。如果第一个元素的类型是 str 则所有元素都变为 numpy.str_ ...
    【解决方案4】:

    直接来自(旧)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)
    

    J.F.Sebastian 建议的当前版本:

    #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(或早期版本中的等效项)创建迭代器,在列表中重复n 次。 izip_longest 然后有效地执行“每个”迭代器的循环;因为这是同一个迭代器,所以每次这样的调用都会推进它,导致每个这样的 zip-roundrobin 生成一个 n 项目的元组。

    【讨论】:

      【解决方案5】:

      我很惊讶没有人想到使用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 的解决方案一样,上述总是垫。据我所知,没有单行或两行 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)]
      

      【讨论】:

      • 单行版本:``` 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,)] ```
      【解决方案6】:

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

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

      【讨论】:

        【解决方案7】:

        简单而不失优雅

        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)
        

        【讨论】:

          【解决方案8】:
          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))。然而,如果它不能被分成相等的块,它会丢弃列表的尾部
          【解决方案9】:

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

          对我来说,“大小均匀的块”意味着它们的长度都相同,或者除非该选项,否则最小方差长度。例如。 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
          

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

          切片

          另一个优雅的解决方案是使用切片 - 特别是不太常用的切片的参数。 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]]
          

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

          【讨论】:

            【解决方案10】:

            如果您知道列表大小:

            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
            

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

            【讨论】:

              【解决方案11】:

              不要重新发明轮子。

              更新:即将推出的 Python 3.12 introduces itertools.batched,终于解决了这个问题。见下文。

              给定

              import itertools as it
              import collections as ct
              
              import more_itertools as mit
              
              
              iterable = range(11)
              n = 3
              

              代码

              itertools.batched++

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

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

              参考

              +实现itertools recipes 等的第三方库。 &gt; pip install more_itertools

              ++包含在 Python 标准库 3.12+ 中。 batched 类似于 more_itertools.chunked

              【讨论】:

              • 我会放弃我所有的观点,让这成为第一个答案
              【解决方案12】:

              我在这个问题的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

              【讨论】:

                【解决方案13】:
                [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]]
                

                【讨论】:

                  【解决方案14】:

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

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

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

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

                  【讨论】:

                  【解决方案15】:

                  toolz 库为此具有 partition 函数:

                  from toolz.itertoolz.core import partition
                  
                  list(partition(2, [1, 2, 3, 4]))
                  [(1, 2), (3, 4)]
                  

                  【讨论】:

                    【解决方案16】:

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

                    【讨论】:

                      【解决方案17】:

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

                      在 Python 3.5.1 上测试

                      import time
                      batch_size = 7
                      arr_len = 298937
                      
                      #---------slice-------------
                      
                      print("
                      slice")
                      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("
                      index")
                      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("
                      batches 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("
                      batches 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("
                      chunks")
                      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("
                      grouper")
                      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
                      

                      【讨论】:

                        【解决方案18】:

                        我非常喜欢 tzot 和 J.F.Sebastian 提出的 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))
                        

                        【讨论】:

                          【解决方案19】:

                          您也可以将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库的创建者.

                          【讨论】:

                            【解决方案20】:

                            代码:

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

                            【讨论】:

                              【解决方案21】:

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

                              在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
                              

                              【讨论】:

                                【解决方案22】:

                                呵呵,一行版本

                                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 更容易阅读和理解。
                                • def chunk 而不是chunk=lambda 产生的函数对象具有.__name__ 属性'chunk' 而不是'<lambda>'。具体名称在回溯中更有用。
                                【解决方案23】:

                                另一个更明确的版本。

                                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
                                

                                【讨论】:

                                  【解决方案24】:
                                  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
                                  

                                  【讨论】:

                                    【解决方案25】:

                                    不调用 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();这是一个恒定时间的操作。
                                    【解决方案26】:

                                    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

                                    【讨论】:

                                    • 很好,但如果大小与块的整数不匹配,则在最后删除元素,例如。 G。 zip(*[iter(range(7))]*3) 仅返回 [(0, 1, 2), (3, 4, 5)] 并忘记输入中的 6
                                    【解决方案27】:
                                    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]]'
                                    

                                    【讨论】:

                                      【解决方案28】:

                                      因为这里的每个人都在谈论迭代器。 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 一起存储。

                                      【讨论】:

                                        【解决方案29】:

                                        考虑使用matplotlib.cbook

                                        例如:

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

                                        【讨论】:

                                          【解决方案30】:
                                          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。此解决方案使用列表理解,因此对于大型数组可能更快。
                                          猜你喜欢
                                          • 2012-05-09
                                          • 2023-01-07
                                          • 1970-01-01
                                          • 1970-01-01
                                          • 1970-01-01
                                          • 2020-03-12
                                          • 1970-01-01
                                          • 1970-01-01
                                          • 1970-01-01
                                          相关资源
                                          最近更新 更多