Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge pull request #81 from shoyer/better_move_func_strides

Generalized move_func_strides to arbitrary dimensions
  • Loading branch information...
commit a608b86aa7351ff837f7ddb3430513b9129c36aa 2 parents 4ea522b + 7b2de3b
kwgoodman authored
Showing with 43 additions and 60 deletions.
  1. +23 −43 bottleneck/slow/move.py
  2. +20 −17 bottleneck/tests/move_test.py
66 bottleneck/slow/move.py
View
@@ -38,7 +38,7 @@ def move_sum(arr, window, axis=-1, method='loop'):
The following moving window methods are available:
========== =====================================
'filter' scipy.ndimage.convolve1d
- 'strides' strides tricks (ndim < 4)
+ 'strides' strides tricks
'loop' brute force python loop (default)
========== =====================================
@@ -88,7 +88,7 @@ def move_nansum(arr, window, axis=-1, method='loop'):
The following moving window methods are available:
========== =====================================
'filter' scipy.ndimage.convolve1d
- 'strides' strides tricks (ndim < 4)
+ 'strides' strides tricks
'loop' brute force python loop (default)
========== =====================================
@@ -254,7 +254,7 @@ def move_mean(arr, window, axis=-1, method='loop'):
The following moving window methods are available:
========== =====================================
'filter' scipy.ndimage.convolve1d
- 'strides' strides tricks (ndim < 4)
+ 'strides' strides tricks
'loop' brute force python loop (default)
========== =====================================
@@ -304,7 +304,7 @@ def move_nanmean(arr, window, axis=-1, method='loop'):
The following moving window methods are available:
========== =====================================
'filter' scipy.ndimage.convolve1d
- 'strides' strides tricks (ndim < 4)
+ 'strides' strides tricks
'loop' brute force python loop (default)
========== =====================================
@@ -411,7 +411,7 @@ def move_var(arr, window, axis=-1, method='loop', ddof=0):
The following moving window methods are available:
========== =====================================
'filter' scipy.ndimage.convolve1d
- 'strides' strides tricks (ndim < 4)
+ 'strides' strides tricks
'loop' brute force python loop (default)
========== =====================================
@@ -463,7 +463,7 @@ def move_nanvar(arr, window, axis=-1, method='loop', ddof=0):
The following moving window methods are available:
========== =====================================
'filter' scipy.ndimage.convolve1d
- 'strides' strides tricks (ndim < 4)
+ 'strides' strides tricks
'loop' brute force python loop (default)
========== =====================================
@@ -582,7 +582,7 @@ def move_std(arr, window, axis=-1, method='loop', ddof=0):
The following moving window methods are available:
========== =====================================
'filter' scipy.ndimage.convolve1d
- 'strides' strides tricks (ndim < 4)
+ 'strides' strides tricks
'loop' brute force python loop (default)
========== =====================================
@@ -635,7 +635,7 @@ def move_nanstd(arr, window, axis=-1, method='loop', ddof=0):
The following moving window methods are available:
========== =====================================
'filter' scipy.ndimage.convolve1d
- 'strides' strides tricks (ndim < 4)
+ 'strides' strides tricks
'loop' brute force python loop (default)
========== =====================================
@@ -718,7 +718,7 @@ def move_min(arr, window, axis=-1, method='loop'):
The following moving window methods are available:
========== =========================================
'filter' scipy.ndimage.minimum_filter1d
- 'strides' strides tricks (ndim < 4)
+ 'strides' strides tricks
'loop' brute force python loop (default)
========== =========================================
@@ -763,7 +763,7 @@ def move_nanmin(arr, window, axis=-1, method='loop'):
The following moving window methods are available:
========== =========================================
'filter' scipy.ndimage.minimum_filter1d
- 'strides' strides tricks (ndim < 4)
+ 'strides' strides tricks
'loop' brute force python loop (default)
========== =========================================
@@ -904,7 +904,7 @@ def move_max(arr, window, axis=-1, method='loop'):
The following moving window methods are available:
========== =========================================
'filter' scipy.ndimage.minimum_filter1d
- 'strides' strides tricks (ndim < 4)
+ 'strides' strides tricks
'loop' brute force python loop (default)
========== =========================================
@@ -949,7 +949,7 @@ def move_nanmax(arr, window, axis=-1, method='loop'):
The following moving window methods are available:
========== =========================================
'filter' scipy.ndimage.maximum_filter1d
- 'strides' strides tricks (ndim < 4)
+ 'strides' strides tricks
'loop' brute force python loop (default)
========== =========================================
@@ -1090,7 +1090,7 @@ def move_median(arr, window, axis=-1, method='loop'):
The following moving window methods are available:
========== =====================================
'loop' brute force python loop (default)
- 'strides' strides tricks (ndim < 4)
+ 'strides' strides tricks
========== =====================================
Returns
@@ -1142,7 +1142,7 @@ def move_func(func, arr, window, axis=-1, method='loop', **kwargs):
The following moving window methods are available:
========== =====================================
'loop' brute force python loop (default)
- 'strides' strides tricks (ndim < 4)
+ 'strides' strides tricks
========== =====================================
Returns
@@ -1203,40 +1203,20 @@ def move_func_strides(func, arr, window, axis=-1, **kwargs):
if window > arr.shape[axis]:
raise ValueError("`window` is too long.")
ndim = arr.ndim
- as_strided = np.lib.stride_tricks.as_strided
idx = range(ndim)
axis = idx[axis]
arrshape0 = tuple(arr.shape)
if axis >= ndim:
raise IndexError("`axis` is out of range.")
- if ndim == 1:
- strides = arr.strides
- shape = (arr.size - window + 1, window)
- strides = 2 * strides
- z = as_strided(arr, shape=shape, strides=strides)
- y = func(z, axis=1, **kwargs)
- elif ndim == 2:
- if axis == 1:
- arr = arr.T
- strides = arr.strides
- shape = (arr.shape[0] - window + 1, window, arr.shape[1])
- strides = (strides[0],) + strides
- z = as_strided(arr, shape=shape, strides=strides)
- y = func(z, axis=1, **kwargs)
- if axis == 1:
- y = y.T
- elif ndim == 3:
- if axis > 0:
- arr = arr.swapaxes(0, axis)
- strides = arr.strides
- shape = (arr.shape[0]-window+1, window, arr.shape[1], arr.shape[2])
- strides = (strides[0],) + strides
- z = as_strided(arr, shape=shape, strides=strides)
- y = func(z, axis=1, **kwargs)
- if axis > 0:
- y = y.swapaxes(0, axis)
- else:
- raise ValueError("Only 1d, 2d, and 3d input arrays are supported.")
+
+ strides = arr.strides
+ num_windows = arr.shape[axis] - window + 1
+ shape = arr.shape[:axis] + (num_windows, window) + arr.shape[axis + 1:]
+ strides = (strides[:axis] + (strides[axis], strides[axis])
+ + strides[axis + 1:])
+ z = np.lib.stride_tricks.as_strided(arr, shape=shape, strides=strides)
+ y = func(z, axis=(axis + 1), **kwargs)
+
ynan = np.empty(arrshape0)
ynan.fill(np.nan)
index = [slice(None)] * ndim
37 bottleneck/tests/move_test.py
View
@@ -44,7 +44,8 @@ def arrays(dtypes=bn.dtypes, nans=True):
def unit_maker(func, func0, decimal=np.inf, nans=True):
"Test that bn.xxx gives the same output as a reference function."
- msg = '\nfunc %s | window %d | input %s (%s) | shape %s | axis %s\n'
+ msg = ('\nfunc %s | window %d | input %s (%s) | shape %s | axis %s | '
+ 'reference_method %r\n')
msg += '\nInput array:\n%s\n'
for i, arr in enumerate(arrays(nans=nans)):
for axis in range(-arr.ndim, arr.ndim):
@@ -52,22 +53,24 @@ def unit_maker(func, func0, decimal=np.inf, nans=True):
if len(windows) == 0:
windows = [1]
for window in windows:
- with np.errstate(invalid='ignore'):
- actual = func(arr, window, axis=axis)
- desired = func0(arr, window, axis=axis, method='loop')
- tup = (func.__name__, window, 'a'+str(i), str(arr.dtype),
- str(arr.shape), str(axis), arr)
- err_msg = msg % tup
- if (decimal < np.inf) and (np.isfinite(arr).sum() > 0):
- assert_array_almost_equal(actual, desired, decimal,
- err_msg)
- else:
- assert_array_equal(actual, desired, err_msg)
- err_msg += '\n dtype mismatch %s %s'
- if hasattr(actual, 'dtype') or hasattr(desired, 'dtype'):
- da = actual.dtype
- dd = desired.dtype
- assert_equal(da, dd, err_msg % (da, dd))
+ for reference_method in ['loop', 'strides']:
+ with np.errstate(invalid='ignore'):
+ actual = func(arr, window, axis=axis)
+ desired = func0(arr, window, axis=axis,
+ method=reference_method)
+ tup = (func.__name__, window, 'a'+str(i), str(arr.dtype),
+ str(arr.shape), str(axis), reference_method, arr)
+ err_msg = msg % tup
+ if (decimal < np.inf) and (np.isfinite(arr).sum() > 0):
+ assert_array_almost_equal(actual, desired, decimal,
+ err_msg)
+ else:
+ assert_array_equal(actual, desired, err_msg)
+ err_msg += '\n dtype mismatch %s %s'
+ if hasattr(actual, 'dtype') or hasattr(desired, 'dtype'):
+ da = actual.dtype
+ dd = desired.dtype
+ assert_equal(da, dd, err_msg % (da, dd))
def test_move_sum():
Please sign in to comment.
Something went wrong with that request. Please try again.