zhangdi
zhangdi

Reputation: 119

Generate sliding window of iterable with length n by step m

def windows(iterable,n,m=1):
    x = iter(iterable)
    l = []
    y = next(x)
    for i in range(n):
        l.append(y)
        y = next(x)
    yield l

    while x:
        for i in range(m):
            l.pop(0)
        for i in range(m):
            l.append(y)
            y = next(x)
        yield l

I need to write a windows generator takes an iterable and two ints (call them n and m; with m’s default value 1) as parameters: it produces lists of n values: the first list contains the first n values; every subsequent list drops the first m from the previous list and adds the next m values from the iterable, until there are fewer than n values to put in the returned list.

for instance:

for i in windows('abcdefghijk', 4,2):
    print(i,end='')

prints ['a','b','c','d'] ['c','d','e','f'] ['e','f','g','h'] ['g','h','i','j'].

when I call the above function, my code prints

[['i', 'j', 'k'], ['i', 'j', 'k'], ['i', 'j', 'k'], ['i', 'j', 'k']]

I cannot figure out the problem. can someone help me to fix it? Thanks in advance.

Upvotes: 4

Views: 1373

Answers (4)

Jeril
Jeril

Reputation: 8521

The following will also work, and will not miss the end terms in some cases:

def sliding_window(iterable, window_size, step_size=1):
    length = len(iterable)
    i = 0
    end_flag = False
    while not end_flag:
        curr_split = list(iterable[i:i + window_size])
        if len(curr_split) < window_size:
            end_flag = True
            curr_split.extend([None] * (window_size - len(curr_split)))
        yield curr_split
        i += step_size

iterable = 'abcdefghijk'
window_size = 4
step_size = 2
res = list(sliding_window(iterable, window_size, step_size))
print(res)

Output:

[['a', 'b', 'c', 'd'],
 ['c', 'd', 'e', 'f'],
 ['e', 'f', 'g', 'h'],
 ['g', 'h', 'i', 'j'],
 ['i', 'j', 'k', None]]

Upvotes: 0

f5r5e5d
f5r5e5d

Reputation: 3706

appears to work for the few cases I tried, the generator itself is a one liner

def WindGen(astr, n, m = 1):
    if m !=0:
        return (list(astr[i * m : i * m + n]) for i in range((len(astr) - n) // m + 1))    

astr = 'abcdefghijk'
n, m = 4, 2
print(*WindGen(astr, n, m), sep='\n')
['a', 'b', 'c', 'd']
['c', 'd', 'e', 'f']
['e', 'f', 'g', 'h']
['g', 'h', 'i', 'j']

Upvotes: 0

Paul Rooney
Paul Rooney

Reputation: 21609

Maybe something like this, assuming you aren't working with a lazy iterable.

def windows(iterable, n, m=1):
    length = len(iterable)
    i = 0

    while i + n < length:
        yield list(iterable[i:i + n])
        i += m

for win in windows('abcdefghijk', 4, 2):
    print(win)

output

['a', 'b', 'c', 'd']
['c', 'd', 'e', 'f']
['e', 'f', 'g', 'h']
['g', 'h', 'i', 'j']

Upvotes: 2

Steven Summers
Steven Summers

Reputation: 5384

You should use slicing to grab n items and have a start value that increases by m.

def windows(iterable, n, m = 1):
    if m == 0: # otherwise infinte loop
        raise ValueError("Parameter 'm' can't be 0")
    lst = list(iterable)
    i = 0
    while i + n < len(lst):
        yield lst[i:i + n]
        i += m

# Output
>>> for i in windows('abcdefghijk', 4, 2):
    print(i)

['a', 'b', 'c', 'd']
['c', 'd', 'e', 'f']
['e', 'f', 'g', 'h']
['g', 'h', 'i', 'j']

Upvotes: 2

Related Questions