diff options
author | Mark Wiebe <mwiebe@enthought.com> | 2011-08-03 12:27:54 -0500 |
---|---|---|
committer | Charles Harris <charlesr.harris@gmail.com> | 2011-08-27 07:26:51 -0600 |
commit | 0e5fb25980859a44fed8a1b5ddc85075d28c7883 (patch) | |
tree | 6ff12a4267493fb8e2b6702ebaa6872671b55819 /numpy | |
parent | b471b5aace551d294f2ffe4f7be569fd6f148f50 (diff) | |
download | numpy-0e5fb25980859a44fed8a1b5ddc85075d28c7883.tar.gz |
ENH: missingdata: Change things to help scipy pass its tests
Improving the NumPy tests a bit to catch these errors better...
Diffstat (limited to 'numpy')
-rw-r--r-- | numpy/core/fromnumeric.py | 18 | ||||
-rw-r--r-- | numpy/core/src/multiarray/mapping.c | 131 | ||||
-rw-r--r-- | numpy/core/src/multiarray/shape.c | 2 | ||||
-rw-r--r-- | numpy/core/src/umath/ufunc_object.c | 14 | ||||
-rw-r--r-- | numpy/core/tests/test_indexing.py | 25 | ||||
-rw-r--r-- | numpy/core/tests/test_ufunc.py | 10 | ||||
-rw-r--r-- | numpy/matrixlib/tests/test_defmatrix.py | 36 |
7 files changed, 134 insertions, 102 deletions
diff --git a/numpy/core/fromnumeric.py b/numpy/core/fromnumeric.py index f102f4b58..c7e390c73 100644 --- a/numpy/core/fromnumeric.py +++ b/numpy/core/fromnumeric.py @@ -1455,7 +1455,14 @@ def sum(a, axis=None, dtype=None, out=None): out[...] = res return out return res - return um.add.reduce(a, axis=axis, dtype=dtype, out=out) + elif not (type(a) is mu.ndarray): + try: + sum = a.sum + except AttributeError: + return um.add.reduce(a, axis=axis, dtype=dtype, out=out) + return sum(axis=axis, dtype=dtype, out=out) + else: + return um.add.reduce(a, axis=axis, dtype=dtype, out=out) def product (a, axis=None, dtype=None, out=None): """ @@ -1998,7 +2005,14 @@ def prod(a, axis=None, dtype=None, out=None): True """ - return um.multiply.reduce(a, axis=axis, dtype=dtype, out=out) + if not (type(a) is mu.ndarray): + try: + prod = a.prod + except AttributeError: + return um.multiply.reduce(a, axis=axis, dtype=dtype, out=out) + return prod(axis=axis, dtype=dtype, out=out) + else: + return um.multiply.reduce(a, axis=axis, dtype=dtype, out=out) def cumprod(a, axis=None, dtype=None, out=None): """ diff --git a/numpy/core/src/multiarray/mapping.c b/numpy/core/src/multiarray/mapping.c index d122eb214..91802ee34 100644 --- a/numpy/core/src/multiarray/mapping.c +++ b/numpy/core/src/multiarray/mapping.c @@ -698,8 +698,6 @@ array_boolean_subscript(PyArrayObject *self, PyArrayObject *ret; int self_has_maskna = PyArray_HASMASKNA(self), needs_api = 0; npy_intp bmask_size; - int bmask_axes[NPY_MAXDIMS]; - int *op_axes[2] = {NULL, NULL}; if (PyArray_DESCR(bmask)->type_num != NPY_BOOL) { PyErr_SetString(PyExc_TypeError, @@ -715,30 +713,15 @@ array_boolean_subscript(PyArrayObject *self, return NULL; } - /* - * If the boolean mask has one dimension, broadcast to - * the left instead of to the right. Other broadcasting - * is disallowed to minimize inconsistency with NumPy in - * general. - */ if (PyArray_NDIM(bmask) != PyArray_NDIM(self)) { - int i; - - if (PyArray_NDIM(bmask) != 1) { - PyErr_SetString(PyExc_ValueError, - "The boolean mask indexing array " - "is neither one-dimensional nor " - "matches the operand's number of " - "dimensions"); - return NULL; - } - op_axes[1] = bmask_axes; - bmask_axes[0] = 0; - for (i = 1; i < PyArray_NDIM(self); ++i) { - bmask_axes[i] = -1; - } + PyErr_SetString(PyExc_ValueError, + "The boolean mask assignment indexing array " + "must have the same number of dimensions as " + "the array being indexed"); + return NULL; } + /* * Since we've checked that the mask contains no NAs, we * can do a straightforward count of the boolean True values @@ -803,9 +786,8 @@ array_boolean_subscript(PyArrayObject *self, */ op_flags[1] = NPY_ITER_READONLY | NPY_ITER_IGNORE_MASKNA; - iter = NpyIter_AdvancedNew(2, op, flags, order, NPY_NO_CASTING, - op_flags, NULL, - PyArray_NDIM(self), op_axes, NULL, 0); + iter = NpyIter_MultiNew(2, op, flags, order, NPY_NO_CASTING, + op_flags, NULL); if (iter == NULL) { Py_DECREF(ret); return NULL; @@ -948,8 +930,6 @@ array_ass_boolean_subscript(PyArrayObject *self, int needs_api = 0; npy_intp bmask_size; char constant_valid_mask = 1; - int bmask_axes[NPY_MAXDIMS]; - int *op_axes[2] = {NULL, NULL}; if (PyArray_DESCR(bmask)->type_num != NPY_BOOL) { PyErr_SetString(PyExc_TypeError, @@ -959,34 +939,19 @@ array_ass_boolean_subscript(PyArrayObject *self, } if (PyArray_NDIM(v) > 1) { - PyErr_SetString(PyExc_TypeError, + PyErr_Format(PyExc_TypeError, "NumPy boolean array indexing assignment " - "requires a 0 or 1-dimensional input"); + "requires a 0 or 1-dimensional input, input " + "has %d dimensions", PyArray_NDIM(v)); return -1; } - /* - * If the boolean mask has one dimension, broadcast to - * the left instead of to the right. Other broadcasting - * is disallowed to minimize inconsistency with NumPy in - * general. - */ if (PyArray_NDIM(bmask) != PyArray_NDIM(self)) { - int i; - - if (PyArray_NDIM(bmask) != 1) { - PyErr_SetString(PyExc_ValueError, - "The boolean mask indexing array " - "is neither one-dimensional nor " - "matches the operand's number of " - "dimensions"); - return -1; - } - op_axes[1] = bmask_axes; - bmask_axes[0] = 0; - for (i = 1; i < PyArray_NDIM(self); ++i) { - bmask_axes[i] = -1; - } + PyErr_SetString(PyExc_ValueError, + "The boolean mask assignment indexing array " + "must have the same number of dimensions as " + "the array being indexed"); + return -1; } /* See the Boolean Indexing section of the missing data NEP */ @@ -1086,9 +1051,8 @@ array_ass_boolean_subscript(PyArrayObject *self, */ op_flags[1] = NPY_ITER_READONLY | NPY_ITER_IGNORE_MASKNA; - iter = NpyIter_AdvancedNew(2, op, flags, order, NPY_NO_CASTING, - op_flags, NULL, - PyArray_NDIM(self), op_axes, NULL, 0); + iter = NpyIter_MultiNew(2, op, flags, order, NPY_NO_CASTING, + op_flags, NULL); if (iter == NULL) { return -1; } @@ -1355,8 +1319,9 @@ array_subscript(PyArrayObject *self, PyObject *op) return NULL; } - /* Boolean indexing */ - if (PyArray_Check(op) && (PyArray_TYPE((PyArrayObject *)op) == NPY_BOOL)) { + /* Boolean indexing special case which supports mask NA */ + if (PyArray_Check(op) && (PyArray_TYPE((PyArrayObject *)op) == NPY_BOOL) + && (PyArray_NDIM(self) == PyArray_NDIM((PyArrayObject *)op))) { return (PyObject *)array_boolean_subscript(self, (PyArrayObject *)op, NPY_CORDER); } @@ -1644,12 +1609,15 @@ array_ass_sub(PyArrayObject *self, PyObject *index, PyObject *op) } PyErr_Clear(); - /* Boolean indexing */ + /* Boolean indexing special case with NA mask support */ if (PyArray_Check(index) && - (PyArray_TYPE((PyArrayObject *)index) == NPY_BOOL)) { + (PyArray_TYPE((PyArrayObject *)index) == NPY_BOOL) && + (PyArray_NDIM(self) == PyArray_NDIM((PyArrayObject *)index))) { + int retcode; PyArrayObject *op_arr; PyArray_Descr *dtype = NULL; - /* If it's an NA with no dtype, specify it explicitly */ + + /* If it's an NA with no dtype, specify the dtype explicitly */ if (NpyNA_Check(op) && ((NpyNA_fields *)op)->dtype == NULL) { dtype = PyArray_DESCR(self); Py_INCREF(dtype); @@ -1660,13 +1628,24 @@ array_ass_sub(PyArrayObject *self, PyObject *index, PyObject *op) return -1; } - return array_ass_boolean_subscript(self, - (PyArrayObject *)index, - (PyArrayObject *)op_arr, NPY_CORDER); + if (PyArray_NDIM(op_arr) < 2) { + retcode = array_ass_boolean_subscript(self, + (PyArrayObject *)index, + op_arr, NPY_CORDER); + Py_DECREF(op_arr); + return retcode; + } + /* + * Assigning from multi-dimensional 'op' in this case seems + * inconsistent, so falling through to old code for backwards + * compatibility. + */ + Py_DECREF(op_arr); } fancy = fancy_indexing_check(index); if (fancy != SOBJ_NOTFANCY) { + oned = ((PyArray_NDIM(self) == 1) && !(PyTuple_Check(index) && PyTuple_GET_SIZE(index) > 1)); mit = (PyArrayMapIterObject *) PyArray_MapIterNew(index, oned, fancy); @@ -1782,26 +1761,6 @@ array_subscript_nice(PyArrayObject *self, PyObject *op) PyArray_DESCR(self), (npy_mask)*maskna_item); } } - -#if 0 - int i; - char *item; - - for (i = 0; i < PyArray_NDIM(self); i++) { - if (vals[i] < 0) { - vals[i] += PyArray_DIMS(self)[i]; - } - if ((vals[i] < 0) || (vals[i] >= PyArray_DIMS(self)[i])) { - PyErr_Format(PyExc_IndexError, - "index (%"INTP_FMT") out of range "\ - "(0<=index<%"INTP_FMT") in dimension %d", - vals[i], PyArray_DIMS(self)[i], i); - return NULL; - } - } - item = PyArray_GetPtr(self, vals); - return PyArray_Scalar(item, PyArray_DESCR(self), (PyObject *)self); -#endif } PyErr_Clear(); @@ -1825,7 +1784,7 @@ array_subscript_nice(PyArrayObject *self, PyObject *op) return NULL; } if (PyArray_Check(mp) && PyArray_NDIM(mp) == 0) { - Bool noellipses = TRUE; + npy_bool noellipses = TRUE; if ((op == Py_Ellipsis) || PyString_Check(op) || PyUnicode_Check(op)) { noellipses = FALSE; } @@ -2302,7 +2261,7 @@ PyArray_MapIterNew(PyObject *indexobj, int oned, int fancy) return NULL; } - mit = (PyArrayMapIterObject *)_pya_malloc(sizeof(PyArrayMapIterObject)); + mit = (PyArrayMapIterObject *)PyArray_malloc(sizeof(PyArrayMapIterObject)); PyObject_Init((PyObject *)mit, &PyArrayMapIter_Type); if (mit == NULL) { return NULL; @@ -2362,7 +2321,7 @@ PyArray_MapIterNew(PyObject *indexobj, int oned, int fancy) PyTuple_SET_ITEM(mit->indexobj, i, PyInt_FromLong(0)); } } - if (PyArray_Check(indexobj) || !PyTuple_Check(indexobj)) { + else if (PyArray_Check(indexobj) || !PyTuple_Check(indexobj)) { mit->numiter = 1; indtype = PyArray_DescrFromType(NPY_INTP); arr = (PyArrayObject *)PyArray_FromAny(indexobj, indtype, 0, 0, @@ -2469,7 +2428,7 @@ arraymapiter_dealloc(PyArrayMapIterObject *mit) for (i = 0; i < mit->numiter; i++) { Py_XDECREF(mit->iters[i]); } - _pya_free(mit); + PyArray_free(mit); } /* diff --git a/numpy/core/src/multiarray/shape.c b/numpy/core/src/multiarray/shape.c index e2af79afd..9a3c29ad9 100644 --- a/numpy/core/src/multiarray/shape.c +++ b/numpy/core/src/multiarray/shape.c @@ -342,7 +342,7 @@ PyArray_Newshape(PyArrayObject *self, PyArray_Dims *newdims, } } else { - for (i = ndim; i >= 0; --i) { + for (i = ndim-1; i >= 0; --i) { fa->maskna_strides[i] = stride; stride *= fa->dimensions[i]; } diff --git a/numpy/core/src/umath/ufunc_object.c b/numpy/core/src/umath/ufunc_object.c index f7d775ca8..500c72620 100644 --- a/numpy/core/src/umath/ufunc_object.c +++ b/numpy/core/src/umath/ufunc_object.c @@ -4047,7 +4047,9 @@ PyUFunc_GenericReduction(PyUFuncObject *self, PyObject *args, if (axis < 0) { axis += PyArray_NDIM(mp); } - if (axis < 0 || axis >= PyArray_NDIM(mp)) { + /* Special case letting axis=0 slip through for scalars */ + if ((axis < 0 || axis >= PyArray_NDIM(mp)) && + !(axis == 0 && PyArray_NDIM(mp) == 0)) { PyErr_SetString(PyExc_ValueError, "'axis' entry is out of bounds"); Py_XDECREF(otype); @@ -4060,8 +4062,14 @@ PyUFunc_GenericReduction(PyUFuncObject *self, PyObject *args, /* Check to see if input is zero-dimensional. */ if (PyArray_NDIM(mp) == 0) { - /* A reduction with no axes is still valid but trivial */ - if (operation == UFUNC_REDUCE && naxes == 0) { + /* + * A reduction with no axes is still valid but trivial. + * As a special case for backwards compatibility in 'sum', + * 'prod', et al, also allow a reduction where axis=0, even + * though this is technically incorrect. + */ + if (operation == UFUNC_REDUCE && + (naxes == 0 || (naxes == 1 && axes[0] == 0))) { Py_XDECREF(otype); /* If there's an output parameter, copy the value */ if (out != NULL) { diff --git a/numpy/core/tests/test_indexing.py b/numpy/core/tests/test_indexing.py new file mode 100644 index 000000000..da19d2a2a --- /dev/null +++ b/numpy/core/tests/test_indexing.py @@ -0,0 +1,25 @@ +import numpy as np +from numpy.compat import asbytes +from numpy.testing import * +import sys, warnings + +# The C implementation of fancy indexing is relatively complicated, +# and has many seeming inconsistencies. It also appears to lack any +# kind of test suite, making any changes to the underlying code difficult +# because of its fragility. + +# This file is to remedy the test suite part a little bit, +# but hopefully NumPy indexing can be changed to be more systematic +# at some point in the future. + +def test_boolean_indexing(): + # Indexing a 2-dimensional array with a length-1 array of 'True' + a = np.array([[ 0., 0., 0.]]) + b = np.array([ True], dtype=bool) + assert_equal(a[b], a) + + a[b] = 1. + assert_equal(a, [[1., 1., 1.]]) + +if __name__ == "__main__": + run_module_suite() diff --git a/numpy/core/tests/test_ufunc.py b/numpy/core/tests/test_ufunc.py index ad483634d..69f2b3c74 100644 --- a/numpy/core/tests/test_ufunc.py +++ b/numpy/core/tests/test_ufunc.py @@ -505,6 +505,16 @@ class TestUfunc(TestCase): assert_raises(ValueError, np.max, []) assert_raises(ValueError, np.min, []) + def test_scalar_reduction(self): + # The functions 'sum', 'prod', etc allow specifying axis=0 + # even for scalars + assert_equal(np.sum(3, axis=0), 3) + assert_equal(np.prod(3.5, axis=0), 3.5) + assert_equal(np.any(True, axis=0), True) + assert_equal(np.all(False, axis=0), False) + assert_equal(np.max(3, axis=0), 3) + assert_equal(np.min(2.5, axis=0), 2.5) + def test_casting_out_param(self): # Test that it's possible to do casts on output a = np.ones((200,100), np.int64) diff --git a/numpy/matrixlib/tests/test_defmatrix.py b/numpy/matrixlib/tests/test_defmatrix.py index 09a4b4892..0a181fca3 100644 --- a/numpy/matrixlib/tests/test_defmatrix.py +++ b/numpy/matrixlib/tests/test_defmatrix.py @@ -65,29 +65,45 @@ class TestProperties(TestCase): sumall = 30 assert_array_equal(sum0, M.sum(axis=0)) assert_array_equal(sum1, M.sum(axis=1)) - assert_(sumall == M.sum()) + assert_equal(sumall, M.sum()) + + assert_array_equal(sum0, np.sum(M, axis=0)) + assert_array_equal(sum1, np.sum(M, axis=1)) + assert_equal(sumall, np.sum(M)) def test_prod(self): x = matrix([[1,2,3],[4,5,6]]) - assert_(x.prod() == 720) - assert_(all(x.prod(0) == matrix([[4,10,18]]))) - assert_(all(x.prod(1) == matrix([[6],[120]]))) + assert_equal(x.prod(), 720) + assert_equal(x.prod(0), matrix([[4,10,18]])) + assert_equal(x.prod(1), matrix([[6],[120]])) + + assert_equal(np.prod(x), 720) + assert_equal(np.prod(x, axis=0), matrix([[4,10,18]])) + assert_equal(np.prod(x, axis=1), matrix([[6],[120]])) y = matrix([0,1,3]) assert_(y.prod() == 0) def test_max(self): x = matrix([[1,2,3],[4,5,6]]) - assert_(x.max() == 6) - assert_(all(x.max(0) == matrix([[4,5,6]]))) - assert_(all(x.max(1) == matrix([[3],[6]]))) + assert_equal(x.max(), 6) + assert_equal(x.max(0), matrix([[4,5,6]])) + assert_equal(x.max(1), matrix([[3],[6]])) + + assert_equal(np.max(x), 6) + assert_equal(np.max(x, axis=0), matrix([[4,5,6]])) + assert_equal(np.max(x, axis=1), matrix([[3],[6]])) def test_min(self): x = matrix([[1,2,3],[4,5,6]]) - assert_(x.min() == 1) - assert_(all(x.min(0) == matrix([[1,2,3]]))) - assert_(all(x.min(1) == matrix([[1],[4]]))) + assert_equal(x.min(), 1) + assert_equal(x.min(0), matrix([[1,2,3]])) + assert_equal(x.min(1), matrix([[1],[4]])) + + assert_equal(np.min(x), 1) + assert_equal(np.min(x, axis=0), matrix([[1,2,3]])) + assert_equal(np.min(x, axis=1), matrix([[1],[4]])) def test_ptp(self): x = np.arange(4).reshape((2,2)) |