【问题标题】:python: is there a library function for chunking an input stream?python:是否有用于分块输入流的库函数?
【发布时间】:2012-10-25 17:32:02
【问题描述】:

我想分块输入流以进行批处理。给定一个输入列表或生成器,

x_in = [1, 2, 3, 4, 5, 6 ...]

我想要一个能返回该输入块的函数。说,如果chunk_size=4,那么,

x_chunked = [[1, 2, 3, 4], [5, 6, ...], ...]

这是我一遍又一遍地做的事情,并且想知道是否有比自己编写更标准的方法。我在itertools 中遗漏了什么吗? (用enumerategroupby 可以解决这个问题,但感觉很笨拙。)如果有人想看到一个实现,就在这里,

def chunk_input_stream(input_stream, chunk_size):
    """partition a generator in a streaming fashion"""
    assert chunk_size >= 1
    accumulator = []
    for x in input_stream:
        accumulator.append(x)
        if len(accumulator) == chunk_size:
            yield accumulator
            accumulator = []
    if accumulator:
        yield accumulator

编辑

受kreativitea 回答的启发,这里有一个islice 的解决方案,它很简单,不需要后过滤,

from itertools import islice

def chunk_input_stream(input_stream, chunk_size):
    while True:
        chunk = list(islice(input_stream, chunk_size))
        if chunk:
            yield chunk
        else:
            return

# test it with list(chunk_input_stream(iter([1, 2, 3, 4]), 3))

【问题讨论】:

    标签: python stream itertools chunking


    【解决方案1】:

    来自itertools的食谱:

    def grouper(n, iterable, fillvalue=None):
        "Collect data into fixed-length chunks or blocks"
        # grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx
        args = [iter(iterable)] * n
        return izip_longest(fillvalue=fillvalue, *args)
    

    【讨论】:

    • 你不能用itertools.repeat代替[]*n吗?
    • 这将需要对 OP 的情况进行一些调整 - OP 的代码没有填满,IIUC。
    • @jpm 由于整个事情将直接耗尽到 zip_longest() 调用作为参数,我想生成器的开销会使其比列表乘法慢。这种方式更简单,可能更快。
    • @gatoatigrado 无意居高临下。老实说,我真的在这里看到没有比这个解决方案更好的解决方案了。
    【解决方案2】:

    [感谢 OP 的更新版本:自从我升级以来,我一直在向所有可见的东西扔 yield from,我什至没有想到我在这里不需要它。]

    哦,这是什么鬼:

    from itertools import takewhile, islice, count
    
    def chunk(stream, size):
        return takewhile(bool, (list(islice(stream, size)) for _ in count()))
    

    给出:

    >>> list(chunk((i for i in range(3)), 3))
    [[0, 1, 2]]
    >>> list(chunk((i for i in range(6)), 3))
    [[0, 1, 2], [3, 4, 5]]
    >>> list(chunk((i for i in range(8)), 3))
    [[0, 1, 2], [3, 4, 5], [6, 7]]
    

    警告:如果输入是列表,则上述问题与 OP 的 chunk_input_stream 相同。你可以用一个额外的iter() 包裹来解决这个问题,但这不那么漂亮。从概念上讲,使用repeatcycle 可能比count() 更有意义,但出于某种原因我在进行字符计数。 :^)

    [FTR:不,我仍然对此并不完全认真,但是嘿-这是星期一。]

    【讨论】:

    • 你不需要yield from;如果您只是 return takewhile...,这可以在 Python 2.x 中完美运行。进行编辑,我会将其标记为正确答案。此外,您可能包括导入行以确保完整性from itertools import takewhile, islice, count。您的解决方案简洁明了,实际上非常简单(请参阅我的 cmets,了解 Jon 的原因),并且有效 - 谢谢!!
    【解决方案3】:

    你有什么理由不使用这样的东西吗?:

    # data is your stream, n is your chunk length
    [data[i:i+n] for i in xrange(0,len(data),n)]
    

    编辑:

    由于人们正在制造发电机......

    def grouper(data, n):
        results = [data[i:i+n] for i in xrange(0,len(data),n)]
        for result in results:
            yield result
    

    编辑 2

    我在想,如果您将内存中的输入流作为双端队列,您可以通过.popleft 非常有效地生成 n 个对象。

    from collections import deque
    stream = deque(data)
    
    def chunk(stream, n):
        """ Returns the next chunk from a data stream. """
        return [stream.popleft() for i in xrange(n)]
    
    def chunks(stream, n, reps):
        """ If you want to yield more than one chunk. """
        for item in [chunk(stream, n) for i in xrange(reps)]:
            yield item
    

    【讨论】:

    • 当您处理大量数据时,流属性非常重要。 OTOH,islice 很好...
    • 不错。并且使用 islice 您不必为负值断言,异常是内置的。
    猜你喜欢
    • 2012-06-23
    • 1970-01-01
    • 1970-01-01
    • 2020-09-21
    • 2018-07-18
    • 2019-04-29
    • 1970-01-01
    • 2019-02-04
    • 1970-01-01
    相关资源
    最近更新 更多