Skip to content

Commit

Permalink
Bug fixes and unit tests for size zero input arrays.
Browse files Browse the repository at this point in the history
Closes #7.
  • Loading branch information
kwgoodman committed Mar 2, 2011
1 parent 858196c commit d3bfcb4
Show file tree
Hide file tree
Showing 16 changed files with 278 additions and 86 deletions.
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,6 @@ After you have installed Bottleneck, run the suite of unit tests::
>>> import bottleneck as bn
>>> bn.test()
<snip>
Ran 46 tests in 40.457s
Ran 46 tests in 43.457s
OK
<nose.result.TextTestResult run=46 errors=0 failures=0>
4 changes: 4 additions & 0 deletions RELEASE.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ contains X new functions.

- Function removed: group_nanmean()

**Bug fix**

- Some functions choked on size zero input arrays

Older versions
==============

Expand Down
14 changes: 10 additions & 4 deletions bottleneck/slow/func.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ def _nanmedian(arr1d): # This only works on 1d arrays
return np.nan
return np.median(x)

# Feb 2011: patched nanmedian to handle nanmedian(a, 1) with a = np.ones((2,0))
def scipy_nanmedian(x, axis=0):
"""
Compute the median along the given axis ignoring nan values.
Expand Down Expand Up @@ -321,10 +322,15 @@ def scipy_nanmedian(x, axis=0):
x, axis = _chk_asarray(x, axis)
if x.ndim == 0:
return float(x.item())
x = x.copy()
x = np.apply_along_axis(_nanmedian, axis, x)
if x.ndim == 0:
x = float(x.item())
shape = list(x.shape)
shape.pop(axis)
if 0 in shape:
x = np.empty(shape)
else:
x = x.copy()
x = np.apply_along_axis(_nanmedian, axis, x)
if x.ndim == 0:
x = float(x.item())
return x

def _chk_asarray(a, axis):
Expand Down
2 changes: 1 addition & 1 deletion bottleneck/src/func/func_header.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ from numpy cimport NPY_FLOAT64 as NPY_float64
from numpy cimport (PyArray_EMPTY, PyArray_TYPE, PyArray_NDIM,
PyArray_SIZE, PyArray_DIMS, import_array,
PyArray_ArgSort, NPY_QUICKSORT, NPY_CORDER,
PyArray_Ravel)
PyArray_Ravel, PyArray_FillWithScalar)
import_array()
import bottleneck as bn

Expand Down
8 changes: 8 additions & 0 deletions bottleneck/src/template/func/median.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

loop = {}
loop[1] = """\
if nINDEX0 == 0:
return np.FLOAT(NAN)
k = nAXIS >> 1
l = 0
r = nAXIS - 1
Expand Down Expand Up @@ -43,6 +45,9 @@
return np.FLOAT(a[k])
"""
loop[2] = """\
if nINDEX1 == 0:
PyArray_FillWithScalar(y, NAN)
return y
for iINDEX0 in range(nINDEX0):
k = nAXIS >> 1
l = 0
Expand Down Expand Up @@ -75,6 +80,9 @@
return y
"""
loop[3] = """\
if nINDEX2 == 0:
PyArray_FillWithScalar(y, NAN)
return y
for iINDEX0 in range(nINDEX0):
for iINDEX1 in range(nINDEX1):
k = nAXIS >> 1
Expand Down
22 changes: 18 additions & 4 deletions bottleneck/src/template/func/nanargmax.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ def NAME_NDIMd_DTYPE_axisAXIS(np.ndarray[np.DTYPE_t, ndim=NDIM] a):

loop = {}
loop[1] = """\
if nINDEX0 == 0:
msg = "numpy.nanargmax raises on a.shape[axis]==0; Bottleneck too."
raise ValueError(msg)
amax = MINDTYPE
for iINDEX0 in range(nINDEX0 - 1, -1, -1):
ai = a[INDEXALL]
Expand All @@ -41,6 +44,9 @@ def NAME_NDIMd_DTYPE_axisAXIS(np.ndarray[np.DTYPE_t, ndim=NDIM] a):
return NAN
"""
loop[2] = """\
if nINDEX1 == 0:
msg = "numpy.nanargmax raises on a.shape[axis]==0; Bottleneck too."
raise ValueError(msg)
for iINDEX0 in range(nINDEX0 - 1, -1, -1):
amax = MINDTYPE
allnan = 1
Expand All @@ -57,6 +63,9 @@ def NAME_NDIMd_DTYPE_axisAXIS(np.ndarray[np.DTYPE_t, ndim=NDIM] a):
return y
"""
loop[3] = """\
if nINDEX2 == 0:
msg = "numpy.nanargmax raises on a.shape[axis]==0; Bottleneck too."
raise ValueError(msg)
for iINDEX0 in range(nINDEX0 - 1, -1, -1):
for iINDEX1 in range(nINDEX1 - 1, -1, -1):
amax = MINDTYPE
Expand Down Expand Up @@ -84,6 +93,9 @@ def NAME_NDIMd_DTYPE_axisAXIS(np.ndarray[np.DTYPE_t, ndim=NDIM] a):

loop = {}
loop[1] = """\
if nINDEX0 == 0:
msg = "numpy.nanargmax raises on a.shape[axis]==0; Bottleneck too."
raise ValueError(msg)
amax = MINDTYPE
for iINDEX0 in range(nINDEX0):
ai = a[INDEXALL]
Expand All @@ -93,6 +105,9 @@ def NAME_NDIMd_DTYPE_axisAXIS(np.ndarray[np.DTYPE_t, ndim=NDIM] a):
return np.NPINT(idx)
"""
loop[2] = """\
if nINDEX1 == 0:
msg = "numpy.nanargmax raises on a.shape[axis]==0; Bottleneck too."
raise ValueError(msg)
for iINDEX0 in range(nINDEX0 - 1, -1, -1):
amax = MINDTYPE
for iINDEX1 in range(nINDEX1 - 1, -1, -1):
Expand All @@ -104,6 +119,9 @@ def NAME_NDIMd_DTYPE_axisAXIS(np.ndarray[np.DTYPE_t, ndim=NDIM] a):
return y
"""
loop[3] = """\
if nINDEX2 == 0:
msg = "numpy.nanargmax raises on a.shape[axis]==0; Bottleneck too."
raise ValueError(msg)
for iINDEX0 in range(nINDEX0 - 1, -1, -1):
for iINDEX1 in range(nINDEX1 - 1, -1, -1):
amax = MINDTYPE
Expand Down Expand Up @@ -237,10 +255,6 @@ def nanargmax_selector(arr, axis):
a = np.array(arr, copy=False)
cdef int ndim = PyArray_NDIM(a)
cdef int dtype = PyArray_TYPE(a)
cdef int size = PyArray_SIZE(a)
if size == 0:
msg = "numpy.nanargmax() raises on size=0; so Bottleneck does too."
raise ValueError, msg
if axis is not None:
if axis < 0:
axis += ndim
Expand Down
23 changes: 18 additions & 5 deletions bottleneck/src/template/func/nanargmin.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ def NAME_NDIMd_DTYPE_axisAXIS(np.ndarray[np.DTYPE_t, ndim=NDIM] a):

loop = {}
loop[1] = """\
if nINDEX0 == 0:
msg = "numpy.nanargmin raises on a.shape[axis]==0; Bottleneck too."
raise ValueError(msg)
amin = MAXDTYPE
for iINDEX0 in range(nINDEX0 - 1, -1, -1):
ai = a[INDEXALL]
Expand All @@ -41,6 +44,9 @@ def NAME_NDIMd_DTYPE_axisAXIS(np.ndarray[np.DTYPE_t, ndim=NDIM] a):
return NAN
"""
loop[2] = """\
if nINDEX1 == 0:
msg = "numpy.nanargmin raises on a.shape[axis]==0; Bottleneck too."
raise ValueError(msg)
for iINDEX0 in range(nINDEX0 - 1, -1, -1):
amin = MAXDTYPE
allnan = 1
Expand All @@ -57,6 +63,9 @@ def NAME_NDIMd_DTYPE_axisAXIS(np.ndarray[np.DTYPE_t, ndim=NDIM] a):
return y
"""
loop[3] = """\
if nINDEX2 == 0:
msg = "numpy.nanargmin raises on a.shape[axis]==0; Bottleneck too."
raise ValueError(msg)
for iINDEX0 in range(nINDEX0 - 1, -1, -1):
for iINDEX1 in range(nINDEX1 - 1, -1, -1):
amin = MAXDTYPE
Expand All @@ -75,7 +84,6 @@ def NAME_NDIMd_DTYPE_axisAXIS(np.ndarray[np.DTYPE_t, ndim=NDIM] a):
"""

floats['loop'] = loop
#floats['loop'][1] = floats['loop'][1].replace('DTYPE2', NPINT)

# Int dtypes (not axis=None) ------------------------------------------------

Expand All @@ -84,6 +92,9 @@ def NAME_NDIMd_DTYPE_axisAXIS(np.ndarray[np.DTYPE_t, ndim=NDIM] a):

loop = {}
loop[1] = """\
if nINDEX0 == 0:
msg = "numpy.nanargmin raises on a.shape[axis]==0; Bottleneck too."
raise ValueError(msg)
amin = MAXDTYPE
for iINDEX0 in range(nINDEX0):
ai = a[INDEXALL]
Expand All @@ -93,6 +104,9 @@ def NAME_NDIMd_DTYPE_axisAXIS(np.ndarray[np.DTYPE_t, ndim=NDIM] a):
return np.NPINT(idx)
"""
loop[2] = """\
if nINDEX1 == 0:
msg = "numpy.nanargmin raises on a.shape[axis]==0; Bottleneck too."
raise ValueError(msg)
for iINDEX0 in range(nINDEX0 - 1, -1, -1):
amin = MAXDTYPE
for iINDEX1 in range(nINDEX1 - 1, -1, -1):
Expand All @@ -104,6 +118,9 @@ def NAME_NDIMd_DTYPE_axisAXIS(np.ndarray[np.DTYPE_t, ndim=NDIM] a):
return y
"""
loop[3] = """\
if nINDEX2 == 0:
msg = "numpy.nanmin raises on a.shape[axis]==0; so Bottleneck does."
raise ValueError(msg)
for iINDEX0 in range(nINDEX0 - 1, -1, -1):
for iINDEX1 in range(nINDEX1 - 1, -1, -1):
amin = MAXDTYPE
Expand Down Expand Up @@ -237,10 +254,6 @@ def nanargmin_selector(arr, axis):
a = np.array(arr, copy=False)
cdef int ndim = PyArray_NDIM(a)
cdef int dtype = PyArray_TYPE(a)
cdef int size = PyArray_SIZE(a)
if size == 0:
msg = "numpy.nanargmin() raises on size=0; so Bottleneck does too."
raise ValueError, msg
if axis is not None:
if axis < 0:
axis += ndim
Expand Down
34 changes: 30 additions & 4 deletions bottleneck/src/template/func/nanmax.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ def NAME_NDIMd_DTYPE_axisAXIS(np.ndarray[np.DTYPE_t, ndim=NDIM] a):

loop = {}
loop[2] = """\
if nINDEX1 == 0:
msg = "numpy.nanmax raises on a.shape[axis]==0; so Bottleneck does."
raise ValueError(msg)
for iINDEX0 in range(nINDEX0):
amax = MINDTYPE
allnan = 1
Expand All @@ -42,6 +45,9 @@ def NAME_NDIMd_DTYPE_axisAXIS(np.ndarray[np.DTYPE_t, ndim=NDIM] a):
return y
"""
loop[3] = """\
if nINDEX2 == 0:
msg = "numpy.nanmax raises on a.shape[axis]==0; so Bottleneck does."
raise ValueError(msg)
for iINDEX0 in range(nINDEX0):
for iINDEX1 in range(nINDEX1):
amax = MINDTYPE
Expand All @@ -67,6 +73,9 @@ def NAME_NDIMd_DTYPE_axisAXIS(np.ndarray[np.DTYPE_t, ndim=NDIM] a):

loop = {}
loop[1] = """\
if nINDEX0 == 0:
m = "numpy.nanmax raises on a.size==0 and axis=None; Bottleneck too."
raise ValueError(m)
amax = MINDTYPE
for iINDEX0 in range(nINDEX0):
ai = a[INDEXALL]
Expand All @@ -79,6 +88,9 @@ def NAME_NDIMd_DTYPE_axisAXIS(np.ndarray[np.DTYPE_t, ndim=NDIM] a):
return NAN
"""
loop[2] = """\
if nINDEX0 * nINDEX1 == 0:
m = "numpy.nanmax raises on a.size==0 and axis=None; Bottleneck too."
raise ValueError(m)
amax = MINDTYPE
for iINDEX0 in range(nINDEX0):
for iINDEX1 in range(nINDEX1):
Expand All @@ -92,6 +104,9 @@ def NAME_NDIMd_DTYPE_axisAXIS(np.ndarray[np.DTYPE_t, ndim=NDIM] a):
return NAN
"""
loop[3] = """\
if nINDEX0 * nINDEX1 * nINDEX2 == 0:
m = "numpy.nanmax raises on a.size==0 and axis=None; Bottleneck too."
raise ValueError(m)
amax = MINDTYPE
for iINDEX0 in range(nINDEX0):
for iINDEX1 in range(nINDEX1):
Expand All @@ -115,6 +130,9 @@ def NAME_NDIMd_DTYPE_axisAXIS(np.ndarray[np.DTYPE_t, ndim=NDIM] a):

loop = {}
loop[2] = """\
if nINDEX1 == 0:
msg = "numpy.nanmax raises on a.shape[axis]==0; so Bottleneck does."
raise ValueError(msg)
for iINDEX0 in range(nINDEX0):
amax = MINDTYPE
for iINDEX1 in range(nINDEX1):
Expand All @@ -125,6 +143,9 @@ def NAME_NDIMd_DTYPE_axisAXIS(np.ndarray[np.DTYPE_t, ndim=NDIM] a):
return y
"""
loop[3] = """\
if nINDEX2 == 0:
msg = "numpy.nanmax raises on a.shape[axis]==0; so Bottleneck does."
raise ValueError(msg)
for iINDEX0 in range(nINDEX0):
for iINDEX1 in range(nINDEX1):
amax = MINDTYPE
Expand All @@ -145,6 +166,9 @@ def NAME_NDIMd_DTYPE_axisAXIS(np.ndarray[np.DTYPE_t, ndim=NDIM] a):

loop = {}
loop[1] = """\
if nINDEX0 == 0:
m = "numpy.nanmax raises on a.size==0 and axis=None; Bottleneck too."
raise ValueError(m)
amax = MINDTYPE
for iINDEX0 in range(nINDEX0):
ai = a[INDEXALL]
Expand All @@ -153,6 +177,9 @@ def NAME_NDIMd_DTYPE_axisAXIS(np.ndarray[np.DTYPE_t, ndim=NDIM] a):
return np.DTYPE(amax)
"""
loop[2] = """\
if nINDEX0 * nINDEX1 == 0:
m = "numpy.nanmax raises on a.size==0 and axis=None; Bottleneck too."
raise ValueError(m)
amax = MINDTYPE
for iINDEX0 in range(nINDEX0):
for iINDEX1 in range(nINDEX1):
Expand All @@ -162,6 +189,9 @@ def NAME_NDIMd_DTYPE_axisAXIS(np.ndarray[np.DTYPE_t, ndim=NDIM] a):
return np.DTYPE(amax)
"""
loop[3] = """\
if nINDEX0 * nINDEX1 * nINDEX2 == 0:
m = "numpy.nanmax raises on a.size==0 and axis=None; Bottleneck too."
raise ValueError(m)
amax = MINDTYPE
for iINDEX0 in range(nINDEX0):
for iINDEX1 in range(nINDEX1):
Expand Down Expand Up @@ -295,10 +325,6 @@ def nanmax_selector(arr, axis):
a = np.array(arr, copy=False)
cdef int ndim = PyArray_NDIM(a)
cdef int dtype = PyArray_TYPE(a)
cdef int size = PyArray_SIZE(a)
if size == 0:
msg = "numpy.nanmax() raises on size=0 input; so Bottleneck does too."
raise ValueError, msg
if (axis < 0) and (axis is not None):
axis += ndim
cdef tuple key = (ndim, dtype, axis)
Expand Down
Loading

0 comments on commit d3bfcb4

Please sign in to comment.