Что бы это ни стоило, вот как вы можете сделать это, используя «причудливые» уловки.Я собирался опубликовать это вчера, но отвлекся на реальную работу!:)
@ Пол и @eat имеют хорошие реализации, использующие различные другие способы сделать это.Просто чтобы продолжить вещи из предыдущего вопроса, я решил опубликовать N-мерный эквивалент.
Однако вы не сможете значительно превзойти функции scipy.ndimage
для> 1D массивов.(scipy.ndimage.uniform_filter
должен побить scipy.ndimage.convolve
, хотя)
Более того, если вы пытаетесь получить многомерное движущееся окно, вы рискуете взорвать использование памяти всякий раз, когда случайно сделаете копию своего массива.В то время как исходный «скользящий» массив представляет собой просто представление в памяти вашего исходного массива, любые промежуточные шаги, которые копируют массив, сделают копию, которая на порядков больше, чем ваш исходный массив (т.е.что вы работаете с исходным массивом 100x100 ... Вид на него (для фильтра размером (3,3)) будет 98x98x3x3, но будет использоваться та же память, что и у оригинала. Однако любые копии будут использовать количествопамяти, которую мог бы заполнить массив full 98x98x3x3 !!)
По сути, использование сумасшедших пошаговых трюков отлично подходит для случаев, когда вы хотите векторизовать операции движущегося окна на одной оси изndarray.Это позволяет очень легко вычислять такие вещи, как стандартное отклонение и т. Д. С минимальными накладными расходами.Когда вы хотите начать делать это по нескольким осям, это возможно, но обычно вам лучше использовать более специализированные функции.(Например, scipy.ndimage
и т. Д.)
В любом случае, вот как вы это делаете:
import numpy as np
def rolling_window_lastaxis(a, window):
"""Directly taken from Erik Rigtorp's post to numpy-discussion.
<http://www.mail-archive.com/numpy-discussion@scipy.org/msg29450.html>"""
if window < 1:
raise ValueError, "`window` must be at least 1."
if window > a.shape[-1]:
raise ValueError, "`window` is too long."
shape = a.shape[:-1] + (a.shape[-1] - window + 1, window)
strides = a.strides + (a.strides[-1],)
return np.lib.stride_tricks.as_strided(a, shape=shape, strides=strides)
def rolling_window(a, window):
if not hasattr(window, '__iter__'):
return rolling_window_lastaxis(a, window)
for i, win in enumerate(window):
if win > 1:
a = a.swapaxes(i, -1)
a = rolling_window_lastaxis(a, win)
a = a.swapaxes(-2, i)
return a
filtsize = (3, 3)
a = np.zeros((10,10), dtype=np.float)
a[5:7,5] = 1
b = rolling_window(a, filtsize)
blurred = b.mean(axis=-1).mean(axis=-1)
Итак, что мы получаем, когда делаем b = rolling_window(a, filtsize)
, это массив 8x8x3x3, этофактически просмотр той же памяти, что и исходный массив 10x10.Мы могли бы так же легко использовать фильтры разных размеров вдоль разных осей или работать только вдоль выбранных осей N-мерного массива (т.е. filtsize = (0,3,0,3)
в 4-мерном массиве даст нам 6-мерное представление).
Затем мы можем несколько раз применить произвольную функцию к последней оси, чтобы эффективно вычислять объекты в движущемся окне.
Однако, поскольку мы храним временные массивы, которые намного больше, чем наш исходный массивна каждом шаге mean
(или std
или что-то еще) это не совсем эффективно для памяти!Это также не будет ужасно быстрым.
Эквивалент ndimage
просто:
blurred = scipy.ndimage.uniform_filter(a, filtsize, output=a)
Это будет обрабатывать различные граничные условия, сделайте "размывание" в-память, не требуя временной копии массива, и быть очень быстрым.Трюки с движением - хороший способ применить функцию к движущемуся окну вдоль одной оси, но они не являются хорошим способом сделать это по нескольким осям, обычно ....
Просто мои 0,02 доллара, во всяком случае ...