2

I'm trying to perform a "running max-min window" on a numpy array, such that for a given window size, the function returns the distance between the maximum and minimum values for this window.

I would also like to determine the length of each "skip" of the window.

For example:

if x_array = np.array([3,5,1,8,3,5,2,2])

a running window of size 2 and skip lengths of 2 should result in: [2, 7, 2, 0] since the first window is 3, 5, the second window is 1,8 etc.

Surely it can be done using a simple for-loop, but I'm struggling with thinking of better solutions.

Any help would be appreciated.

Zennie
  • 375
  • 1
  • 3
  • 13
  • What do you mean by `distance between the maximum and minimum values for this window`? What is `length of each "skip" of the window`? You didn't list the final expected output for the given sample. – Divakar Aug 12 '18 at 12:01
  • Hey @Divakar, I actually did. `a running window of size 2 and skip lengths of 2 should result in: [2, 7, 2, 0] since the first window is 3, 5, the second window is 1,8 etc. ` – Zennie Aug 12 '18 at 13:07

2 Answers2

2

Adopting a bit from this answer, what you can do is

from numpy.lib.stride_tricks import as_strided

def running_max_min(a, window_size, step_size):
    nrows = (a.size - window_size)//step_size + 1
    n = a.strides[0]
    s = as_strided(a, shape=(nrows, window_size), strides=(step_size*n, n))
    return s.ptp(1)

For instance,

In [22]: running_max_min(x_array, 2, 2)
Out[22]: array([2, 7, 2, 0])
fuglede
  • 17,388
  • 2
  • 54
  • 99
  • Works perfectly. Thank you! – Zennie Aug 12 '18 at 13:06
  • 1
    for more information, the discussion on the [Suggestion: Sliding Window Function](https://github.com/numpy/numpy/issues/7753) on Numpy's Github is quite interesting – xdze2 Aug 12 '18 at 13:08
2

Here is a solution using the maximum and minimum filters from ndimage:

import numpy as np

from scipy.ndimage import maximum_filter
from scipy.ndimage import minimum_filter

def max_minus_min(a, length, step=1):
    a = np.asarray(a)
    center = length//2 # shift the window to the right
    len_output = len(a)-length+1 # crop the output to keep only full windows
    max_a = maximum_filter(a, size=length, origin=-center, mode='nearest')[:len_output:step]
    min_a = minimum_filter(a, size=length, origin=-center, mode='nearest')[:len_output:step]
    return max_a - min_a

print(max_minus_min([3,5,1,8,3,5,2,2], 2, 2))  # array([2, 7, 2, 0])
print(max_minus_min([3,5,1,8,3,5,2,2], 2, 1))  # array([2, 4, 7, 5, 2, 3, 0])
print(max_minus_min([0, 0, 0, 0, 1, 0, 0, 0], 4, 1))  # array([0, 1, 1, 1, 1])
print(max_minus_min([0, 0, 0, 0, 1, 0, 0, 0], 4, 3))  # array([0, 1])
xdze2
  • 3,986
  • 2
  • 12
  • 29