Rolling or sliding window iterator?

后端 未结 23 1452
南方客
南方客 2020-11-21 05:23

I need a rolling window (aka sliding window) iterable over a sequence/iterator/generator. Default Python iteration can be considered a special case, where the window length

相关标签:
23条回答
  • 2020-11-21 06:09

    a slightly modified version of the deque window, to make it a true rolling window. So that it starts being populated with just one element, then grows to it's maximum window size, and then shrinks as it's left edge comes near the end:

    from collections import deque
    def window(seq, n=2):
        it = iter(seq)
        win = deque((next(it, None) for _ in xrange(1)), maxlen=n)
        yield win
        append = win.append
        for e in it:
            append(e)
            yield win
        for _ in xrange(len(win)-1):
            win.popleft()
            yield win
    
    for wnd in window(range(5), n=3):
        print(list(wnd))
    

    this gives

    [0]
    [0, 1]
    [0, 1, 2]
    [1, 2, 3]
    [2, 3, 4]
    [3, 4]
    [4]
    
    0 讨论(0)
  • 2020-11-21 06:10

    This seems tailor-made for a collections.deque since you essentially have a FIFO (add to one end, remove from the other). However, even if you use a list you shouldn't be slicing twice; instead, you should probably just pop(0) from the list and append() the new item.

    Here is an optimized deque-based implementation patterned after your original:

    from collections import deque
    
    def window(seq, n=2):
        it = iter(seq)
        win = deque((next(it, None) for _ in xrange(n)), maxlen=n)
        yield win
        append = win.append
        for e in it:
            append(e)
            yield win
    

    In my tests it handily beats everything else posted here most of the time, though pillmuncher's tee version beats it for large iterables and small windows. On larger windows, the deque pulls ahead again in raw speed.

    Access to individual items in the deque may be faster or slower than with lists or tuples. (Items near the beginning are faster, or items near the end if you use a negative index.) I put a sum(w) in the body of my loop; this plays to the deque's strength (iterating from one item to the next is fast, so this loop ran a a full 20% faster than the next fastest method, pillmuncher's). When I changed it to individually look up and add items in a window of ten, the tables turned and the tee method was 20% faster. I was able to recover some speed by using negative indexes for the last five terms in the addition, but tee was still a little faster. Overall I would estimate that either one is plenty fast for most uses and if you need a little more performance, profile and pick the one that works best.

    0 讨论(0)
  • 2020-11-21 06:10
    def GetShiftingWindows(thelist, size):
        return [ thelist[x:x+size] for x in range( len(thelist) - size + 1 ) ]
    
    >> a = [1, 2, 3, 4, 5]
    >> GetShiftingWindows(a, 3)
    [ [1, 2, 3], [2, 3, 4], [3, 4, 5] ]
    
    0 讨论(0)
  • 2020-11-21 06:10

    Multiple iterators!

    def window(seq, size, step=1):
        # initialize iterators
        iters = [iter(seq) for i in range(size)]
        # stagger iterators (without yielding)
        [next(iters[i]) for j in range(size) for i in range(-1, -j-1, -1)]
        while(True):
            yield [next(i) for i in iters]
            # next line does nothing for step = 1 (skips iterations for step > 1)
            [next(i) for i in iters for j in range(step-1)]
    

    next(it) raises StopIteration when the sequence is finished, and for some cool reason that's beyond me, the yield statement here excepts it and the function returns, ignoring the leftover values that don't form a full window.

    Anyway, this is the least-lines solution yet whose only requirement is that seq implement either __iter__ or __getitem__ and doesn't rely on itertools or collections besides @dansalmo's solution :)

    0 讨论(0)
  • 2020-11-21 06:10

    How about using the following:

    mylist = [1, 2, 3, 4, 5, 6, 7]
    
    def sliding_window(l, window_size=2):
        if window_size > len(l):
            raise ValueError("Window size must be smaller or equal to the number of elements in the list.")
    
        t = []
        for i in xrange(0, window_size):
            t.append(l[i:])
    
        return zip(*t)
    
    print sliding_window(mylist, 3)
    

    Output:

    [(1, 2, 3), (2, 3, 4), (3, 4, 5), (4, 5, 6), (5, 6, 7)]
    
    0 讨论(0)
提交回复
热议问题