まず、これを行うには複数の方法があります。
速度の点で最も効率的ではありませんが、を使用scipy.ndimage.generic_filter
すると、移動するウィンドウに任意の python 関数を簡単に適用できます。
簡単な例として:
result = scipy.ndimage.generic_filter(data, np.std, size=2*radius)
mode
境界条件はkwarg で制御できることに注意してください。
これを行う別の方法は、いくつかのさまざまなストライド トリックを使用して、効果的に移動ウィンドウである配列のビューを作成しnp.std
、最後の軸に沿って適用することです。(注:これは私の以前の回答の1つから取られています:https://stackoverflow.com/a/4947453/325565)
def strided_sliding_std_dev(data, radius=5):
windowed = rolling_window(data, (2*radius, 2*radius))
shape = windowed.shape
windowed = windowed.reshape(shape[0], shape[1], -1)
return windowed.std(axis=-1)
def rolling_window(a, window):
"""Takes a numpy array *a* and a sequence of (or single) *window* lengths
and returns a view of *a* that represents a moving 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
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)
一見すると、ここで何が起こっているのかを理解するのは少し難しいです。私自身の答えの1つを差し込むのではなく、説明を再入力したくないので、https ://stackoverflow.com/a/4924433/325565を見てください。前に「ストライディング」トリック。
a が 5 のランダムな float の 100x100 配列でタイミングを比較すると、オリジナルまたはバージョンradius
よりも ~10 倍高速です。generic_filter
ただし、このバージョンでは境界条件に柔軟性がありません。(これは現在行っていることと同じですが、generic_filter
バージョンは速度を犠牲にして多くの柔軟性を提供します。)
# Your original function with nested loops
In [21]: %timeit sliding_std_dev(data)
1 loops, best of 3: 237 ms per loop
# Using scipy.ndimage.generic_filter
In [22]: %timeit ndimage_std_dev(data)
1 loops, best of 3: 244 ms per loop
# The "stride-tricks" version above
In [23]: %timeit strided_sliding_std_dev(data)
100 loops, best of 3: 15.4 ms per loop
# Ophion's version that uses `np.take`
In [24]: %timeit new_std_dev(data)
100 loops, best of 3: 19.3 ms per loop
「ストライド トリック」バージョンの欠点は、「通常の」ストライド ローリング ウィンドウ トリックとは異なり、このバージョンではコピーが作成され、元の配列よりもはるかに大きいことです。これを大きな配列で使用すると、メモリの問題が発生します。(ちなみに、メモリ使用量と速度の点では、@ Ophionの回答と基本的に同等です。同じことを行うための別のアプローチです。)