diff options
Diffstat (limited to 'numpy')
37 files changed, 647 insertions, 332 deletions
diff --git a/numpy/add_newdocs.py b/numpy/add_newdocs.py index 09f4e40c4..3916d1304 100644 --- a/numpy/add_newdocs.py +++ b/numpy/add_newdocs.py @@ -6728,6 +6728,51 @@ add_newdoc('numpy.core.multiarray', 'busday_count', 53 """) +add_newdoc('numpy.core.multiarray', 'normalize_axis_index', + """ + normalize_axis_index(axis, ndim) + + Normalizes an axis index, `axis`, such that is a valid positive index into + the shape of array with `ndim` dimensions. Raises an AxisError with an + appropriate message if this is not possible. + + Used internally by all axis-checking logic. + + .. versionadded:: 1.13.0 + + Parameters + ---------- + axis : int + The un-normalized index of the axis. Can be negative + ndim : int + The number of dimensions of the array that `axis` should be normalized + against + + Returns + ------- + normalized_axis : int + The normalized axis index, such that `0 <= normalized_axis < ndim` + + Raises + ------ + AxisError + If the axis index is invalid, when `-ndim <= axis < ndim` is false. + + Examples + -------- + >>> normalize_axis_index(0, ndim=3) + 0 + >>> normalize_axis_index(1, ndim=3) + 1 + >>> normalize_axis_index(-1, ndim=3) + 2 + + >>> normalize_axis_index(3, ndim=3) + Traceback (most recent call last): + ... + AxisError: axis 3 is out of bounds for array of dimension 3 + """) + ############################################################################## # # nd_grid instances diff --git a/numpy/core/_internal.py b/numpy/core/_internal.py index 741c8bb5f..d73cdcc55 100644 --- a/numpy/core/_internal.py +++ b/numpy/core/_internal.py @@ -630,3 +630,6 @@ def _gcd(a, b): # Exception used in shares_memory() class TooHardError(RuntimeError): pass + +class AxisError(ValueError, IndexError): + pass diff --git a/numpy/core/numeric.py b/numpy/core/numeric.py index 97d19f008..066697f3e 100644 --- a/numpy/core/numeric.py +++ b/numpy/core/numeric.py @@ -17,7 +17,7 @@ from .multiarray import ( inner, int_asbuffer, lexsort, matmul, may_share_memory, min_scalar_type, ndarray, nditer, nested_iters, promote_types, putmask, result_type, set_numeric_ops, shares_memory, vdot, where, - zeros) + zeros, normalize_axis_index) if sys.version_info[0] < 3: from .multiarray import newbuffer, getbuffer @@ -27,7 +27,7 @@ from .umath import (invert, sin, UFUNC_BUFSIZE_DEFAULT, ERR_IGNORE, ERR_DEFAULT, PINF, NAN) from . import numerictypes from .numerictypes import longlong, intc, int_, float_, complex_, bool_ -from ._internal import TooHardError +from ._internal import TooHardError, AxisError bitwise_not = invert ufunc = type(sin) @@ -65,7 +65,7 @@ __all__ = [ 'True_', 'bitwise_not', 'CLIP', 'RAISE', 'WRAP', 'MAXDIMS', 'BUFSIZE', 'ALLOW_THREADS', 'ComplexWarning', 'full', 'full_like', 'matmul', 'shares_memory', 'may_share_memory', 'MAY_SHARE_BOUNDS', 'MAY_SHARE_EXACT', - 'TooHardError', + 'TooHardError', 'AxisError' ] @@ -1527,15 +1527,12 @@ def rollaxis(a, axis, start=0): """ n = a.ndim - if axis < 0: - axis += n + axis = normalize_axis_index(axis, n) if start < 0: start += n msg = "'%s' arg requires %d <= %s < %d, but %d was passed in" - if not (0 <= axis < n): - raise ValueError(msg % ('axis', -n, 'axis', n, axis)) if not (0 <= start < n + 1): - raise ValueError(msg % ('start', -n, 'start', n + 1, start)) + raise AxisError(msg % ('start', -n, 'start', n + 1, start)) if axis < start: # it's been removed start -= 1 @@ -1554,7 +1551,7 @@ def _validate_axis(axis, ndim, argname): axis = list(axis) axis = [a + ndim if a < 0 else a for a in axis] if not builtins.all(0 <= a < ndim for a in axis): - raise ValueError('invalid axis for this array in `%s` argument' % + raise AxisError('invalid axis for this array in `%s` argument' % argname) if len(set(axis)) != len(axis): raise ValueError('repeated axis in `%s` argument' % argname) diff --git a/numpy/core/shape_base.py b/numpy/core/shape_base.py index 70afdb746..58b0dcaac 100644 --- a/numpy/core/shape_base.py +++ b/numpy/core/shape_base.py @@ -5,6 +5,7 @@ __all__ = ['atleast_1d', 'atleast_2d', 'atleast_3d', 'vstack', 'hstack', from . import numeric as _nx from .numeric import asanyarray, newaxis +from .multiarray import normalize_axis_index def atleast_1d(*arys): """ @@ -347,11 +348,7 @@ def stack(arrays, axis=0): raise ValueError('all input arrays must have the same shape') result_ndim = arrays[0].ndim + 1 - if not -result_ndim <= axis < result_ndim: - msg = 'axis {0} out of bounds [-{1}, {1})'.format(axis, result_ndim) - raise IndexError(msg) - if axis < 0: - axis += result_ndim + axis = normalize_axis_index(axis, result_ndim) sl = (slice(None),) * axis + (_nx.newaxis,) expanded_arrays = [arr[sl] for arr in arrays] diff --git a/numpy/core/src/multiarray/common.h b/numpy/core/src/multiarray/common.h index 5e14b80a7..625ca9d76 100644 --- a/numpy/core/src/multiarray/common.h +++ b/numpy/core/src/multiarray/common.h @@ -134,6 +134,42 @@ check_and_adjust_index(npy_intp *index, npy_intp max_item, int axis, return 0; } +/* + * Returns -1 and sets an exception if *axis is an invalid axis for + * an array of dimension ndim, otherwise adjusts it in place to be + * 0 <= *axis < ndim, and returns 0. + */ +static NPY_INLINE int +check_and_adjust_axis(int *axis, int ndim) +{ + /* Check that index is valid, taking into account negative indices */ + if (NPY_UNLIKELY((*axis < -ndim) || (*axis >= ndim))) { + /* + * Load the exception type, if we don't already have it. Unfortunately + * we don't have access to npy_cache_import here + */ + static PyObject *AxisError_cls = NULL; + if (AxisError_cls == NULL) { + PyObject *mod = PyImport_ImportModule("numpy.core._internal"); + + if (mod != NULL) { + AxisError_cls = PyObject_GetAttrString(mod, "AxisError"); + Py_DECREF(mod); + } + } + + PyErr_Format(AxisError_cls, + "axis %d is out of bounds for array of dimension %d", + *axis, ndim); + return -1; + } + /* adjust negative indices */ + if (*axis < 0) { + *axis += ndim; + } + return 0; +} + /* * return true if pointer is aligned to 'alignment' diff --git a/numpy/core/src/multiarray/conversion_utils.c b/numpy/core/src/multiarray/conversion_utils.c index c016bb8d1..8ed08a366 100644 --- a/numpy/core/src/multiarray/conversion_utils.c +++ b/numpy/core/src/multiarray/conversion_utils.c @@ -259,17 +259,10 @@ PyArray_ConvertMultiAxis(PyObject *axis_in, int ndim, npy_bool *out_axis_flags) PyObject *tmp = PyTuple_GET_ITEM(axis_in, i); int axis = PyArray_PyIntAsInt_ErrMsg(tmp, "integers are required for the axis tuple elements"); - int axis_orig = axis; if (error_converting(axis)) { return NPY_FAIL; } - if (axis < 0) { - axis += ndim; - } - if (axis < 0 || axis >= ndim) { - PyErr_Format(PyExc_ValueError, - "'axis' entry %d is out of bounds [-%d, %d)", - axis_orig, ndim, ndim); + if (check_and_adjust_axis(&axis, ndim) < 0) { return NPY_FAIL; } if (out_axis_flags[axis]) { @@ -284,20 +277,16 @@ PyArray_ConvertMultiAxis(PyObject *axis_in, int ndim, npy_bool *out_axis_flags) } /* Try to interpret axis as an integer */ else { - int axis, axis_orig; + int axis; memset(out_axis_flags, 0, ndim); axis = PyArray_PyIntAsInt_ErrMsg(axis_in, "an integer is required for the axis"); - axis_orig = axis; if (error_converting(axis)) { return NPY_FAIL; } - if (axis < 0) { - axis += ndim; - } /* * Special case letting axis={-1,0} slip through for scalars, * for backwards compatibility reasons. @@ -306,10 +295,7 @@ PyArray_ConvertMultiAxis(PyObject *axis_in, int ndim, npy_bool *out_axis_flags) return NPY_SUCCEED; } - if (axis < 0 || axis >= ndim) { - PyErr_Format(PyExc_ValueError, - "'axis' entry %d is out of bounds [-%d, %d)", - axis_orig, ndim, ndim); + if (check_and_adjust_axis(&axis, ndim) < 0) { return NPY_FAIL; } diff --git a/numpy/core/src/multiarray/ctors.c b/numpy/core/src/multiarray/ctors.c index 349b59c5f..ee6b66eef 100644 --- a/numpy/core/src/multiarray/ctors.c +++ b/numpy/core/src/multiarray/ctors.c @@ -2793,7 +2793,6 @@ PyArray_CheckAxis(PyArrayObject *arr, int *axis, int flags) { PyObject *temp1, *temp2; int n = PyArray_NDIM(arr); - int axis_orig = *axis; if (*axis == NPY_MAXDIMS || n == 0) { if (n != 1) { @@ -2831,12 +2830,7 @@ PyArray_CheckAxis(PyArrayObject *arr, int *axis, int flags) temp2 = (PyObject *)temp1; } n = PyArray_NDIM((PyArrayObject *)temp2); - if (*axis < 0) { - *axis += n; - } - if ((*axis < 0) || (*axis >= n)) { - PyErr_Format(PyExc_ValueError, - "axis(=%d) out of bounds", axis_orig); + if (check_and_adjust_axis(axis, n) < 0) { Py_DECREF(temp2); return NULL; } diff --git a/numpy/core/src/multiarray/item_selection.c b/numpy/core/src/multiarray/item_selection.c index 08b9c5965..3c0f0782e 100644 --- a/numpy/core/src/multiarray/item_selection.c +++ b/numpy/core/src/multiarray/item_selection.c @@ -1101,16 +1101,12 @@ NPY_NO_EXPORT int PyArray_Sort(PyArrayObject *op, int axis, NPY_SORTKIND which) { PyArray_SortFunc *sort; - int axis_orig = axis; - int n = PyArray_NDIM(op); + int n = PyArray_NDIM(op); - if (axis < 0) { - axis += n; - } - if (axis < 0 || axis >= n) { - PyErr_Format(PyExc_ValueError, "axis(=%d) out of bounds", axis_orig); + if (check_and_adjust_axis(&axis, n) < 0) { return -1; } + if (PyArray_FailUnlessWriteable(op, "sort array") < 0) { return -1; } @@ -1212,17 +1208,13 @@ PyArray_Partition(PyArrayObject *op, PyArrayObject * ktharray, int axis, PyArrayObject *kthrvl; PyArray_PartitionFunc *part; PyArray_SortFunc *sort; - int axis_orig = axis; int n = PyArray_NDIM(op); int ret; - if (axis < 0) { - axis += n; - } - if (axis < 0 || axis >= n) { - PyErr_Format(PyExc_ValueError, "axis(=%d) out of bounds", axis_orig); + if (check_and_adjust_axis(&axis, n) < 0) { return -1; } + if (PyArray_FailUnlessWriteable(op, "partition array") < 0) { return -1; } @@ -1455,12 +1447,7 @@ PyArray_LexSort(PyObject *sort_keys, int axis) *((npy_intp *)(PyArray_DATA(ret))) = 0; goto finish; } - if (axis < 0) { - axis += nd; - } - if ((axis < 0) || (axis >= nd)) { - PyErr_Format(PyExc_ValueError, - "axis(=%d) out of bounds", axis); + if (check_and_adjust_axis(&axis, nd) < 0) { goto fail; } diff --git a/numpy/core/src/multiarray/multiarraymodule.c b/numpy/core/src/multiarray/multiarraymodule.c index f00de46c4..1c8d9b5e4 100644 --- a/numpy/core/src/multiarray/multiarraymodule.c +++ b/numpy/core/src/multiarray/multiarraymodule.c @@ -327,7 +327,6 @@ PyArray_ConcatenateArrays(int narrays, PyArrayObject **arrays, int axis) PyArray_Descr *dtype = NULL; PyArrayObject *ret = NULL; PyArrayObject_fields *sliding_view = NULL; - int orig_axis = axis; if (narrays <= 0) { PyErr_SetString(PyExc_ValueError, @@ -345,13 +344,7 @@ PyArray_ConcatenateArrays(int narrays, PyArrayObject **arrays, int axis) } /* Handle standard Python negative indexing */ - if (axis < 0) { - axis += ndim; - } - - if (axis < 0 || axis >= ndim) { - PyErr_Format(PyExc_IndexError, - "axis %d out of bounds [0, %d)", orig_axis, ndim); + if (check_and_adjust_axis(&axis, ndim) < 0) { return NULL; } @@ -4109,6 +4102,24 @@ array_may_share_memory(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject * return array_shares_memory_impl(args, kwds, NPY_MAY_SHARE_BOUNDS, 0); } +static PyObject * +normalize_axis_index(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"axis", "ndim", NULL}; + int axis; + int ndim; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ii", kwlist, + &axis, &ndim)) { + return NULL; + } + + if(check_and_adjust_axis(&axis, ndim) < 0) { + return NULL; + } + + return PyInt_FromLong(axis); +} static struct PyMethodDef array_module_methods[] = { {"_get_ndarray_c_version", @@ -4284,6 +4295,8 @@ static struct PyMethodDef array_module_methods[] = { METH_VARARGS | METH_KEYWORDS, NULL}, {"unpackbits", (PyCFunction)io_unpack, METH_VARARGS | METH_KEYWORDS, NULL}, + {"normalize_axis_index", (PyCFunction)normalize_axis_index, + METH_VARARGS | METH_KEYWORDS, NULL}, {NULL, NULL, 0, NULL} /* sentinel */ }; diff --git a/numpy/core/src/multiarray/shape.c b/numpy/core/src/multiarray/shape.c index 3bee562be..5207513bf 100644 --- a/numpy/core/src/multiarray/shape.c +++ b/numpy/core/src/multiarray/shape.c @@ -705,12 +705,7 @@ PyArray_Transpose(PyArrayObject *ap, PyArray_Dims *permute) } for (i = 0; i < n; i++) { axis = axes[i]; - if (axis < 0) { - axis = PyArray_NDIM(ap) + axis; - } - if (axis < 0 || axis >= PyArray_NDIM(ap)) { - PyErr_SetString(PyExc_ValueError, - "invalid axis for this array"); + if (check_and_adjust_axis(&axis, PyArray_NDIM(ap)) < 0) { return NULL; } if (reverse_permutation[axis] != -1) { diff --git a/numpy/core/src/umath/ufunc_object.c b/numpy/core/src/umath/ufunc_object.c index 0bae2d591..af4ce12db 100644 --- a/numpy/core/src/umath/ufunc_object.c +++ b/numpy/core/src/umath/ufunc_object.c @@ -4036,12 +4036,7 @@ PyUFunc_GenericReduction(PyUFuncObject *ufunc, PyObject *args, Py_DECREF(mp); return NULL; } - if (axis < 0) { - axis += ndim; - } - if (axis < 0 || axis >= ndim) { - PyErr_SetString(PyExc_ValueError, - "'axis' entry is out of bounds"); + if (check_and_adjust_axis(&axis, ndim) < 0) { Py_XDECREF(otype); Py_DECREF(mp); return NULL; @@ -4058,18 +4053,11 @@ PyUFunc_GenericReduction(PyUFuncObject *ufunc, PyObject *args, Py_DECREF(mp); return NULL; } - if (axis < 0) { - axis += ndim; - } /* Special case letting axis={0 or -1} slip through for scalars */ if (ndim == 0 && (axis == 0 || axis == -1)) { axis = 0; } - else if (axis < 0 || axis >= ndim) { - PyErr_SetString(PyExc_ValueError, - "'axis' entry is out of bounds"); - Py_XDECREF(otype); - Py_DECREF(mp); + else if (check_and_adjust_axis(&axis, ndim) < 0) { return NULL; } axes[0] = (int)axis; diff --git a/numpy/core/tests/test_function_base.py b/numpy/core/tests/test_function_base.py index 1b563dde2..94c55bdd1 100644 --- a/numpy/core/tests/test_function_base.py +++ b/numpy/core/tests/test_function_base.py @@ -1,7 +1,7 @@ from __future__ import division, absolute_import, print_function -from numpy import (logspace, linspace, geomspace, dtype, array, finfo, - typecodes, arange, isnan, ndarray, sqrt) +from numpy import (logspace, linspace, geomspace, dtype, array, sctypes, + arange, isnan, ndarray, sqrt, nextafter) from numpy.testing import ( TestCase, run_module_suite, assert_, assert_equal, assert_raises, assert_array_equal, assert_allclose, suppress_warnings @@ -301,9 +301,9 @@ class TestLinspace(TestCase): def test_denormal_numbers(self): # Regression test for gh-5437. Will probably fail when compiled # with ICC, which flushes denormals to zero - for dt in (dtype(f) for f in typecodes['Float']): - stop = finfo(dt).tiny * finfo(dt).resolution - assert_(any(linspace(0, stop, 10, endpoint=False, dtype=dt))) + for ftype in sctypes['float']: + stop = nextafter(ftype(0), ftype(1)) * 5 # A denormal number + assert_(any(linspace(0, stop, 10, endpoint=False, dtype=ftype))) def test_equivalent_to_arange(self): for j in range(1000): diff --git a/numpy/core/tests/test_memmap.py b/numpy/core/tests/test_memmap.py index f643dacc1..c0c352230 100644 --- a/numpy/core/tests/test_memmap.py +++ b/numpy/core/tests/test_memmap.py @@ -41,6 +41,7 @@ class TestMemmap(TestCase): shape=self.shape) assert_(allclose(self.data, newfp)) assert_array_equal(self.data, newfp) + self.assertEqual(newfp.flags.writeable, False) def test_open_with_filename(self): tmpname = mktemp('', 'mmap', dir=self.tempdir) diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py index 8229f1e1a..fa5051ba7 100644 --- a/numpy/core/tests/test_multiarray.py +++ b/numpy/core/tests/test_multiarray.py @@ -2013,13 +2013,13 @@ class TestMethods(TestCase): d = np.array([2, 1]) d.partition(0, kind=k) assert_raises(ValueError, d.partition, 2) - assert_raises(ValueError, d.partition, 3, axis=1) + assert_raises(np.AxisError, d.partition, 3, axis=1) assert_raises(ValueError, np.partition, d, 2) - assert_raises(ValueError, np.partition, d, 2, axis=1) + assert_raises(np.AxisError, np.partition, d, 2, axis=1) assert_raises(ValueError, d.argpartition, 2) - assert_raises(ValueError, d.argpartition, 3, axis=1) + assert_raises(np.AxisError, d.argpartition, 3, axis=1) assert_raises(ValueError, np.argpartition, d, 2) - assert_raises(ValueError, np.argpartition, d, 2, axis=1) + assert_raises(np.AxisError, np.argpartition, d, 2, axis=1) d = np.arange(10).reshape((2, 5)) d.partition(1, axis=0, kind=k) d.partition(4, axis=1, kind=k) @@ -3522,8 +3522,8 @@ class TestArgmin(TestCase): class TestMinMax(TestCase): def test_scalar(self): - assert_raises(ValueError, np.amax, 1, 1) - assert_raises(ValueError, np.amin, 1, 1) + assert_raises(np.AxisError, np.amax, 1, 1) + assert_raises(np.AxisError, np.amin, 1, 1) assert_equal(np.amax(1, axis=0), 1) assert_equal(np.amin(1, axis=0), 1) @@ -3531,7 +3531,7 @@ class TestMinMax(TestCase): assert_equal(np.amin(1, axis=None), 1) def test_axis(self): - assert_raises(ValueError, np.amax, [1, 2, 3], 1000) + assert_raises(np.AxisError, np.amax, [1, 2, 3], 1000) assert_equal(np.amax([[1, 2, 3]], axis=1), 3) def test_datetime(self): @@ -3793,7 +3793,7 @@ class TestLexsort(TestCase): def test_invalid_axis(self): # gh-7528 x = np.linspace(0., 1., 42*3).reshape(42, 3) - assert_raises(ValueError, np.lexsort, x, axis=2) + assert_raises(np.AxisError, np.lexsort, x, axis=2) class TestIO(object): """Test tofile, fromfile, tobytes, and fromstring""" diff --git a/numpy/core/tests/test_numeric.py b/numpy/core/tests/test_numeric.py index 4aa6bed33..906280e15 100644 --- a/numpy/core/tests/test_numeric.py +++ b/numpy/core/tests/test_numeric.py @@ -1010,7 +1010,7 @@ class TestNonzero(TestCase): assert_raises(ValueError, np.count_nonzero, m, axis=(1, 1)) assert_raises(TypeError, np.count_nonzero, m, axis='foo') - assert_raises(ValueError, np.count_nonzero, m, axis=3) + assert_raises(np.AxisError, np.count_nonzero, m, axis=3) assert_raises(TypeError, np.count_nonzero, m, axis=np.array([[1], [2]])) @@ -2323,10 +2323,10 @@ class TestRollaxis(TestCase): def test_exceptions(self): a = np.arange(1*2*3*4).reshape(1, 2, 3, 4) - assert_raises(ValueError, np.rollaxis, a, -5, 0) - assert_raises(ValueError, np.rollaxis, a, 0, -5) - assert_raises(ValueError, np.rollaxis, a, 4, 0) - assert_raises(ValueError, np.rollaxis, a, 0, 5) + assert_raises(np.AxisError, np.rollaxis, a, -5, 0) + assert_raises(np.AxisError, np.rollaxis, a, 0, -5) + assert_raises(np.AxisError, np.rollaxis, a, 4, 0) + assert_raises(np.AxisError, np.rollaxis, a, 0, 5) def test_results(self): a = np.arange(1*2*3*4).reshape(1, 2, 3, 4).copy() @@ -2413,11 +2413,11 @@ class TestMoveaxis(TestCase): def test_errors(self): x = np.random.randn(1, 2, 3) - assert_raises_regex(ValueError, 'invalid axis .* `source`', + assert_raises_regex(np.AxisError, 'invalid axis .* `source`', np.moveaxis, x, 3, 0) - assert_raises_regex(ValueError, 'invalid axis .* `source`', + assert_raises_regex(np.AxisError, 'invalid axis .* `source`', np.moveaxis, x, -4, 0) - assert_raises_regex(ValueError, 'invalid axis .* `destination`', + assert_raises_regex(np.AxisError, 'invalid axis .* `destination`', np.moveaxis, x, 0, 5) assert_raises_regex(ValueError, 'repeated axis in `source`', np.moveaxis, x, [0, 0], [0, 1]) diff --git a/numpy/core/tests/test_shape_base.py b/numpy/core/tests/test_shape_base.py index ac8dc1eea..727608a17 100644 --- a/numpy/core/tests/test_shape_base.py +++ b/numpy/core/tests/test_shape_base.py @@ -184,8 +184,8 @@ class TestConcatenate(TestCase): for ndim in [1, 2, 3]: a = np.ones((1,)*ndim) np.concatenate((a, a), axis=0) # OK - assert_raises(IndexError, np.concatenate, (a, a), axis=ndim) - assert_raises(IndexError, np.concatenate, (a, a), axis=-(ndim + 1)) + assert_raises(np.AxisError, np.concatenate, (a, a), axis=ndim) + assert_raises(np.AxisError, np.concatenate, (a, a), axis=-(ndim + 1)) # Scalars cannot be concatenated assert_raises(ValueError, concatenate, (0,)) @@ -294,8 +294,8 @@ def test_stack(): expected_shapes = [(10, 3), (3, 10), (3, 10), (10, 3)] for axis, expected_shape in zip(axes, expected_shapes): assert_equal(np.stack(arrays, axis).shape, expected_shape) - assert_raises_regex(IndexError, 'out of bounds', stack, arrays, axis=2) - assert_raises_regex(IndexError, 'out of bounds', stack, arrays, axis=-3) + assert_raises_regex(np.AxisError, 'out of bounds', stack, arrays, axis=2) + assert_raises_regex(np.AxisError, 'out of bounds', stack, arrays, axis=-3) # all shapes for 2d input arrays = [np.random.randn(3, 4) for _ in range(10)] axes = [0, 1, 2, -1, -2, -3] diff --git a/numpy/core/tests/test_ufunc.py b/numpy/core/tests/test_ufunc.py index 3fea68700..f7b66f90c 100644 --- a/numpy/core/tests/test_ufunc.py +++ b/numpy/core/tests/test_ufunc.py @@ -703,14 +703,14 @@ class TestUfunc(TestCase): def test_axis_out_of_bounds(self): a = np.array([False, False]) - assert_raises(ValueError, a.all, axis=1) + assert_raises(np.AxisError, a.all, axis=1) a = np.array([False, False]) - assert_raises(ValueError, a.all, axis=-2) + assert_raises(np.AxisError, a.all, axis=-2) a = np.array([False, False]) - assert_raises(ValueError, a.any, axis=1) + assert_raises(np.AxisError, a.any, axis=1) a = np.array([False, False]) - assert_raises(ValueError, a.any, axis=-2) + assert_raises(np.AxisError, a.any, axis=-2) def test_scalar_reduction(self): # The functions 'sum', 'prod', etc allow specifying axis=0 diff --git a/numpy/core/tests/test_umath.py b/numpy/core/tests/test_umath.py index d83b7dccd..daca67150 100644 --- a/numpy/core/tests/test_umath.py +++ b/numpy/core/tests/test_umath.py @@ -2126,6 +2126,12 @@ def test_nextafterf(): def test_nextafterl(): return _test_nextafter(np.longdouble) +def test_nextafter_0(): + for t, direction in itertools.product(np.sctypes['float'], (1, -1)): + tiny = np.finfo(t).tiny + assert_(0. < direction * np.nextafter(t(0), t(direction)) < tiny) + assert_equal(np.nextafter(t(0), t(direction)) / t(2.1), direction * 0.0) + def _test_spacing(t): one = t(1) eps = np.finfo(t).eps diff --git a/numpy/f2py/crackfortran.py b/numpy/f2py/crackfortran.py index e38e8e3fe..f738a9f00 100755 --- a/numpy/f2py/crackfortran.py +++ b/numpy/f2py/crackfortran.py @@ -2392,7 +2392,8 @@ def _selected_real_kind_func(p, r=0, radix=0): return 4 if p < 16: return 8 - if platform.machine().lower().startswith('power'): + machine = platform.machine().lower() + if machine.startswith('power') or machine.startswith('ppc64'): if p <= 20: return 16 else: diff --git a/numpy/lib/function_base.py b/numpy/lib/function_base.py index ae1420b72..fc49a6fd7 100644 --- a/numpy/lib/function_base.py +++ b/numpy/lib/function_base.py @@ -25,7 +25,7 @@ from numpy.core.numerictypes import typecodes, number from numpy.lib.twodim_base import diag from .utils import deprecate from numpy.core.multiarray import ( - _insert, add_docstring, digitize, bincount, + _insert, add_docstring, digitize, bincount, normalize_axis_index, interp as compiled_interp, interp_complex as compiled_interp_complex ) from numpy.core.umath import _add_newdoc_ufunc as add_newdoc_ufunc @@ -128,7 +128,8 @@ def rot90(m, k=1, axes=(0,1)): return flip(flip(m, axes[0]), axes[1]) axes_list = arange(0, m.ndim) - axes_list[axes[0]], axes_list[axes[1]] = axes_list[axes[1]], axes_list[axes[0]] + (axes_list[axes[0]], axes_list[axes[1]]) = (axes_list[axes[1]], + axes_list[axes[0]]) if k == 1: return transpose(flip(m,axes[1]), axes_list) @@ -332,7 +333,7 @@ def _hist_bin_doane(x): Improved version of Sturges' formula which works better for non-normal data. See - http://stats.stackexchange.com/questions/55134/doanes-formula-for-histogram-binning + stats.stackexchange.com/questions/55134/doanes-formula-for-histogram-binning Parameters ---------- @@ -638,7 +639,7 @@ def histogram(a, bins=10, range=None, normed=False, weights=None, >>> rng = np.random.RandomState(10) # deterministic random data >>> a = np.hstack((rng.normal(size=1000), ... rng.normal(loc=5, scale=2, size=1000))) - >>> plt.hist(a, bins='auto') # plt.hist passes its arguments to np.histogram + >>> plt.hist(a, bins='auto') # arguments are passed to np.histogram >>> plt.title("Histogram with 'auto' bins") >>> plt.show() @@ -764,15 +765,19 @@ def histogram(a, bins=10, range=None, normed=False, weights=None, decrement = tmp_a_data < bin_edges[indices] indices[decrement] -= 1 # The last bin includes the right edge. The other bins do not. - increment = (tmp_a_data >= bin_edges[indices + 1]) & (indices != bins - 1) + increment = ((tmp_a_data >= bin_edges[indices + 1]) + & (indices != bins - 1)) indices[increment] += 1 # We now compute the histogram using bincount if ntype.kind == 'c': - n.real += np.bincount(indices, weights=tmp_w.real, minlength=bins) - n.imag += np.bincount(indices, weights=tmp_w.imag, minlength=bins) + n.real += np.bincount(indices, weights=tmp_w.real, + minlength=bins) + n.imag += np.bincount(indices, weights=tmp_w.imag, + minlength=bins) else: - n += np.bincount(indices, weights=tmp_w, minlength=bins).astype(ntype) + n += np.bincount(indices, weights=tmp_w, + minlength=bins).astype(ntype) # Rename the bin edges for return. bins = bin_edges @@ -1499,19 +1504,29 @@ def gradient(f, *varargs, **kwargs): Return the gradient of an N-dimensional array. The gradient is computed using second order accurate central differences - in the interior and either first differences or second order accurate - one-sides (forward or backwards) differences at the boundaries. The - returned gradient hence has the same shape as the input array. + in the interior points and either first or second order accurate one-sides + (forward or backwards) differences at the boundaries. + The returned gradient hence has the same shape as the input array. Parameters ---------- f : array_like An N-dimensional array containing samples of a scalar function. - varargs : scalar or list of scalar, optional - N scalars specifying the sample distances for each dimension, - i.e. `dx`, `dy`, `dz`, ... Default distance: 1. - single scalar specifies sample distance for all dimensions. - if `axis` is given, the number of varargs must equal the number of axes. + varargs : list of scalar or array, optional + Spacing between f values. Default unitary spacing for all dimensions. + Spacing can be specified using: + + 1. single scalar to specify a sample distance for all dimensions. + 2. N scalars to specify a constant sample distance for each dimension. + i.e. `dx`, `dy`, `dz`, ... + 3. N arrays to specify the coordinates of the values along each + dimension of F. The length of the array must match the size of + the corresponding dimension + 4. Any combination of N scalars/arrays with the meaning of 2. and 3. + + If `axis` is given, the number of varargs must equal the number of axes. + Default: 1. + edge_order : {1, 2}, optional Gradient is calculated using N-th order accurate differences at the boundaries. Default: 1. @@ -1520,8 +1535,9 @@ def gradient(f, *varargs, **kwargs): axis : None or int or tuple of ints, optional Gradient is calculated only along the given axis or axes - The default (axis = None) is to calculate the gradient for all the axes of the input array. - axis may be negative, in which case it counts from the last to the first axis. + The default (axis = None) is to calculate the gradient for all the axes + of the input array. axis may be negative, in which case it counts from + the last to the first axis. .. versionadded:: 1.11.0 @@ -1529,17 +1545,31 @@ def gradient(f, *varargs, **kwargs): ------- gradient : ndarray or list of ndarray A set of ndarrays (or a single ndarray if there is only one dimension) - correposnding to the derivatives of f with respect to each dimension. + corresponding to the derivatives of f with respect to each dimension. Each derivative has the same shape as f. - + Examples -------- - >>> x = np.array([1, 2, 4, 7, 11, 16], dtype=np.float) - >>> np.gradient(x) + >>> f = np.array([1, 2, 4, 7, 11, 16], dtype=np.float) + >>> np.gradient(f) array([ 1. , 1.5, 2.5, 3.5, 4.5, 5. ]) - >>> np.gradient(x, 2) + >>> np.gradient(f, 2) array([ 0.5 , 0.75, 1.25, 1.75, 2.25, 2.5 ]) + + Spacing can be also specified with an array that represents the coordinates + of the values F along the dimensions. + For instance a uniform spacing: + + >>> x = np.arange(f.size) + >>> np.gradient(f, x) + array([ 1. , 1.5, 2.5, 3.5, 4.5, 5. ]) + Or a non uniform one: + + >>> x = np.array([0., 1., 1.5, 3.5, 4., 6.], dtype=np.float) + >>> np.gradient(f, x) + array([ 1. , 3. , 3.5, 6.7, 6.9, 2.5]) + For two dimensional arrays, the return will be two arrays ordered by axis. In this example the first array stands for the gradient in rows and the second one in columns direction: @@ -1549,15 +1579,99 @@ def gradient(f, *varargs, **kwargs): [ 2., 2., -1.]]), array([[ 1. , 2.5, 4. ], [ 1. , 1. , 1. ]])] + In this example the spacing is also specified: + uniform for axis=0 and non uniform for axis=1 + + >>> dx = 2. + >>> y = [1., 1.5, 3.5] + >>> np.gradient(np.array([[1, 2, 6], [3, 4, 5]], dtype=np.float), dx, y) + [array([[ 1. , 1. , -0.5], + [ 1. , 1. , -0.5]]), array([[ 2. , 2. , 2. ], + [ 2. , 1.7, 0.5]])] + + It is possible to specify how boundaries are treated using `edge_order` + >>> x = np.array([0, 1, 2, 3, 4]) - >>> y = x**2 - >>> np.gradient(y, edge_order=2) + >>> f = x**2 + >>> np.gradient(f, edge_order=1) + array([ 1., 2., 4., 6., 7.]) + >>> np.gradient(f, edge_order=2) array([-0., 2., 4., 6., 8.]) - The axis keyword can be used to specify a subset of axes of which the gradient is calculated + The `axis` keyword can be used to specify a subset of axes of which the + gradient is calculated + >>> np.gradient(np.array([[1, 2, 6], [3, 4, 5]], dtype=np.float), axis=0) array([[ 2., 2., -1.], [ 2., 2., -1.]]) + + Notes + ----- + Assuming that :math:`f\\in C^{3}` (i.e., :math:`f` has at least 3 continous + derivatives) and let be :math:`h_{*}` a non homogeneous stepsize, the + spacing the finite difference coefficients are computed by minimising + the consistency error :math:`\\eta_{i}`: + + .. math:: + + \\eta_{i} = f_{i}^{\\left(1\\right)} - + \\left[ \\alpha f\\left(x_{i}\\right) + + \\beta f\\left(x_{i} + h_{d}\\right) + + \\gamma f\\left(x_{i}-h_{s}\\right) + \\right] + + By substituting :math:`f(x_{i} + h_{d})` and :math:`f(x_{i} - h_{s})` + with their Taylor series expansion, this translates into solving + the following the linear system: + + .. math:: + + \\left\\{ + \\begin{array}{r} + \\alpha+\\beta+\\gamma=0 \\\\ + -\\beta h_{d}+\\gamma h_{s}=1 \\\\ + \\beta h_{d}^{2}+\\gamma h_{s}^{2}=0 + \\end{array} + \\right. + + The resulting approximation of :math:`f_{i}^{(1)}` is the following: + + .. math:: + + \\hat f_{i}^{(1)} = + \\frac{ + h_{s}^{2}f\\left(x_{i} + h_{d}\\right) + + \\left(h_{d}^{2} - h_{s}^{2}\\right)f\\left(x_{i}\\right) + - h_{d}^{2}f\\left(x_{i}-h_{s}\\right)} + { h_{s}h_{d}\\left(h_{d} + h_{s}\\right)} + + \mathcal{O}\\left(\\frac{h_{d}h_{s}^{2} + + h_{s}h_{d}^{2}}{h_{d} + + h_{s}}\\right) + + It is worth noting that if :math:`h_{s}=h_{d}` + (i.e., data are evenly spaced) + we find the standard second order approximation: + + .. math:: + + \\hat f_{i}^{(1)}= + \\frac{f\\left(x_{i+1}\\right) - f\\left(x_{i-1}\\right)}{2h} + + \mathcal{O}\\left(h^{2}\\right) + + With a similar procedure the forward/backward approximations used for + boundaries can be derived. + + References + ---------- + .. [1] Quarteroni A., Sacco R., Saleri F. (2007) Numerical Mathematics + (Texts in Applied Mathematics). New York: Springer. + .. [2] Durran D. R. (1999) Numerical Methods for Wave Equations + in Geophysical Fluid Dynamics. New York: Springer. + .. [3] Fornberg B. (1988) Generation of Finite Difference Formulas on + Arbitrarily Spaced Grids, + Mathematics of Computation 51, no. 184 : 699-706. + `PDF <http://www.ams.org/journals/mcom/1988-51-184/ + S0025-5718-1988-0935077-0/S0025-5718-1988-0935077-0.pdf>`_. """ f = np.asanyarray(f) N = len(f.shape) # number of dimensions @@ -1576,22 +1690,32 @@ def gradient(f, *varargs, **kwargs): if max(axes) >= N or min(axes) < 0: raise ValueError("'axis' entry is out of bounds") - if len(set(axes)) != len(axes): + len_axes = len(axes) + if len(set(axes)) != len_axes: raise ValueError("duplicate value in 'axis'") n = len(varargs) if n == 0: - dx = [1.0]*N - elif n == 1: - dx = [varargs[0]]*N - elif n == len(axes): + dx = [1.0] * len_axes + elif n == len_axes or (n == 1 and np.isscalar(varargs[0])): dx = list(varargs) + for i, distances in enumerate(dx): + if np.isscalar(distances): + continue + if len(distances) != f.shape[axes[i]]: + raise ValueError("distances must be either scalars or match " + "the length of the corresponding dimension") + diffx = np.diff(dx[i]) + # if distances are constant reduce to the scalar case + # since it brings a consistent speedup + if (diffx == diffx[0]).all(): + diffx = diffx[0] + dx[i] = diffx + if len(dx) == 1: + dx *= len_axes else: - raise SyntaxError( - "invalid number of arguments") - if any([not np.isscalar(dxi) for dxi in dx]): - raise ValueError("distances must be scalars") - + raise TypeError("invalid number of arguments") + edge_order = kwargs.pop('edge_order', 1) if kwargs: raise TypeError('"{}" are not valid keyword arguments.'.format( @@ -1631,62 +1755,88 @@ def gradient(f, *varargs, **kwargs): y = f for i, axis in enumerate(axes): - - if y.shape[axis] < 2: + if y.shape[axis] < edge_order + 1: raise ValueError( "Shape of array too small to calculate a numerical gradient, " - "at least two elements are required.") - - # Numerical differentiation: 1st order edges, 2nd order interior - if y.shape[axis] == 2 or edge_order == 1: - # Use first order differences for time data - out = np.empty_like(y, dtype=otype) - - slice1[axis] = slice(1, -1) - slice2[axis] = slice(2, None) - slice3[axis] = slice(None, -2) - # 1D equivalent -- out[1:-1] = (y[2:] - y[:-2])/2.0 - out[slice1] = (y[slice2] - y[slice3])/2.0 - + "at least (edge_order + 1) elements are required.") + # result allocation + out = np.empty_like(y, dtype=otype) + + uniform_spacing = np.isscalar(dx[i]) + + # Numerical differentiation: 2nd order interior + slice1[axis] = slice(1, -1) + slice2[axis] = slice(None, -2) + slice3[axis] = slice(1, -1) + slice4[axis] = slice(2, None) + + if uniform_spacing: + out[slice1] = (f[slice4] - f[slice2]) / (2. * dx[i]) + else: + dx1 = dx[i][0:-1] + dx2 = dx[i][1:] + a = -(dx2)/(dx1 * (dx1 + dx2)) + b = (dx2 - dx1) / (dx1 * dx2) + c = dx1 / (dx2 * (dx1 + dx2)) + # fix the shape for broadcasting + shape = np.ones(N, dtype=int) + shape[axis] = -1 + a.shape = b.shape = c.shape = shape + # 1D equivalent -- out[1:-1] = a * f[:-2] + b * f[1:-1] + c * f[2:] + out[slice1] = a * f[slice2] + b * f[slice3] + c * f[slice4] + + # Numerical differentiation: 1st order edges + if edge_order == 1: slice1[axis] = 0 slice2[axis] = 1 slice3[axis] = 0 - # 1D equivalent -- out[0] = (y[1] - y[0]) - out[slice1] = (y[slice2] - y[slice3]) - + dx_0 = dx[i] if uniform_spacing else dx[i][0] + # 1D equivalent -- out[0] = (y[1] - y[0]) / (x[1] - x[0]) + out[slice1] = (y[slice2] - y[slice3]) / dx_0 + slice1[axis] = -1 slice2[axis] = -1 slice3[axis] = -2 - # 1D equivalent -- out[-1] = (y[-1] - y[-2]) - out[slice1] = (y[slice2] - y[slice3]) - - # Numerical differentiation: 2st order edges, 2nd order interior + dx_n = dx[i] if uniform_spacing else dx[i][-1] + # 1D equivalent -- out[-1] = (y[-1] - y[-2]) / (x[-1] - x[-2]) + out[slice1] = (y[slice2] - y[slice3]) / dx_n + + # Numerical differentiation: 2nd order edges else: - # Use second order differences where possible - out = np.empty_like(y, dtype=otype) - - slice1[axis] = slice(1, -1) - slice2[axis] = slice(2, None) - slice3[axis] = slice(None, -2) - # 1D equivalent -- out[1:-1] = (y[2:] - y[:-2])/2.0 - out[slice1] = (y[slice2] - y[slice3])/2.0 - slice1[axis] = 0 slice2[axis] = 0 slice3[axis] = 1 slice4[axis] = 2 - # 1D equivalent -- out[0] = -(3*y[0] - 4*y[1] + y[2]) / 2.0 - out[slice1] = -(3.0*y[slice2] - 4.0*y[slice3] + y[slice4])/2.0 - + if uniform_spacing: + a = -1.5 / dx[i] + b = 2. / dx[i] + c = -0.5 / dx[i] + else: + dx1 = dx[i][0] + dx2 = dx[i][1] + a = -(2. * dx1 + dx2)/(dx1 * (dx1 + dx2)) + b = (dx1 + dx2) / (dx1 * dx2) + c = - dx1 / (dx2 * (dx1 + dx2)) + # 1D equivalent -- out[0] = a * y[0] + b * y[1] + c * y[2] + out[slice1] = a * y[slice2] + b * y[slice3] + c * y[slice4] + slice1[axis] = -1 - slice2[axis] = -1 + slice2[axis] = -3 slice3[axis] = -2 - slice4[axis] = -3 - # 1D equivalent -- out[-1] = (3*y[-1] - 4*y[-2] + y[-3]) - out[slice1] = (3.0*y[slice2] - 4.0*y[slice3] + y[slice4])/2.0 - - # divide by step size - out /= dx[i] + slice4[axis] = -1 + if uniform_spacing: + a = 0.5 / dx[i] + b = -2. / dx[i] + c = 1.5 / dx[i] + else: + dx1 = dx[i][-2] + dx2 = dx[i][-1] + a = (dx2) / (dx1 * (dx1 + dx2)) + b = - (dx2 + dx1) / (dx1 * dx2) + c = (2. * dx2 + dx1) / (dx2 * (dx1 + dx2)) + # 1D equivalent -- out[-1] = a * f[-3] + b * f[-2] + c * f[-1] + out[slice1] = a * y[slice2] + b * y[slice3] + c * y[slice4] + outvals.append(out) # reset the slice object in this dimension to ":" @@ -1695,7 +1845,7 @@ def gradient(f, *varargs, **kwargs): slice3[axis] = slice(None) slice4[axis] = slice(None) - if len(axes) == 1: + if len_axes == 1: return outvals[0] else: return outvals @@ -2746,9 +2896,9 @@ def cov(m, y=None, rowvar=True, bias=False, ddof=None, fweights=None, contain observations. bias : bool, optional Default normalization (False) is by ``(N - 1)``, where ``N`` is the - number of observations given (unbiased estimate). If `bias` is True, then - normalization is by ``N``. These values can be overridden by using the - keyword ``ddof`` in numpy versions >= 1.5. + number of observations given (unbiased estimate). If `bias` is True, + then normalization is by ``N``. These values can be overridden by using + the keyword ``ddof`` in numpy versions >= 1.5. ddof : int, optional If not ``None`` the default value implied by `bias` is overridden. Note that ``ddof=1`` will return the unbiased estimate, even if both @@ -2912,7 +3062,8 @@ def cov(m, y=None, rowvar=True, bias=False, ddof=None, fweights=None, fact = w_sum - ddof*sum(w*aweights)/w_sum if fact <= 0: - warnings.warn("Degrees of freedom <= 0 for slice", RuntimeWarning, stacklevel=2) + warnings.warn("Degrees of freedom <= 0 for slice", + RuntimeWarning, stacklevel=2) fact = 0.0 X -= avg[:, None] @@ -4665,9 +4816,9 @@ def delete(arr, obj, axis=None): # After removing the special handling of booleans and out of # bounds values, the conversion to the array can be removed. if obj.dtype == bool: - warnings.warn( - "in the future insert will treat boolean arrays and array-likes " - "as boolean index instead of casting it to integer", FutureWarning, stacklevel=2) + warnings.warn("in the future insert will treat boolean arrays and " + "array-likes as boolean index instead of casting it " + "to integer", FutureWarning, stacklevel=2) obj = obj.astype(intp) if isinstance(_obj, (int, long, integer)): # optimization for a single value @@ -4828,14 +4979,7 @@ def insert(arr, obj, values, axis=None): arr = arr.ravel() ndim = arr.ndim axis = ndim - 1 - else: - if ndim > 0 and (axis < -ndim or axis >= ndim): - raise IndexError( - "axis %i is out of bounds for an array of " - "dimension %i" % (axis, ndim)) - if (axis < 0): - axis += ndim - if (ndim == 0): + elif ndim == 0: # 2013-09-24, 1.9 warnings.warn( "in the future the special handling of scalars will be removed " @@ -4846,6 +4990,8 @@ def insert(arr, obj, values, axis=None): return wrap(arr) else: return arr + else: + axis = normalize_axis_index(axis, ndim) slobj = [slice(None)]*ndim N = arr.shape[axis] newshape = list(arr.shape) diff --git a/numpy/lib/shape_base.py b/numpy/lib/shape_base.py index 58e13533b..62798286f 100644 --- a/numpy/lib/shape_base.py +++ b/numpy/lib/shape_base.py @@ -7,6 +7,7 @@ from numpy.core.numeric import ( asarray, zeros, outer, concatenate, isscalar, array, asanyarray ) from numpy.core.fromnumeric import product, reshape, transpose +from numpy.core.multiarray import normalize_axis_index from numpy.core import vstack, atleast_3d from numpy.lib.index_tricks import ndindex from numpy.matrixlib.defmatrix import matrix # this raises all the right alarm bells @@ -96,10 +97,7 @@ def apply_along_axis(func1d, axis, arr, *args, **kwargs): # handle negative axes arr = asanyarray(arr) nd = arr.ndim - if not (-nd <= axis < nd): - raise IndexError('axis {0} out of bounds [-{1}, {1})'.format(axis, nd)) - if axis < 0: - axis += nd + axis = normalize_axis_index(axis, nd) # arr, with the iteration axis at the end in_dims = list(range(nd)) @@ -289,8 +287,7 @@ def expand_dims(a, axis): """ a = asarray(a) shape = a.shape - if axis < 0: - axis = axis + len(shape) + 1 + axis = normalize_axis_index(axis, a.ndim + 1) return a.reshape(shape[:axis] + (1,) + shape[axis:]) row_stack = vstack diff --git a/numpy/lib/tests/test_function_base.py b/numpy/lib/tests/test_function_base.py index f69c24d59..4fb0dba51 100644 --- a/numpy/lib/tests/test_function_base.py +++ b/numpy/lib/tests/test_function_base.py @@ -96,7 +96,7 @@ class TestRot90(TestCase): for k in range(1,5): assert_equal(rot90(a, k=k, axes=(2, 0)), - rot90(a_rot90_20, k=k-1, axes=(2, 0))) + rot90(a_rot90_20, k=k-1, axes=(2, 0))) class TestFlip(TestCase): @@ -168,7 +168,8 @@ class TestFlip(TestCase): def test_4d(self): a = np.arange(2 * 3 * 4 * 5).reshape(2, 3, 4, 5) for i in range(a.ndim): - assert_equal(np.flip(a, i), np.flipud(a.swapaxes(0, i)).swapaxes(i, 0)) + assert_equal(np.flip(a, i), + np.flipud(a.swapaxes(0, i)).swapaxes(i, 0)) class TestAny(TestCase): @@ -219,7 +220,7 @@ class TestCopy(TestCase): def test_order(self): # It turns out that people rely on np.copy() preserving order by # default; changing this broke scikit-learn: - # https://github.com/scikit-learn/scikit-learn/commit/7842748cf777412c506a8c0ed28090711d3a3783 + # github.com/scikit-learn/scikit-learn/commit/7842748cf777412c506a8c0ed28090711d3a3783 # noqa a = np.array([[1, 2], [3, 4]]) assert_(a.flags.c_contiguous) assert_(not a.flags.f_contiguous) @@ -466,8 +467,8 @@ class TestInsert(TestCase): insert(a, 1, a[:, 2,:], axis=1)) # invalid axis value - assert_raises(IndexError, insert, a, 1, a[:, 2, :], axis=3) - assert_raises(IndexError, insert, a, 1, a[:, 2, :], axis=-4) + assert_raises(np.AxisError, insert, a, 1, a[:, 2, :], axis=3) + assert_raises(np.AxisError, insert, a, 1, a[:, 2, :], axis=-4) # negative axis value a = np.arange(24).reshape((2, 3, 4)) @@ -555,7 +556,8 @@ class TestCumsum(TestCase): ba = [1, 2, 10, 11, 6, 5, 4] ba2 = [[1, 2, 3, 4], [5, 6, 7, 9], [10, 3, 4, 5]] for ctype in [np.int8, np.uint8, np.int16, np.uint16, np.int32, - np.uint32, np.float32, np.float64, np.complex64, np.complex128]: + np.uint32, np.float32, np.float64, np.complex64, + np.complex128]: a = np.array(ba, ctype) a2 = np.array(ba2, ctype) @@ -726,14 +728,51 @@ class TestGradient(TestCase): assert_array_equal(gradient(x), dx) assert_array_equal(gradient(v), dx) + def test_args(self): + dx = np.cumsum(np.ones(5)) + dx_uneven = [1., 2., 5., 9., 11.] + f_2d = np.arange(25).reshape(5, 5) + + # distances must be scalars or have size equal to gradient[axis] + gradient(np.arange(5), 3.) + gradient(np.arange(5), dx) + gradient(f_2d, 1.5) # dy is set equal to dx because scalar + + gradient(f_2d, dx_uneven, dx_uneven) + # mix between even and uneven spaces and + # mix between scalar and vector + gradient(f_2d, dx, 2) + + # 2D but axis specified + gradient(f_2d, dx, axis=1) + def test_badargs(self): - # for 2D array, gradient can take 0, 1, or 2 extra args - x = np.array([[1, 1], [3, 4]]) - assert_raises(SyntaxError, gradient, x, np.array([1., 1.]), - np.array([1., 1.]), np.array([1., 1.])) + f_2d = np.arange(25).reshape(5, 5) + x = np.cumsum(np.ones(5)) + + # wrong sizes + assert_raises(ValueError, gradient, f_2d, x, np.ones(2)) + assert_raises(ValueError, gradient, f_2d, 1, np.ones(2)) + assert_raises(ValueError, gradient, f_2d, np.ones(2), np.ones(2)) + # wrong number of arguments + assert_raises(TypeError, gradient, f_2d, x) + assert_raises(TypeError, gradient, f_2d, x, axis=(0,1)) + assert_raises(TypeError, gradient, f_2d, x, x, x) + assert_raises(TypeError, gradient, f_2d, 1, 1, 1) + assert_raises(TypeError, gradient, f_2d, x, x, axis=1) + assert_raises(TypeError, gradient, f_2d, 1, 1, axis=1) - # disallow arrays as distances, see gh-6847 - assert_raises(ValueError, gradient, np.arange(5), np.ones(5)) + def test_datetime64(self): + # Make sure gradient() can handle special types like datetime64 + x = np.array( + ['1910-08-16', '1910-08-11', '1910-08-10', '1910-08-12', + '1910-10-12', '1910-12-12', '1912-12-12'], + dtype='datetime64[D]') + dx = np.array( + [-5, -3, 0, 31, 61, 396, 731], + dtype='timedelta64[D]') + assert_array_equal(gradient(x), dx) + assert_(dx.dtype == np.dtype('timedelta64[D]')) def test_masked(self): # Make sure that gradient supports subclasses like masked arrays @@ -750,29 +789,6 @@ class TestGradient(TestCase): np.gradient(x2, edge_order=2) assert_array_equal(x2.mask, [False, False, True, False, False]) - def test_datetime64(self): - # Make sure gradient() can handle special types like datetime64 - x = np.array( - ['1910-08-16', '1910-08-11', '1910-08-10', '1910-08-12', - '1910-10-12', '1910-12-12', '1912-12-12'], - dtype='datetime64[D]') - dx = np.array( - [-5, -3, 0, 31, 61, 396, 731], - dtype='timedelta64[D]') - assert_array_equal(gradient(x), dx) - assert_(dx.dtype == np.dtype('timedelta64[D]')) - - def test_timedelta64(self): - # Make sure gradient() can handle special types like timedelta64 - x = np.array( - [-5, -3, 10, 12, 61, 321, 300], - dtype='timedelta64[D]') - dx = np.array( - [2, 7, 7, 25, 154, 119, -21], - dtype='timedelta64[D]') - assert_array_equal(gradient(x), dx) - assert_(dx.dtype == np.dtype('timedelta64[D]')) - def test_second_order_accurate(self): # Testing that the relative numerical error is less that 3% for # this example problem. This corresponds to second order @@ -785,6 +801,78 @@ class TestGradient(TestCase): num_error = np.abs((np.gradient(y, dx, edge_order=2) / analytical) - 1) assert_(np.all(num_error < 0.03) == True) + # test with unevenly spaced + np.random.seed(0) + x = np.sort(np.random.random(10)) + y = 2 * x ** 3 + 4 * x ** 2 + 2 * x + analytical = 6 * x ** 2 + 8 * x + 2 + num_error = np.abs((np.gradient(y, x, edge_order=2) / analytical) - 1) + assert_(np.all(num_error < 0.03) == True) + + def test_spacing(self): + f = np.array([0, 2., 3., 4., 5., 5.]) + f = np.tile(f, (6,1)) + f.reshape(-1, 1) + x_uneven = np.array([0., 0.5, 1., 3., 5., 7.]) + x_even = np.arange(6.) + + fdx_even_ord1 = np.tile([2., 1.5, 1., 1., 0.5, 0.], (6,1)) + fdx_even_ord2 = np.tile([2.5, 1.5, 1., 1., 0.5, -0.5], (6,1)) + fdx_uneven_ord1 = np.tile([4., 3., 1.7, 0.5, 0.25, 0.], (6,1)) + fdx_uneven_ord2 = np.tile([5., 3., 1.7, 0.5, 0.25, -0.25], (6,1)) + + # evenly spaced + for edge_order, exp_res in [(1, fdx_even_ord1), (2, fdx_even_ord2)]: + res1 = gradient(f, 1., axis=(0,1), edge_order=edge_order) + res2 = gradient(f, x_even, x_even, + axis=(0,1), edge_order=edge_order) + res3 = gradient(f, x_even, x_even, + axis=None, edge_order=edge_order) + assert_array_equal(res1, res2) + assert_array_equal(res2, res3) + assert_almost_equal(res1[0], exp_res.T) + assert_almost_equal(res1[1], exp_res) + + res1 = gradient(f, 1., axis=0, edge_order=edge_order) + res2 = gradient(f, x_even, axis=0, edge_order=edge_order) + assert_(res1.shape == res2.shape) + assert_almost_equal(res2, exp_res.T) + + res1 = gradient(f, 1., axis=1, edge_order=edge_order) + res2 = gradient(f, x_even, axis=1, edge_order=edge_order) + assert_(res1.shape == res2.shape) + assert_array_equal(res2, exp_res) + + # unevenly spaced + for edge_order, exp_res in [(1, fdx_uneven_ord1), (2, fdx_uneven_ord2)]: + res1 = gradient(f, x_uneven, x_uneven, + axis=(0,1), edge_order=edge_order) + res2 = gradient(f, x_uneven, x_uneven, + axis=None, edge_order=edge_order) + assert_array_equal(res1, res2) + assert_almost_equal(res1[0], exp_res.T) + assert_almost_equal(res1[1], exp_res) + + res1 = gradient(f, x_uneven, axis=0, edge_order=edge_order) + assert_almost_equal(res1, exp_res.T) + + res1 = gradient(f, x_uneven, axis=1, edge_order=edge_order) + assert_almost_equal(res1, exp_res) + + # mixed + res1 = gradient(f, x_even, x_uneven, axis=(0,1), edge_order=1) + res2 = gradient(f, x_uneven, x_even, axis=(1,0), edge_order=1) + assert_array_equal(res1[0], res2[1]) + assert_array_equal(res1[1], res2[0]) + assert_almost_equal(res1[0], fdx_even_ord1.T) + assert_almost_equal(res1[1], fdx_uneven_ord1) + + res1 = gradient(f, x_even, x_uneven, axis=(0,1), edge_order=2) + res2 = gradient(f, x_uneven, x_even, axis=(1,0), edge_order=2) + assert_array_equal(res1[0], res2[1]) + assert_array_equal(res1[1], res2[0]) + assert_almost_equal(res1[0], fdx_even_ord2.T) + assert_almost_equal(res1[1], fdx_uneven_ord2) + def test_specific_axes(self): # Testing that gradient can work on a given axis only v = [[1, 1], [3, 4]] @@ -802,13 +890,37 @@ class TestGradient(TestCase): assert_almost_equal(gradient(x, axis=None), gradient(x)) # test vararg order - assert_array_equal(gradient(x, 2, 3, axis=(1, 0)), [dx[1]/2.0, dx[0]/3.0]) + assert_array_equal(gradient(x, 2, 3, axis=(1, 0)), + [dx[1]/2.0, dx[0]/3.0]) # test maximal number of varargs - assert_raises(SyntaxError, gradient, x, 1, 2, axis=1) + assert_raises(TypeError, gradient, x, 1, 2, axis=1) assert_raises(ValueError, gradient, x, axis=3) assert_raises(ValueError, gradient, x, axis=-3) assert_raises(TypeError, gradient, x, axis=[1,]) + + def test_timedelta64(self): + # Make sure gradient() can handle special types like timedelta64 + x = np.array( + [-5, -3, 10, 12, 61, 321, 300], + dtype='timedelta64[D]') + dx = np.array( + [2, 7, 7, 25, 154, 119, -21], + dtype='timedelta64[D]') + assert_array_equal(gradient(x), dx) + assert_(dx.dtype == np.dtype('timedelta64[D]')) + + def test_values(self): + # needs at least 2 points for edge_order ==1 + gradient(np.arange(2), edge_order=1) + # needs at least 3 points for edge_order ==1 + gradient(np.arange(3), edge_order=2) + + assert_raises(ValueError, gradient, np.arange(0), edge_order=1) + assert_raises(ValueError, gradient, np.arange(0), edge_order=2) + assert_raises(ValueError, gradient, np.arange(1), edge_order=1) + assert_raises(ValueError, gradient, np.arange(1), edge_order=2) + assert_raises(ValueError, gradient, np.arange(2), edge_order=2) class TestAngle(TestCase): @@ -1753,20 +1865,28 @@ class TestHistogramOptimBinNums(TestCase): completely ignored. All test values have been precomputed and the shouldn't change. """ - # some basic sanity checking, with some fixed data. Checking for the correct number of bins - basic_test = {50: {'fd': 8, 'scott': 8, 'rice': 15, 'sturges': 14, 'auto': 14}, - 500: {'fd': 15, 'scott': 16, 'rice': 32, 'sturges': 20, 'auto': 20}, - 5000: {'fd': 33, 'scott': 33, 'rice': 69, 'sturges': 27, 'auto': 33}} + # some basic sanity checking, with some fixed data. + # Checking for the correct number of bins + basic_test = { + 50: {'fd': 8, 'scott': 8, 'rice': 15, + 'sturges': 14, 'auto': 14}, + 500: {'fd': 15, 'scott': 16, 'rice': 32, + 'sturges': 20, 'auto': 20}, + 5000: {'fd': 33, 'scott': 33, 'rice': 69, + 'sturges': 27, 'auto': 33} + } for testlen, expectedResults in basic_test.items(): - # create some sort of non uniform data to test with (3 peak uniform mixture) + # create some sort of non uniform data to test with + # (3 peak uniform mixture) x1 = np.linspace(-10, -1, testlen // 5 * 2) x2 = np.linspace(1, 10, testlen // 5 * 3) x3 = np.linspace(-100, -50, testlen) x = np.hstack((x1, x2, x3)) for estimator, numbins in expectedResults.items(): a, b = np.histogram(x, estimator, range = (-20, 20)) - msg = "For the {0} estimator with datasize of {1}".format(estimator, testlen) + msg = "For the {0} estimator".format(estimator) + msg += " with datasize of {0}".format(testlen) assert_equal(len(a), numbins, err_msg=msg) def test_simple_weighted(self): @@ -1775,7 +1895,8 @@ class TestHistogramOptimBinNums(TestCase): """ estimator_list = ['fd', 'scott', 'rice', 'sturges', 'auto'] for estimator in estimator_list: - assert_raises(TypeError, histogram, [1, 2, 3], estimator, weights=[1, 2, 3]) + assert_raises(TypeError, histogram, [1, 2, 3], + estimator, weights=[1, 2, 3]) class TestHistogramdd(TestCase): diff --git a/numpy/linalg/lapack_lite/blas_lite.c b/numpy/linalg/lapack_lite/blas_lite.c index 0991b2d21..6ef3bf0a3 100644 --- a/numpy/linalg/lapack_lite/blas_lite.c +++ b/numpy/linalg/lapack_lite/blas_lite.c @@ -16,6 +16,15 @@ extern doublereal dlamch_(char *); extern doublereal dlapy2_(doublereal *x, doublereal *y); +/* +f2c knows the exact rules for precedence, and so omits parentheses where not +strictly necessary. Since this is generated code, we don't really care if +it's readable, and we know what is written is correct. So don't warn about +them. +*/ +#if defined(__GNUC__) +#pragma GCC diagnostic ignored "-Wparentheses" +#endif /* Table of constant values */ diff --git a/numpy/linalg/lapack_lite/dlapack_lite.c b/numpy/linalg/lapack_lite/dlapack_lite.c index be6e0c6d4..116aa6ceb 100644 --- a/numpy/linalg/lapack_lite/dlapack_lite.c +++ b/numpy/linalg/lapack_lite/dlapack_lite.c @@ -16,6 +16,15 @@ extern doublereal dlamch_(char *); extern doublereal dlapy2_(doublereal *x, doublereal *y); +/* +f2c knows the exact rules for precedence, and so omits parentheses where not +strictly necessary. Since this is generated code, we don't really care if +it's readable, and we know what is written is correct. So don't warn about +them. +*/ +#if defined(__GNUC__) +#pragma GCC diagnostic ignored "-Wparentheses" +#endif /* Table of constant values */ diff --git a/numpy/linalg/lapack_lite/make_lite.py b/numpy/linalg/lapack_lite/make_lite.py index 2370e0004..041e34692 100755 --- a/numpy/linalg/lapack_lite/make_lite.py +++ b/numpy/linalg/lapack_lite/make_lite.py @@ -35,6 +35,15 @@ extern doublereal dlamch_(char *); extern doublereal dlapy2_(doublereal *x, doublereal *y); +/* +f2c knows the exact rules for precedence, and so omits parentheses where not +strictly necessary. Since this is generated code, we don't really care if +it's readable, and we know what is written is correct. So don't warn about +them. +*/ +#if defined(__GNUC__) +#pragma GCC diagnostic ignored "-Wparentheses" +#endif ''' class FortranRoutine(object): diff --git a/numpy/linalg/lapack_lite/zlapack_lite.c b/numpy/linalg/lapack_lite/zlapack_lite.c index 7dcd92cc5..143b7254f 100644 --- a/numpy/linalg/lapack_lite/zlapack_lite.c +++ b/numpy/linalg/lapack_lite/zlapack_lite.c @@ -16,6 +16,15 @@ extern doublereal dlamch_(char *); extern doublereal dlapy2_(doublereal *x, doublereal *y); +/* +f2c knows the exact rules for precedence, and so omits parentheses where not +strictly necessary. Since this is generated code, we don't really care if +it's readable, and we know what is written is correct. So don't warn about +them. +*/ +#if defined(__GNUC__) +#pragma GCC diagnostic ignored "-Wparentheses" +#endif /* Table of constant values */ diff --git a/numpy/linalg/linalg.py b/numpy/linalg/linalg.py index 7b4bbf416..6002c63b9 100644 --- a/numpy/linalg/linalg.py +++ b/numpy/linalg/linalg.py @@ -25,6 +25,7 @@ from numpy.core import ( finfo, errstate, geterrobj, longdouble, rollaxis, amin, amax, product, abs, broadcast, atleast_2d, intp, asanyarray, isscalar, object_ ) +from numpy.core.multiarray import normalize_axis_index from numpy.lib import triu, asfarray from numpy.linalg import lapack_lite, _umath_linalg from numpy.matrixlib.defmatrix import matrix_power @@ -2236,13 +2237,8 @@ def norm(x, ord=None, axis=None, keepdims=False): return add.reduce(absx, axis=axis, keepdims=keepdims) ** (1.0 / ord) elif len(axis) == 2: row_axis, col_axis = axis - if row_axis < 0: - row_axis += nd - if col_axis < 0: - col_axis += nd - if not (0 <= row_axis < nd and 0 <= col_axis < nd): - raise ValueError('Invalid axis %r for an array with shape %r' % - (axis, x.shape)) + row_axis = normalize_axis_index(row_axis, nd) + col_axis = normalize_axis_index(col_axis, nd) if row_axis == col_axis: raise ValueError('Duplicate axes given.') if ord == 2: diff --git a/numpy/linalg/tests/test_linalg.py b/numpy/linalg/tests/test_linalg.py index 6f289f51f..fc4f98ed7 100644 --- a/numpy/linalg/tests/test_linalg.py +++ b/numpy/linalg/tests/test_linalg.py @@ -1198,8 +1198,8 @@ class _TestNorm(object): assert_raises(ValueError, norm, B, order, (1, 2)) # Invalid axis - assert_raises(ValueError, norm, B, None, 3) - assert_raises(ValueError, norm, B, None, (2, 3)) + assert_raises(np.AxisError, norm, B, None, 3) + assert_raises(np.AxisError, norm, B, None, (2, 3)) assert_raises(ValueError, norm, B, None, (0, 1, 2)) diff --git a/numpy/ma/core.py b/numpy/ma/core.py index a6f474b95..1b25725d1 100644 --- a/numpy/ma/core.py +++ b/numpy/ma/core.py @@ -41,6 +41,7 @@ from numpy.compat import ( getargspec, formatargspec, long, basestring, unicode, bytes, sixu ) from numpy import expand_dims as n_expand_dims +from numpy.core.multiarray import normalize_axis_index if sys.version_info[0] >= 3: @@ -3902,7 +3903,9 @@ class MaskedArray(ndarray): axis = None try: mask = mask.view((bool_, len(self.dtype))).all(axis) - except ValueError: + except (ValueError, np.AxisError): + # TODO: what error are we trying to catch here? + # invalid axis, or invalid view? mask = np.all([[f[n].all() for n in mask.dtype.names] for f in mask], axis=axis) check._mask = mask @@ -3938,7 +3941,9 @@ class MaskedArray(ndarray): axis = None try: mask = mask.view((bool_, len(self.dtype))).all(axis) - except ValueError: + except (ValueError, np.AxisError): + # TODO: what error are we trying to catch here? + # invalid axis, or invalid view? mask = np.all([[f[n].all() for n in mask.dtype.names] for f in mask], axis=axis) check._mask = mask @@ -4340,7 +4345,7 @@ class MaskedArray(ndarray): if self.shape is (): if axis not in (None, 0): - raise ValueError("'axis' entry is out of bounds") + raise np.AxisError("'axis' entry is out of bounds") return 1 elif axis is None: if kwargs.get('keepdims', False): @@ -4348,11 +4353,9 @@ class MaskedArray(ndarray): return self.size axes = axis if isinstance(axis, tuple) else (axis,) - axes = tuple(a if a >= 0 else self.ndim + a for a in axes) + axes = tuple(normalize_axis_index(a, self.ndim) for a in axes) if len(axes) != len(set(axes)): raise ValueError("duplicate value in 'axis'") - if builtins.any(a < 0 or a >= self.ndim for a in axes): - raise ValueError("'axis' entry is out of bounds") items = 1 for ax in axes: items *= self.shape[ax] diff --git a/numpy/ma/extras.py b/numpy/ma/extras.py index 29a15633d..7149b525b 100644 --- a/numpy/ma/extras.py +++ b/numpy/ma/extras.py @@ -36,6 +36,7 @@ from .core import ( import numpy as np from numpy import ndarray, array as nxarray import numpy.core.umath as umath +from numpy.core.multiarray import normalize_axis_index from numpy.lib.function_base import _ureduce from numpy.lib.index_tricks import AxisConcatenator @@ -380,11 +381,7 @@ def apply_along_axis(func1d, axis, arr, *args, **kwargs): """ arr = array(arr, copy=False, subok=True) nd = arr.ndim - if axis < 0: - axis += nd - if (axis >= nd): - raise ValueError("axis must be less than arr.ndim; axis=%d, rank=%d." - % (axis, nd)) + axis = normalize_axis_index(axis, nd) ind = [0] * (nd - 1) i = np.zeros(nd, 'O') indlist = list(range(nd)) @@ -717,8 +714,8 @@ def _median(a, axis=None, out=None, overwrite_input=False): if axis is None: axis = 0 - elif axis < 0: - axis += asorted.ndim + else: + axis = normalize_axis_index(axis, asorted.ndim) if asorted.ndim == 1: counts = count(asorted) diff --git a/numpy/ma/tests/test_core.py b/numpy/ma/tests/test_core.py index f72ddc5ea..9d8002ed0 100644 --- a/numpy/ma/tests/test_core.py +++ b/numpy/ma/tests/test_core.py @@ -1030,7 +1030,7 @@ class TestMaskedArrayArithmetic(TestCase): res = count(ott, 0) assert_(isinstance(res, ndarray)) assert_(res.dtype.type is np.intp) - assert_raises(ValueError, ott.count, axis=1) + assert_raises(np.AxisError, ott.count, axis=1) def test_minmax_func(self): # Tests minimum and maximum. @@ -4409,7 +4409,7 @@ class TestOptionalArgs(TestCase): assert_equal(count(a, axis=(0,1), keepdims=True), 4*ones((1,1,4))) assert_equal(count(a, axis=-2), 2*ones((2,4))) assert_raises(ValueError, count, a, axis=(1,1)) - assert_raises(ValueError, count, a, axis=3) + assert_raises(np.AxisError, count, a, axis=3) # check the 'nomask' path a = np.ma.array(d, mask=nomask) @@ -4423,13 +4423,13 @@ class TestOptionalArgs(TestCase): assert_equal(count(a, axis=(0,1), keepdims=True), 6*ones((1,1,4))) assert_equal(count(a, axis=-2), 3*ones((2,4))) assert_raises(ValueError, count, a, axis=(1,1)) - assert_raises(ValueError, count, a, axis=3) + assert_raises(np.AxisError, count, a, axis=3) # check the 'masked' singleton assert_equal(count(np.ma.masked), 0) # check 0-d arrays do not allow axis > 0 - assert_raises(ValueError, count, np.ma.array(1), axis=1) + assert_raises(np.AxisError, count, np.ma.array(1), axis=1) class TestMaskedConstant(TestCase): diff --git a/numpy/polynomial/chebyshev.py b/numpy/polynomial/chebyshev.py index 3babb8fc2..49d0302e0 100644 --- a/numpy/polynomial/chebyshev.py +++ b/numpy/polynomial/chebyshev.py @@ -90,6 +90,7 @@ from __future__ import division, absolute_import, print_function import warnings import numpy as np import numpy.linalg as la +from numpy.core.multiarray import normalize_axis_index from . import polyutils as pu from ._polybase import ABCPolyBase @@ -936,10 +937,7 @@ def chebder(c, m=1, scl=1, axis=0): raise ValueError("The order of derivation must be non-negative") if iaxis != axis: raise ValueError("The axis must be integer") - if not -c.ndim <= iaxis < c.ndim: - raise ValueError("The axis is out of range") - if iaxis < 0: - iaxis += c.ndim + iaxis = normalize_axis_index(iaxis, c.ndim) if cnt == 0: return c @@ -1064,10 +1062,7 @@ def chebint(c, m=1, k=[], lbnd=0, scl=1, axis=0): raise ValueError("Too many integration constants") if iaxis != axis: raise ValueError("The axis must be integer") - if not -c.ndim <= iaxis < c.ndim: - raise ValueError("The axis is out of range") - if iaxis < 0: - iaxis += c.ndim + iaxis = normalize_axis_index(iaxis, c.ndim) if cnt == 0: return c diff --git a/numpy/polynomial/hermite.py b/numpy/polynomial/hermite.py index 0ebae2027..a03fe722c 100644 --- a/numpy/polynomial/hermite.py +++ b/numpy/polynomial/hermite.py @@ -62,6 +62,7 @@ from __future__ import division, absolute_import, print_function import warnings import numpy as np import numpy.linalg as la +from numpy.core.multiarray import normalize_axis_index from . import polyutils as pu from ._polybase import ABCPolyBase @@ -700,10 +701,7 @@ def hermder(c, m=1, scl=1, axis=0): raise ValueError("The order of derivation must be non-negative") if iaxis != axis: raise ValueError("The axis must be integer") - if not -c.ndim <= iaxis < c.ndim: - raise ValueError("The axis is out of range") - if iaxis < 0: - iaxis += c.ndim + iaxis = normalize_axis_index(iaxis, c.ndim) if cnt == 0: return c @@ -822,10 +820,7 @@ def hermint(c, m=1, k=[], lbnd=0, scl=1, axis=0): raise ValueError("Too many integration constants") if iaxis != axis: raise ValueError("The axis must be integer") - if not -c.ndim <= iaxis < c.ndim: - raise ValueError("The axis is out of range") - if iaxis < 0: - iaxis += c.ndim + iaxis = normalize_axis_index(iaxis, c.ndim) if cnt == 0: return c diff --git a/numpy/polynomial/hermite_e.py b/numpy/polynomial/hermite_e.py index a09b66670..2a29d61cf 100644 --- a/numpy/polynomial/hermite_e.py +++ b/numpy/polynomial/hermite_e.py @@ -62,6 +62,7 @@ from __future__ import division, absolute_import, print_function import warnings import numpy as np import numpy.linalg as la +from numpy.core.multiarray import normalize_axis_index from . import polyutils as pu from ._polybase import ABCPolyBase @@ -699,10 +700,7 @@ def hermeder(c, m=1, scl=1, axis=0): raise ValueError("The order of derivation must be non-negative") if iaxis != axis: raise ValueError("The axis must be integer") - if not -c.ndim <= iaxis < c.ndim: - raise ValueError("The axis is out of range") - if iaxis < 0: - iaxis += c.ndim + iaxis = normalize_axis_index(iaxis, c.ndim) if cnt == 0: return c @@ -821,10 +819,7 @@ def hermeint(c, m=1, k=[], lbnd=0, scl=1, axis=0): raise ValueError("Too many integration constants") if iaxis != axis: raise ValueError("The axis must be integer") - if not -c.ndim <= iaxis < c.ndim: - raise ValueError("The axis is out of range") - if iaxis < 0: - iaxis += c.ndim + iaxis = normalize_axis_index(iaxis, c.ndim) if cnt == 0: return c diff --git a/numpy/polynomial/laguerre.py b/numpy/polynomial/laguerre.py index dfa997254..c9e1302e1 100644 --- a/numpy/polynomial/laguerre.py +++ b/numpy/polynomial/laguerre.py @@ -62,6 +62,7 @@ from __future__ import division, absolute_import, print_function import warnings import numpy as np import numpy.linalg as la +from numpy.core.multiarray import normalize_axis_index from . import polyutils as pu from ._polybase import ABCPolyBase @@ -697,10 +698,7 @@ def lagder(c, m=1, scl=1, axis=0): raise ValueError("The order of derivation must be non-negative") if iaxis != axis: raise ValueError("The axis must be integer") - if not -c.ndim <= iaxis < c.ndim: - raise ValueError("The axis is out of range") - if iaxis < 0: - iaxis += c.ndim + iaxis = normalize_axis_index(iaxis, c.ndim) if cnt == 0: return c @@ -822,10 +820,7 @@ def lagint(c, m=1, k=[], lbnd=0, scl=1, axis=0): raise ValueError("Too many integration constants") if iaxis != axis: raise ValueError("The axis must be integer") - if not -c.ndim <= iaxis < c.ndim: - raise ValueError("The axis is out of range") - if iaxis < 0: - iaxis += c.ndim + iaxis = normalize_axis_index(iaxis, c.ndim) if cnt == 0: return c diff --git a/numpy/polynomial/legendre.py b/numpy/polynomial/legendre.py index fdaa56e0c..fa578360e 100644 --- a/numpy/polynomial/legendre.py +++ b/numpy/polynomial/legendre.py @@ -86,6 +86,7 @@ from __future__ import division, absolute_import, print_function import warnings import numpy as np import numpy.linalg as la +from numpy.core.multiarray import normalize_axis_index from . import polyutils as pu from ._polybase import ABCPolyBase @@ -736,10 +737,7 @@ def legder(c, m=1, scl=1, axis=0): raise ValueError("The order of derivation must be non-negative") if iaxis != axis: raise ValueError("The axis must be integer") - if not -c.ndim <= iaxis < c.ndim: - raise ValueError("The axis is out of range") - if iaxis < 0: - iaxis += c.ndim + iaxis = normalize_axis_index(iaxis, c.ndim) if cnt == 0: return c @@ -864,10 +862,7 @@ def legint(c, m=1, k=[], lbnd=0, scl=1, axis=0): raise ValueError("Too many integration constants") if iaxis != axis: raise ValueError("The axis must be integer") - if not -c.ndim <= iaxis < c.ndim: - raise ValueError("The axis is out of range") - if iaxis < 0: - iaxis += c.ndim + iaxis = normalize_axis_index(iaxis, c.ndim) if cnt == 0: return c diff --git a/numpy/polynomial/polynomial.py b/numpy/polynomial/polynomial.py index 19b085eaf..c357b48c9 100644 --- a/numpy/polynomial/polynomial.py +++ b/numpy/polynomial/polynomial.py @@ -66,6 +66,7 @@ __all__ = [ import warnings import numpy as np import numpy.linalg as la +from numpy.core.multiarray import normalize_axis_index from . import polyutils as pu from ._polybase import ABCPolyBase @@ -540,10 +541,7 @@ def polyder(c, m=1, scl=1, axis=0): raise ValueError("The order of derivation must be non-negative") if iaxis != axis: raise ValueError("The axis must be integer") - if not -c.ndim <= iaxis < c.ndim: - raise ValueError("The axis is out of range") - if iaxis < 0: - iaxis += c.ndim + iaxis = normalize_axis_index(iaxis, c.ndim) if cnt == 0: return c @@ -658,10 +656,7 @@ def polyint(c, m=1, k=[], lbnd=0, scl=1, axis=0): raise ValueError("Too many integration constants") if iaxis != axis: raise ValueError("The axis must be integer") - if not -c.ndim <= iaxis < c.ndim: - raise ValueError("The axis is out of range") - if iaxis < 0: - iaxis += c.ndim + iaxis = normalize_axis_index(iaxis, c.ndim) if cnt == 0: return c |