How to get sliding window of a values for each element in both direction (forward, backward)?

Question:

I have a list of values like this,

lst = [1, 2, 3, 4, 5, 6, 7, 8]

Desired Output:

window size = 3
    1  # first element in the list
    forward = [2, 3, 4]
    backward = []

    2  # second element in the list
    forward = [3, 4, 5]
    backward = [1]

    3  # third element in the list
    forward = [4, 5, 6]
    backward = [1, 2]

    4  # fourth element in the list
    forward = [5, 6, 7]
    backward = [1, 2, 3]

    5  # fifth element in the list
    forward = [6, 7, 8]
    backward = [2, 3, 4]

    6  # sixth element in the list
    forward = [7, 8]
    backward = [3, 4, 5]

    7  # seventh element in the list
    forward = [8]
    backward = [4, 5, 6]

    8  # eight element in the list
    forward = []
    backward = [5, 6, 7]

Lets assume a window size of 4, now my desired output:

for each_element in the list, I want 4 values in-front and 4 values backward ignoring the current value.

I was able to use this to get sliding window of values but this also not giving me the correct required output.

import more_itertools
list(more_itertools.windowed([1, 2, 3, 4, 5, 6, 7, 8], n=3))
Asked By: user_12

||

Answers:

[ll[i-4:i+4] for i in range(4, len(ll)-4)]

does the trick, I should think.

Answered By: Igor Rivin

Here is the quick code I wrote

lst = [1, 2, 3, 4, 5, 6, 7, 8]
sliding_window_size = 3

def get_sliding_list(l, index):
    l_list = []
    r_list = []

    min_range = 0
    if index > sliding_window_size:
        min_range = index - sliding_window_size

    max_range = len(l)
    if index + sliding_window_size < len(l):
        max_range = index + sliding_window_size + 1

    return (l[min_range:index], l[index + 1:max_range])

print(get_sliding_list(lst, 0))
print(get_sliding_list(lst, 1))
print(get_sliding_list(lst, 2))
print(get_sliding_list(lst, 3))
print(get_sliding_list(lst, 4))
print(get_sliding_list(lst, 5))
print(get_sliding_list(lst, 6))
print(get_sliding_list(lst, 7))

Output

([], [2, 3, 4])
([1], [3, 4, 5])
([1, 2], [4, 5, 6])
([1, 2, 3], [5, 6, 7])
([2, 3, 4], [6, 7, 8])
([3, 4, 5], [7, 8])
([4, 5, 6], [8])
([5, 6, 7], [])

Pass index of the element for which you want to retrieve sliding window

Answered By: anuragal

Code:

arr = [1, 2, 3, 4, 5, 6, 7, 8]
window = 3
for backward, current in enumerate(range(len(arr)), start = 0-window):
    if backward < 0:
        backward = 0
    print(arr[current+1:current+1+window], arr[backward:current])

Output:

[2, 3, 4], []
[3, 4, 5], [1]
[4, 5, 6], [1, 2]
[5, 6, 7], [1, 2, 3]
[6, 7, 8], [2, 3, 4]
[7, 8], [3, 4, 5]
[8], [4, 5, 6]
[], [5, 6, 7]

One Liner:

print(dict([(e, (lst[i+1:i+4], lst[max(i-3,0):i])) for i,e in enumerate(last)]))

Output:

{1: ([2, 3, 4], []),
 2: ([3, 4, 5], [1]),
 3: ([4, 5, 6], [1, 2]),
 4: ([5, 6, 7], [1, 2, 3]),
 5: ([6, 7, 8], [2, 3, 4]),
 6: ([7, 8], [3, 4, 5]),
 7: ([8], [4, 5, 6]),
 8: ([], [5, 6, 7])}

Credit: thanks to suggestions from @FeRD and @Androbin, the solution now looks better

Answered By: Anurag Wagh

Your sliding window reminds me of another data structure: fixed-size stacks. If you think about it, what you want is actually a fixed-size stack of 7 elements where the right three are the forward window elements and the back three are the back window elements. The 4th element is the current element. Here’s how I would do it:

import collections

my_list = [1, 2, 3, 4, 5, 6, 7, 8]

window = collections.deque([], 7)

for i in my_list:
    window.append(i)

    # Get the back three elements
    forward_window = list(window)[-3:]
    # Get the front three elements
    backward_window = list(window)[:len(window)-4]

    print()
    print(list(forward_window))
    print(list(backward_window))

Of course, the code is not exactly what you want as the stack needs to be primed with some starting elements but that can be done with a bit more work:

import collections

my_list = [1, 2, 3, 4, 5, 6, 7, 8]

# Start with the first three elements
window = collections.deque(my_list[:3], 7)

# Iterate from the fourth element
for i in my_list[3:]:
    window.append(i)
    forward_window = list(window)[-3:]
    backward_window = list(window)[:len(window)-4]
    print()
    print(list(forward_window))
    print(list(backward_window))

After that you just need to clear the stack by adding some empty elements:

while len(window) != 4:
    window.popleft()
    forward_window = list(window)[4:]
    backward_window = list(window)[:3]
    print()
    print(list(forward_window))
    print(list(backward_window))
Answered By: Moon Cheesez

This should get you started:

from dataclasses import dataclass
from typing import List

@dataclass
class Window:
  index: int
  backward: List[int]
  forward: List[int]

def window(iterable, window_size, index):
  backward = iterable[max(0, index - window_size):index]
  forward = iterable[index + 1:index + 1 + window_size]
  return Window(index, backward, forward)
>>> window([1,2,3,4,5,6], 3, 0)
Window(index=0, backward=[], forward=[2, 3, 4])
>>> window([1,2,3,4,5,6], 3, 5)
Window(index=5, backward=[3, 4, 5], forward=[])

I would also suggest adding some checks whether the index and window size make sense.

If you are stuck with an older Python version that doesn’t have dataclasses yet, you can use Named Tuples instead.

Answered By: Hubert Grzeskowiak

Here’s a short and neat code based on list comprehension.

forward = [lst[i+1:i+1+window] for i in range(len(lst)]
backward = [lst[::-1][i+1:i+1+window] for i in range(len(lst)] # first reverse the input list and do same as did in forward
out = zip(forward,backward[::-1]) # first reverse the backward list and zip two list into one

Output

>>> forward
[[2, 3, 4], [3, 4, 5], [4, 5, 6], [5, 6, 7], [6, 7, 8], [7, 8], [8], []]
>>> backward
[[7, 6, 5], [6, 5, 4], [5, 4, 3], [4, 3, 2], [3, 2, 1], [2, 1], [1], []]
>>> out
[([2, 3, 4], []), ([3, 4, 5], [1]), ([4, 5, 6], [2, 1]), ([5, 6, 7], [3, 2, 1]), ([6, 7, 8], [4, 3, 2]), ([7, 8], [5, 4, 3]), ([8], [6, 5, 4]), ([], [7, 6, 5])]

Answered By: user13486325

This will work with more_itertools.windowed if you adjust the window size. Since you want 7 items (3 backward, 1 current, 3 forward), set the window size to 7.

from itertools import chain
from more_itertools import windowed
n = 3
iterable = [1, 2, 3, 4, 5, 6, 7, 8]
# pad the iterable so you start with an empty backward window
it = chain([None] * n, iterable, [None] * n)

for window in windowed(it, n * 2 + 1):
    print(window[n])
    print('forward =', [x for x in window[n + 1:] if x is not None])
    print('backward =', [x for x in window[:n] if x is not None])

The output is:

1
forward = [2, 3, 4]
backward = []

2
forward = [3, 4, 5]
backward = [1]

3
forward = [4, 5, 6]
backward = [1, 2]

4
forward = [5, 6, 7]
backward = [1, 2, 3]

5
forward = [6, 7, 8]
backward = [2, 3, 4]

6
forward = [7, 8]
backward = [3, 4, 5]

7
forward = [8]
backward = [4, 5, 6]

8
forward = []
backward = [5, 6, 7]
Answered By: bbayles

You can just use min and max to make sure you stay within the list (no loops needed) .

lst = [1, 2, 3, 4, 5, 6, 7, 8]
ws = 3 # window

st = 3 # starting point

mn = max(st-ws-1, 0)
mx = min(st+ws, len(lst))
print('Forward = ',lst[st:mx])
print('Backward = ', lst[mn:st-1])

Output:

Forward =  [4, 5, 6]
Backward =  [1, 2]
Answered By: LevB
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.