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))
Answers:
[ll[i-4:i+4] for i in range(4, len(ll)-4)]
does the trick, I should think.
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
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
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))
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.
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])]
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]
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]
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))
[ll[i-4:i+4] for i in range(4, len(ll)-4)]
does the trick, I should think.
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
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
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))
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.
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])]
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]
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]