Нашли ошибку?
продвинутыйNumPy 2.5.3

Скользящие окна

Скользящее среднее, максимум за последние дни и окна в изображении — без циклов и без копирования данных.

Нужно знать:Оси: axis и keepdimsПорядок памяти и strides

Содержание статьи

Коротко

  • sliding_window_view(a, k) возвращает все окна длины k как строки нового массива: из n элементов получается (n − k + 1, k).
  • Это представление через strides: данные не копируются, окна пересекаются.
  • Дальше — любая агрегация по последней оси: .mean(axis=1) — скользящее среднее, .max(axis=1) — скользящий максимум.
  • Для многомерных данных передайте форму окна: (2, 2) — квадратики 2 × 2.
  • Результат только для чтения. Чтобы окна были той же длины, что данные, сначала дополните массив через np.pad.

Синтаксис

from numpy.lib.stride_tricks import sliding_window_view

sliding_window_view(a, window_shape, axis=None)
sliding_window_view(a, 3).mean(axis=-1)      # скользящее среднее
sliding_window_view(a, 3)[::2]               # окна с шагом 2

Параметры

ПараметрЧто делаетПо умолчанию
window_shapeразмер окна: число или кортеж по осямобязательный
axisпо каким осям скользить; None — по всемNone
writeableразрешить запись (опасно: окна делят одни данные)False

Примеры

Данные для примеровиспользуются во всех примерах ниже
from numpy.lib.stride_tricks import sliding_window_view

visits = np.array([120.0, 135, 128, 150, 162, 158, 170])   # посетители по дням
Все окна длины 3
sliding_window_view(visits, 3)          # все окна длины 3
Вывод
array([[120., 135., 128.],
       [135., 128., 150.],
       [128., 150., 162.],
       [150., 162., 158.],
       [162., 158., 170.]])
Скользящее среднее
sliding_window_view(visits, 3).mean(axis=1).round(2)   # скользящее среднее
Вывод
array([127.67, 137.67, 146.67, 156.67, 163.33])
Скользящий максимум
sliding_window_view(visits, 3).max(axis=1)             # максимум за 3 дня
Вывод
array([135., 150., 162., 162., 170.])
Окна с шагом
sliding_window_view(visits, 3)[::2]                    # окна с шагом 2
Вывод
array([[120., 135., 128.],
       [128., 150., 162.],
       [162., 158., 170.]])
Окна 2 × 2 в матрице
img = np.arange(16).reshape(4, 4)
patches = sliding_window_view(img, (2, 2))
print(patches.shape)                    # 3 × 3 окна размера 2 × 2
patches.max(axis=(-2, -1))              # максимум в каждом окне
Вывод
(3, 3, 2, 2)
array([[ 5,  6,  7],
       [ 9, 10, 11],
       [13, 14, 15]])

Подводные камни

Запись в окна запрещена

Изменение окнаошибка: ValueError
w = sliding_window_view(visits, 3)
w[0, 0] = 0                             # окна пересекаются — запись запрещена
Ошибка
ValueError: assignment destination is read-only

Большие окна — много вычислений

Данные не копируются, но .mean(axis=1) для окна длины k делает k сложений на каждую позицию. Для длинных окон скользящую сумму быстрее посчитать через cumsum.

Скользящее среднее через cumsum
c = np.cumsum(np.insert(visits, 0, 0))
((c[3:] - c[:-3]) / 3).round(2)         # то же скользящее среднее за O(n)
Вывод
array([127.67, 137.67, 146.67, 156.67, 163.33])

as_strided

В старом коде окна делали через np.lib.stride_tricks.as_strided. Эта функция не проверяет границы: ошибка в strides читает чужую память. Используйте sliding_window_view.