2
\$\begingroup\$

Here is a function for creating sliding windows from a 1D NumPy array:

from math import ceil, floor
import numpy as np

def slide_window(A, win_size, stride, padding = None):
    '''Collects windows that slides over a one-dimensional array.

    If padding is None, the last (rightmost) window is dropped if it
    is incomplete, otherwise it is padded with the padding value.
    '''
    if win_size <= 0:
        raise ValueError('Window size must be positive.')
    if not (0 < stride <= win_size):
        raise ValueError(f'Stride must satisfy 0 < stride <= {win_size}.')
    if not A.base is None:
        raise ValueError('Views cannot be slided over!')

    n_elems = len(A)
    if padding is not None:
        n_windows = ceil(n_elems / stride)
        A = np.pad(A, (0, n_windows * win_size - n_elems),
                   constant_values = padding)
    else:
        n_windows = floor(n_elems / stride)
    shape = n_windows, win_size

    elem_size = A.strides[-1]
    return np.lib.stride_tricks.as_strided(
        A, shape = shape,
        strides = (elem_size * stride, elem_size),
        writeable = False)

(Code has been updated based on Marc's feedback) Meant to be used like this:

>>> slide_window(np.arange(5), 3, 2, -1)
array([[ 0,  1,  2],
       [ 2,  3,  4],
       [ 4, -1, -1]])

Is my implementation correct? Can the code be made more readable? In NumPy 1.20 there is a function called sliding_window_view, but my code needs to work with older NumPy versions.

\$\endgroup\$
2
  • \$\begingroup\$ numpy.lib.stride_tricks.sliding_window_view is written in pure python; if it is not for the sake of a programming exercise, is there any reason why you can't ... faithfully borrow ... their code? \$\endgroup\$ Mar 18 at 21:06
  • \$\begingroup\$ It doesn't support padding the last element. Maybe that can be added but it looks like lots of work. \$\endgroup\$ Mar 18 at 21:18
2
\$\begingroup\$

Few suggestions:

  • Input validation: there is no input validation for win_size and padding. If win_size is -3 the exception says ValueError: Stride must satisfy 0 < stride <= -3.. If padding is a string, numpy throws an exception.

  • Type hints: consider adding typing to provide more info to the caller.

  • f-Strings: depending on the version of Python you use, the exception message can be slightly simplified.

    From:

    if not (0 < stride <= win_size):
        fmt = 'Stride must satisfy 0 < stride <= %d.'
        raise ValueError(fmt % win_size)
    

    To:

    if not 0 < stride <= win_size:
        raise ValueError(f'Stride must satisfy 0 < stride <= {win_size}.')
    
  • Duplication: the statement shape = n_windows, win_size seems duplicated and can be simplified. From:

    if padding is not None:
        n_windows = ceil(n_elems / stride)
        shape = n_windows, win_size
        A = np.pad(A, (0, n_windows * win_size - n_elems),
                 constant_values = padding)
    else:
        n_windows = floor(n_elems / stride)
        shape = n_windows, win_size
    

    To:

    if padding is not None:
        n_windows = ceil(n_elems / stride)
        A = np.pad(A, (0, n_windows * win_size - n_elems),
                       constant_values=padding)
    else:
        n_windows = floor(n_elems / stride)
    shape = n_windows, win_size
    
  • Warning: FYI on the doc of np.lib.stride_tricks.as_strided there is a warning that says This function has to be used with extreme care, see notes.. Not sure if it applies to your use case but consider checking it out.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.