Skip to content

Commit

Permalink
Less overhead in selector functions. Plus:
Browse files Browse the repository at this point in the history
Removed window parameter from moving window
selector functions signature. Not needed.
  • Loading branch information
kwgoodman committed Jan 19, 2011
1 parent cbe997a commit 0dde7e1
Show file tree
Hide file tree
Showing 46 changed files with 260 additions and 278 deletions.
38 changes: 19 additions & 19 deletions README.rst
Expand Up @@ -46,17 +46,17 @@ Bottleneck is fast::

>>> arr = np.random.rand(100, 100)
>>> timeit np.nanmax(arr)
10000 loops, best of 3: 99.6 us per loop
10000 loops, best of 3: 93.9 us per loop
>>> timeit bn.nanmax(arr)
100000 loops, best of 3: 15.3 us per loop
100000 loops, best of 3: 14.3 us per loop

Let's not forget to add some NaNs::

>>> arr[arr > 0.5] = np.nan
>>> timeit np.nanmax(arr)
10000 loops, best of 3: 146 us per loop
10000 loops, best of 3: 139 us per loop
>>> timeit bn.nanmax(arr)
100000 loops, best of 3: 15.2 us per loop
100000 loops, best of 3: 14.3 us per loop

Bottleneck comes with a benchmark suite. To run the benchmark::
Expand All @@ -71,21 +71,21 @@ Bottleneck comes with a benchmark suite. To run the benchmark::

no NaN no NaN no NaN NaN NaN NaN
(10,10) (100,100) (1000,1000) (10,10) (100,100) (1000,1000)
median 6.00 13.49 7.40 5.34 3.29 2.92
nanmedian 155.51 132.44 8.89 160.37 174.60 8.13
nansum 7.32 5.83 1.73 7.26 6.63 1.72
nanmax 8.08 5.86 1.69 8.25 9.11 1.69
nanmean 15.13 12.82 3.01 16.03 26.48 5.05
nanstd 18.68 9.40 2.65 19.52 16.63 3.67
nanargmax 7.48 5.69 2.64 7.53 8.32 2.84
move_sum 7.33 7.82 11.96 7.42 8.26 13.08
move_nansum 20.59 19.15 29.42 19.96 24.45 27.26
move_mean 7.67 4.26 14.29 7.53 8.22 13.40
move_nanmean 21.46 11.49 29.43 22.42 14.13 28.88
move_std 13.00 3.31 22.92 15.70 20.14 29.51
move_nanstd 25.58 6.14 33.55 28.91 6.96 35.87
move_max 3.26 3.64 9.38 3.70 5.56 11.70
move_nanmax 16.84 6.23 19.49 17.57 14.37 26.70
median 6.59 13.83 7.22 5.83 3.50 2.83
nanmedian 160.86 135.68 8.27 163.28 179.70 8.05
nansum 7.51 5.85 1.71 7.56 6.74 1.70
nanmax 8.40 5.89 1.67 8.66 9.37 1.68
nanmean 16.02 13.48 2.98 16.77 27.22 4.97
nanstd 20.37 9.33 2.62 21.01 17.10 3.64
nanargmax 7.79 5.72 2.57 7.78 8.43 2.76
move_sum 7.82 7.86 14.41 7.82 8.20 13.95
move_nansum 18.89 18.73 29.44 18.31 23.83 29.83
move_mean 7.14 4.17 14.47 7.17 8.13 14.28
move_nanmean 22.34 11.56 29.87 22.77 14.09 30.91
move_std 12.26 3.31 22.94 14.45 19.95 29.92
move_nanstd 25.12 6.14 34.99 27.98 6.97 36.13
move_max 3.07 3.64 9.33 3.64 5.44 11.76
move_nanmax 16.04 6.28 19.38 16.72 14.56 27.44

Reference functions:
median np.median
Expand Down
4 changes: 4 additions & 0 deletions RELEASE.rst
Expand Up @@ -35,6 +35,10 @@ The third release of Bottleneck is faster and contains 10 new function.
- You can now specify the dtype and axis to use in the benchmark timings
- Improved documentation and more unit tests

**Breaks from 0.2.0**

- Low-level moving window selector functions no longer take window as input

**Bug fix**

- int input array resulted in call to slow, non-cython version of move_nanmean
Expand Down
16 changes: 8 additions & 8 deletions bottleneck/benchmark/bench.py
Expand Up @@ -226,7 +226,7 @@ def getsetups(setup):
setup = """
from bottleneck.slow.move import move_sum as scipy_move_sum
w = a.shape[AXIS] / 5
func, a = bn.move.move_sum_selector(a, window=w, axis=AXIS)
func, a = bn.move.move_sum_selector(a, axis=AXIS)
"""
run['setups'] = getsetups(setup)
if axis != 'None':
Expand All @@ -246,7 +246,7 @@ def getsetups(setup):
setup = """
from bottleneck.slow.move import move_nansum as scipy_move_nansum
w = a.shape[AXIS] / 5
func, a = bn.move.move_nansum_selector(a, window=w, axis=AXIS)
func, a = bn.move.move_nansum_selector(a, axis=AXIS)
"""
run['setups'] = getsetups(setup)
if axis != 'None':
Expand All @@ -266,7 +266,7 @@ def getsetups(setup):
setup = """
from bottleneck.slow.move import move_mean as scipy_move_mean
w = a.shape[AXIS] / 5
func, a = bn.move.move_mean_selector(a, window=w, axis=AXIS)
func, a = bn.move.move_mean_selector(a, axis=AXIS)
"""
run['setups'] = getsetups(setup)
if axis != 'None':
Expand All @@ -286,7 +286,7 @@ def getsetups(setup):
setup = """
from bottleneck.slow.move import move_nanmean as scipy_move_nanmean
w = a.shape[AXIS] / 5
func, a = bn.move.move_nanmean_selector(a, window=w, axis=AXIS)
func, a = bn.move.move_nanmean_selector(a, axis=AXIS)
"""
run['setups'] = getsetups(setup)
if axis != 'None':
Expand All @@ -306,7 +306,7 @@ def getsetups(setup):
setup = """
from bottleneck.slow.move import move_std as scipy_move_std
w = a.shape[AXIS] / 5
func, a = bn.move.move_std_selector(a, window=w, axis=AXIS)
func, a = bn.move.move_std_selector(a, axis=AXIS)
"""
run['setups'] = getsetups(setup)
if axis != 'None':
Expand All @@ -326,7 +326,7 @@ def getsetups(setup):
setup = """
from bottleneck.slow.move import move_nanstd as scipy_move_nanstd
w = a.shape[AXIS] / 5
func, a = bn.move.move_nanstd_selector(a, window=w, axis=AXIS)
func, a = bn.move.move_nanstd_selector(a, axis=AXIS)
"""
run['setups'] = getsetups(setup)
if axis != 'None':
Expand All @@ -346,7 +346,7 @@ def getsetups(setup):
setup = """
from bottleneck.slow.move import move_max as scipy_move_max
w = a.shape[AXIS] / 5
func, a = bn.move.move_max_selector(a, window=w, axis=AXIS)
func, a = bn.move.move_max_selector(a, axis=AXIS)
"""
run['setups'] = getsetups(setup)
if axis != 'None':
Expand All @@ -366,7 +366,7 @@ def getsetups(setup):
setup = """
from bottleneck.slow.move import move_nanmax as scipy_move_nanmax
w = a.shape[AXIS] / 5
func, a = bn.move.move_nanmax_selector(a, window=w, axis=AXIS)
func, a = bn.move.move_nanmax_selector(a, axis=AXIS)
"""
run['setups'] = getsetups(setup)
if axis != 'None':
Expand Down
6 changes: 3 additions & 3 deletions bottleneck/src/func/median.pyx
Expand Up @@ -118,11 +118,9 @@ def median_selector(arr, axis):
cdef tuple key
cdef int ndim = a.ndim
cdef np.dtype dtype = a.dtype
if axis != None:
if axis is not None:
if axis < 0:
axis += ndim
if (axis < 0) or (axis >= ndim):
raise ValueError, "axis(=%d) out of bounds" % axis
else:
a = a.ravel()
axis = 0
Expand All @@ -131,6 +129,8 @@ def median_selector(arr, axis):
try:
func = median_dict[key]
except KeyError:
if (axis < 0) or (axis >= ndim):
raise ValueError, "axis(=%d) out of bounds" % axis
try:
func = median_slow_dict[axis]
except KeyError:
Expand Down
6 changes: 3 additions & 3 deletions bottleneck/src/func/nanargmax.pyx
Expand Up @@ -101,11 +101,9 @@ def nanargmax_selector(arr, axis):
if size == 0:
msg = "numpy.nanargmax() raises on size=0; so Bottleneck does too."
raise ValueError, msg
if axis != None:
if axis is not None:
if axis < 0:
axis += ndim
if (axis < 0) or (axis >= ndim):
raise ValueError, "axis(=%d) out of bounds" % axis
else:
a = a.ravel()
axis = 0
Expand All @@ -114,6 +112,8 @@ def nanargmax_selector(arr, axis):
try:
func = nanargmax_dict[key]
except KeyError:
if (axis < 0) or (axis >= ndim):
raise ValueError, "axis(=%d) out of bounds" % axis
try:
func = nanargmax_slow_dict[axis]
except KeyError:
Expand Down
6 changes: 3 additions & 3 deletions bottleneck/src/func/nanargmin.pyx
Expand Up @@ -101,11 +101,9 @@ def nanargmin_selector(arr, axis):
if size == 0:
msg = "numpy.nanargmin() raises on size=0; so Bottleneck does too."
raise ValueError, msg
if axis != None:
if axis is not None:
if axis < 0:
axis += ndim
if (axis < 0) or (axis >= ndim):
raise ValueError, "axis(=%d) out of bounds" % axis
else:
a = a.ravel()
axis = 0
Expand All @@ -114,6 +112,8 @@ def nanargmin_selector(arr, axis):
try:
func = nanargmin_dict[key]
except KeyError:
if (axis < 0) or (axis >= ndim):
raise ValueError, "axis(=%d) out of bounds" % axis
try:
func = nanargmin_slow_dict[axis]
except KeyError:
Expand Down
10 changes: 5 additions & 5 deletions bottleneck/src/func/nanmax.pyx
Expand Up @@ -102,15 +102,15 @@ def nanmax_selector(arr, axis):
if size == 0:
msg = "numpy.nanmax() raises on size=0 input; so Bottleneck does too."
raise ValueError, msg
if axis != None:
if axis < 0:
axis += ndim
if (axis < 0) or (axis >= ndim):
raise ValueError, "axis(=%d) out of bounds" % axis
if (axis < 0) and (axis is not None):
axis += ndim
cdef tuple key = (ndim, dtype, axis)
try:
func = nanmax_dict[key]
except KeyError:
if axis is not None:
if (axis < 0) or (axis >= ndim):
raise ValueError, "axis(=%d) out of bounds" % axis
try:
func = nanmax_slow_dict[axis]
except KeyError:
Expand Down
10 changes: 5 additions & 5 deletions bottleneck/src/func/nanmean.pyx
Expand Up @@ -117,15 +117,15 @@ def nanmean_selector(arr, axis):
a = np.array(arr, copy=False)
cdef int ndim = a.ndim
cdef np.dtype dtype = a.dtype
if axis != None:
if axis < 0:
axis += ndim
if (axis < 0) or (axis >= ndim):
raise ValueError, "axis(=%d) out of bounds" % axis
if (axis < 0) and (axis is not None):
axis += ndim
cdef tuple key = (ndim, dtype, axis)
try:
func = nanmean_dict[key]
except KeyError:
if axis is not None:
if (axis < 0) or (axis >= ndim):
raise ValueError, "axis(=%d) out of bounds" % axis
try:
func = nanmean_slow_dict[axis]
except KeyError:
Expand Down
7 changes: 3 additions & 4 deletions bottleneck/src/func/nanmedian.pyx
Expand Up @@ -113,11 +113,9 @@ def nanmedian_selector(arr, axis):
cdef tuple key
cdef int ndim = a.ndim
cdef np.dtype dtype = a.dtype
if axis != None:
if axis is not None:
if axis < 0:
axis += ndim
if (axis < 0) or (axis >= ndim):
raise ValueError, "axis(=%d) out of bounds" % axis
else:
a = a.ravel()
axis = 0
Expand All @@ -126,7 +124,8 @@ def nanmedian_selector(arr, axis):
try:
func = nanmedian_dict[key]
except KeyError:
pass
if (axis < 0) or (axis >= ndim):
raise ValueError, "axis(=%d) out of bounds" % axis
try:
func = nanmedian_slow_dict[axis]
except KeyError:
Expand Down
10 changes: 5 additions & 5 deletions bottleneck/src/func/nanmin.pyx
Expand Up @@ -102,15 +102,15 @@ def nanmin_selector(arr, axis):
if size == 0:
msg = "numpy.nanmin() raises on size=0 input; so Bottleneck does too."
raise ValueError, msg
if axis != None:
if axis < 0:
axis += ndim
if (axis < 0) or (axis >= ndim):
raise ValueError, "axis(=%d) out of bounds" % axis
if (axis < 0) and (axis is not None):
axis += ndim
cdef tuple key = (ndim, dtype, axis)
try:
func = nanmin_dict[key]
except KeyError:
if axis is not None:
if (axis < 0) or (axis >= ndim):
raise ValueError, "axis(=%d) out of bounds" % axis
try:
func = nanmin_slow_dict[axis]
except KeyError:
Expand Down
10 changes: 5 additions & 5 deletions bottleneck/src/func/nanstd.pyx
Expand Up @@ -130,15 +130,15 @@ def nanstd_selector(arr, axis):
a = np.array(arr, copy=False)
cdef int ndim = a.ndim
cdef np.dtype dtype = a.dtype
if axis != None:
if axis < 0:
axis += ndim
if (axis < 0) or (axis >= ndim):
raise ValueError, "axis(=%d) out of bounds" % axis
if (axis < 0) and (axis is not None):
axis += ndim
cdef tuple key = (ndim, dtype, axis)
try:
func = nanstd_dict[key]
except KeyError:
if axis is not None:
if (axis < 0) or (axis >= ndim):
raise ValueError, "axis(=%d) out of bounds" % axis
try:
func = nanstd_slow_dict[axis]
except KeyError:
Expand Down
10 changes: 5 additions & 5 deletions bottleneck/src/func/nansum.pyx
Expand Up @@ -116,15 +116,15 @@ def nansum_selector(arr, axis):
if dtype < np.int_:
a = a.astype(np.int_)
dtype = a.dtype
if axis != None:
if axis < 0:
axis += ndim
if (axis < 0) or (axis >= ndim):
raise ValueError, "axis(=%d) out of bounds" % axis
if (axis < 0) and (axis is not None):
axis += ndim
cdef tuple key = (ndim, dtype, axis)
try:
func = nansum_dict[key]
except KeyError:
if axis is not None:
if (axis < 0) or (axis >= ndim):
raise ValueError, "axis(=%d) out of bounds" % axis
try:
func = nansum_slow_dict[axis]
except KeyError:
Expand Down
10 changes: 5 additions & 5 deletions bottleneck/src/func/nanvar.pyx
Expand Up @@ -129,15 +129,15 @@ def nanvar_selector(arr, axis):
a = np.array(arr, copy=False)
cdef int ndim = a.ndim
cdef np.dtype dtype = a.dtype
if axis != None:
if axis < 0:
axis += ndim
if (axis < 0) or (axis >= ndim):
raise ValueError, "axis(=%d) out of bounds" % axis
if (axis < 0) and (axis is not None):
axis += ndim
cdef tuple key = (ndim, dtype, axis)
try:
func = nanvar_dict[key]
except KeyError:
if axis is not None:
if (axis < 0) or (axis >= ndim):
raise ValueError, "axis(=%d) out of bounds" % axis
try:
func = nanvar_slow_dict[axis]
except KeyError:
Expand Down
4 changes: 2 additions & 2 deletions bottleneck/src/group/group_nanmean.pyx
Expand Up @@ -164,8 +164,6 @@ def group_nanmean_selector(arr, label, order=None, int axis=0):
cdef np.dtype dtype = a.dtype
if axis < 0:
axis += ndim
if (axis < 0) or (axis >= ndim):
raise ValueError("axis(=%d) out of bounds" % axis)
cdef int narr = a.shape[axis], nlabel = len(label)
if narr != nlabel:
msg = "Number of labels (=%d) must equal number of elements (=%d) "
Expand All @@ -175,6 +173,8 @@ def group_nanmean_selector(arr, label, order=None, int axis=0):
try:
func = group_nanmean_dict[key]
except KeyError:
if (axis < 0) or (axis >= ndim):
raise ValueError("axis(=%d) out of bounds" % axis)
tup = (str(ndim), str(dtype))
raise TypeError("Unsupported ndim/dtype (%s/%s)." % tup)
label_dict, order = group_mapper(label, order)
Expand Down

0 comments on commit 0dde7e1

Please sign in to comment.