diff options
Diffstat (limited to 'numpy')
-rw-r--r-- | numpy/core/SConscript | 4 | ||||
-rw-r--r-- | numpy/core/setup.py | 2 | ||||
-rw-r--r-- | numpy/core/src/multiarray/boolean_ops.c.src | 380 | ||||
-rw-r--r-- | numpy/core/src/multiarray/boolean_ops.h | 9 | ||||
-rw-r--r-- | numpy/core/src/multiarray/item_selection.c | 13 | ||||
-rw-r--r-- | numpy/core/src/multiarray/methods.c | 50 | ||||
-rw-r--r-- | numpy/core/src/multiarray/reduction.c | 59 | ||||
-rw-r--r-- | numpy/core/src/multiarray/reduction.h | 6 | ||||
-rw-r--r-- | numpy/core/src/umath/ufunc_object.c | 3 | ||||
-rw-r--r-- | numpy/matrixlib/defmatrix.py | 27 |
10 files changed, 508 insertions, 45 deletions
diff --git a/numpy/core/SConscript b/numpy/core/SConscript index 7ed79fea3..918dc5b46 100644 --- a/numpy/core/SConscript +++ b/numpy/core/SConscript @@ -381,6 +381,8 @@ arraytypes_src = env.GenerateFromTemplate( pjoin('src', 'multiarray', 'arraytypes.c.src')) nditer_src = env.GenerateFromTemplate( pjoin('src', 'multiarray', 'nditer_templ.c.src')) +boolean_ops_src = env.GenerateFromTemplate( + pjoin('src', 'multiarray', 'boolean_ops.c.src')) lowlevel_strided_loops_src = env.GenerateFromTemplate( pjoin('src', 'multiarray', 'lowlevel_strided_loops.c.src')) einsum_src = env.GenerateFromTemplate(pjoin('src', 'multiarray', 'einsum.c.src')) @@ -446,6 +448,7 @@ if ENABLE_SEPARATE_COMPILATION: pjoin('src', 'multiarray', 'array_assign.c'), pjoin('src', 'multiarray', 'array_assign_scalar.c'), pjoin('src', 'multiarray', 'array_assign_array.c'), + pjoin('src', 'multiarray', 'boolean_ops.c'), pjoin('src', 'multiarray', 'datetime.c'), pjoin('src', 'multiarray', 'datetime_strings.c'), pjoin('src', 'multiarray', 'datetime_busday.c'), @@ -482,6 +485,7 @@ if ENABLE_SEPARATE_COMPILATION: multiarray_src.extend(arraytypes_src) multiarray_src.extend(scalartypes_src) multiarray_src.extend(lowlevel_strided_loops_src) + multiarray_src.extend(boolean_ops_src) multiarray_src.extend(nditer_src) multiarray_src.extend(einsum_src) if PYTHON_HAS_UNICODE_WIDE: diff --git a/numpy/core/setup.py b/numpy/core/setup.py index 4622d16bb..e8f40e806 100644 --- a/numpy/core/setup.py +++ b/numpy/core/setup.py @@ -688,6 +688,7 @@ def configuration(parent_package='',top_path=None): join(local_dir, subpath, 'arraytypes.c.src'), join(local_dir, subpath, 'nditer_templ.c.src'), join(local_dir, subpath, 'lowlevel_strided_loops.c.src'), + join(local_dir, subpath, 'boolean_ops.c.src'), join(local_dir, subpath, 'einsum.c.src')] # numpy.distutils generate .c from .c.src in weird directories, we have @@ -757,6 +758,7 @@ def configuration(parent_package='',top_path=None): join('src', 'multiarray', 'array_assign.c'), join('src', 'multiarray', 'array_assign_scalar.c'), join('src', 'multiarray', 'array_assign_array.c'), + join('src', 'multiarray', 'boolean_ops.c.src'), join('src', 'multiarray', 'buffer.c'), join('src', 'multiarray', 'calculation.c'), join('src', 'multiarray', 'common.c'), diff --git a/numpy/core/src/multiarray/boolean_ops.c.src b/numpy/core/src/multiarray/boolean_ops.c.src new file mode 100644 index 000000000..4641f2089 --- /dev/null +++ b/numpy/core/src/multiarray/boolean_ops.c.src @@ -0,0 +1,380 @@ +/* + * This file implements some boolean methods which have special NA + * interactions, including np.any and np.all. Could later gain + * np.logical_or and np.logical_and, as well as np.bitwise_or and + * np.bitwise_and because they are often used for boolean operations. + * + * NOTE: These functions assume that the input boolean data is valid, + * i.e. that each boolean is either 0 or 1. Any deviation from this + * may produce incorrect answers. + * + * Written by Mark Wiebe (mwwiebe@gmail.com) + * Copyright (c) 2011 by Enthought, Inc. + * + * See LICENSE.txt for the license. + */ + +#define PY_SSIZE_T_CLEAN +#include <Python.h> + +#define NPY_NO_DEPRECATED_API +#define _MULTIARRAYMODULE +#include <numpy/arrayobject.h> + +#include "npy_config.h" +#include "numpy/npy_3kcompat.h" + +#include "reduction.h" + +/* Typedef for the reduction inner loop */ +typedef void (reduce_inner_loop)(char **, npy_intp *, npy_intp); + +static int +assign_identity_any(PyArrayObject *result, int preservena, void *data) +{ + return PyArray_AssignZero(result, NULL, preservena, NULL); +} + +static void +any_inner_gen_gen(char **dataptr, npy_intp *strides, npy_intp count) +{ + char *data0 = dataptr[0], *data1 = dataptr[1]; + npy_intp stride0 = strides[0], stride1 = strides[1]; + npy_intp i; + + for (i = 0; i < count; ++i) { + *data0 |= *data1; + + data0 += stride0; + data1 += stride1; + } +} + +static void +any_inner_0stride_gen(char **dataptr, npy_intp *strides, npy_intp count) +{ + char *data0 = dataptr[0], *data1 = dataptr[1]; + npy_intp stride1 = strides[1]; + npy_intp i; + char value = *data0; + + for (i = 0; i < count && !value; ++i) { + value |= *data1; + + data1 += stride1; + } + + *(npy_bool *)data0 = value; +} + +static void +any_inner_0stride_contig(char **dataptr, npy_intp *strides, npy_intp count) +{ + char *data0 = dataptr[0], *data1 = dataptr[1]; + char value = *data0; + + if (count > 0 && !value && memchr(data1, 1, count) != NULL) { + *(npy_bool *)data0 = 1; + } +} + +static void +any_inner_contig_contig(char **dataptr, npy_intp *strides, npy_intp count) +{ + char *data0 = dataptr[0], *data1 = dataptr[1]; + npy_intp i; + + for (i = 0; i < count; ++i) { + *data0 |= *data1; + + ++data0; + ++data1; + } +} + +static int +reduce_any_loop(NpyIter *iter, + char **dataptr, + npy_intp *strides, + npy_intp *countptr, + NpyIter_IterNextFunc *iternext, + int needs_api, + npy_intp skip_first_count, + void *data) +{ + npy_intp fixed_strides[2]; + reduce_inner_loop *inner_loop; + NPY_BEGIN_THREADS_DEF; + + NpyIter_GetInnerFixedStrideArray(iter, fixed_strides); + + if (!needs_api) { + NPY_BEGIN_THREADS; + } + + /* Choose a loop specialized based on the strides */ + if (fixed_strides[0] == 0) { + if (fixed_strides[1] == 1) { + inner_loop = &any_inner_0stride_contig; + } + else { + inner_loop = &any_inner_0stride_gen; + } + } + else { + if (fixed_strides[0] == 1 && fixed_strides[1] == 1) { + inner_loop = &any_inner_contig_contig; + } + else { + inner_loop = &any_inner_gen_gen; + } + } + + /* + * 'skip_first_count' will always be 0 because we are doing a reduction + * with an identity. + */ + + do { + inner_loop(dataptr, strides, *countptr); + } while (iternext(iter)); + + if (!needs_api) { + NPY_END_THREADS; + } + + return (needs_api && PyErr_Occurred()) ? -1 : 0; +} + +static void +any_masked_inner_gen_gen_gen(char **dataptr, + npy_intp *strides, npy_intp count) +{ + char *data0 = dataptr[0], *data1 = dataptr[1], *data2 = dataptr[2]; + npy_intp stride0 = strides[0], stride1 = strides[1], stride2 = strides[2]; + npy_intp i; + + for (i = 0; i < count; ++i) { + *data0 |= *data1 & *data2; + + data0 += stride0; + data1 += stride1; + data2 += stride2; + } +} + +static void +any_masked_inner_0stride_gen_gen(char **dataptr, + npy_intp *strides, npy_intp count) +{ + char *data0 = dataptr[0], *data1 = dataptr[1], *data2 = dataptr[2]; + npy_intp stride1 = strides[1], stride2 = strides[2]; + npy_intp i; + char value = *data0; + + for (i = 0; i < count && !value; ++i) { + value |= *data1 & *data2; + + data1 += stride1; + data2 += stride2; + } + + *(npy_bool *)data0 = value; +} + +static void +any_masked_inner_0stride_gen_0stride(char **dataptr, + npy_intp *strides, npy_intp count) +{ + char *data0 = dataptr[0], *data1 = dataptr[1], *data2 = dataptr[2]; + npy_intp stride1 = strides[1]; + npy_intp i; + char value = *data0, maskvalue = *data2; + + for (i = 0; i < count && !value; ++i) { + value |= *data1 & maskvalue; + + data1 += stride1; + } + + *(npy_bool *)data0 = value; +} + +static int +reduce_any_masked_loop(NpyIter *iter, + char **dataptr, + npy_intp *strides, + npy_intp *countptr, + NpyIter_IterNextFunc *iternext, + int needs_api, + npy_intp skip_first_count, + void *data) +{ + npy_intp fixed_strides[3]; + reduce_inner_loop *inner_loop; + NPY_BEGIN_THREADS_DEF; + + NpyIter_GetInnerFixedStrideArray(iter, fixed_strides); + + if (!needs_api) { + NPY_BEGIN_THREADS; + } + + /* Choose a loop specialized based on the strides */ + if (fixed_strides[0] == 0) { + if (fixed_strides[2] == 0) { + inner_loop = &any_masked_inner_0stride_gen_0stride; + } + else { + inner_loop = &any_masked_inner_0stride_gen_gen; + } + } + else { + inner_loop = &any_masked_inner_gen_gen_gen; + } + + /* + * 'skip_first_count' will always be 0 because we are doing a reduction + * with an identity. + */ + + do { + inner_loop(dataptr, strides, *countptr); + } while (iternext(iter)); + + if (!needs_api) { + NPY_END_THREADS; + } + + return (needs_api && PyErr_Occurred()) ? -1 : 0; +} + +static void +any_adv_masked_inner_gen_gen_gen_gen(char **dataptr, + npy_intp *strides, npy_intp count) +{ + char *data0 = dataptr[0], *data1 = dataptr[1]; + char *data2 = dataptr[2], *data3 = dataptr[3]; + npy_intp stride0 = strides[0], stride1 = strides[1]; + npy_intp stride2 = strides[2], stride3 = strides[3]; + npy_intp i; + + for (i = 0; i < count; ++i) { + /* Normal case */ + if (*data2) { + *data0 |= *data1; + } + /* If the value is an exposed True, expose the result as well */ + else if (*data1 & *data3) { + *data0 = 1; + *data2 = 1; + } + + data0 += stride0; + data1 += stride1; + data2 += stride2; + data3 += stride3; + } +} + +static void +any_adv_masked_inner_0stride_gen_0stride_gen(char **dataptr, + npy_intp *strides, npy_intp count) +{ + char *data0 = dataptr[0], *data1 = dataptr[1]; + char *data2 = dataptr[2], *data3 = dataptr[3]; + npy_intp stride1 = strides[1], stride3 = strides[3]; + npy_intp i; + char maskvalue = *data2; + char value = maskvalue ? *data0 : 0; + + for (i = 0; i < count && !value; ++i) { + /* Normal case */ + if (maskvalue) { + value |= *data1; + } + /* If the value is an exposed True, expose the result as well */ + else if (*data1 & *data3) { + value = 1; + maskvalue = 1; + break; + } + + data1 += stride1; + data3 += stride3; + } + + if (maskvalue) { + *data0 = value; + *data2 = maskvalue; + } +} + +static int +reduce_any_advanced_masked_loop(NpyIter *iter, + char **dataptr, + npy_intp *strides, + npy_intp *countptr, + NpyIter_IterNextFunc *iternext, + int needs_api, + npy_intp skip_first_count, + void *data) +{ + npy_intp fixed_strides[4]; + reduce_inner_loop *inner_loop; + NPY_BEGIN_THREADS_DEF; + + NpyIter_GetInnerFixedStrideArray(iter, fixed_strides); + + if (!needs_api) { + NPY_BEGIN_THREADS; + } + + /* Choose a loop specialized based on the strides */ + if (fixed_strides[0] == 0 && fixed_strides[2] == 0) { + inner_loop = &any_adv_masked_inner_0stride_gen_0stride_gen; + } + else { + inner_loop = &any_adv_masked_inner_gen_gen_gen_gen; + } + + /* + * 'skip_first_count' will always be 0 because we are doing a reduction + * with an identity. + */ + + do { + inner_loop(dataptr, strides, *countptr); + } while (iternext(iter)); + + if (!needs_api) { + NPY_END_THREADS; + } + + return (needs_api && PyErr_Occurred()) ? -1 : 0; +} + +NPY_NO_EXPORT PyArrayObject * +PyArray_ReduceAny(PyArrayObject *arr, PyArrayObject *out, + npy_bool *axis_flags, int skipna, int keepdims) +{ + PyArrayObject *result; + PyArray_Descr *bool_dtype; + + bool_dtype = PyArray_DescrFromType(NPY_BOOL); + if (bool_dtype == NULL) { + return NULL; + } + + result = PyArray_ReduceWrapper(arr, out, NULL, + bool_dtype, bool_dtype, + NPY_UNSAFE_CASTING, + axis_flags, 1, skipna, NULL, keepdims, 1, + &assign_identity_any, + &reduce_any_loop, + &reduce_any_masked_loop, + &reduce_any_advanced_masked_loop, + NULL, 0, "any"); + Py_DECREF(bool_dtype); + return result; +} diff --git a/numpy/core/src/multiarray/boolean_ops.h b/numpy/core/src/multiarray/boolean_ops.h new file mode 100644 index 000000000..b3cf41a6d --- /dev/null +++ b/numpy/core/src/multiarray/boolean_ops.h @@ -0,0 +1,9 @@ +#ifndef _NPY_PRIVATE__BOOLEAN_OPS_H_ +#define _NPY_PRIVATE__BOOLEAN_OPS_H_ + +NPY_NO_EXPORT PyArrayObject * +PyArray_ReduceAny(PyArrayObject *arr, PyArrayObject *out, + npy_bool *axis_flags, int skipna, int keepdims); + + +#endif diff --git a/numpy/core/src/multiarray/item_selection.c b/numpy/core/src/multiarray/item_selection.c index 5d8b99489..11f506c8c 100644 --- a/numpy/core/src/multiarray/item_selection.c +++ b/numpy/core/src/multiarray/item_selection.c @@ -1923,6 +1923,11 @@ reduce_count_nonzero_loop(NpyIter *iter, NPY_BEGIN_THREADS; } + /* + * 'skip_first_count' will always be 0 because we are doing a reduction + * with an identity. + */ + do { char *data0 = dataptr[0], *data1 = dataptr[1]; npy_intp stride0 = strides[0], stride1 = strides[1]; @@ -1963,6 +1968,11 @@ reduce_count_nonzero_masked_loop(NpyIter *iter, NPY_BEGIN_THREADS; } + /* + * 'skip_first_count' will always be 0 because we are doing a reduction + * with an identity. + */ + do { char *data0 = dataptr[0], *data1 = dataptr[1], *data2 = dataptr[2]; npy_intp stride0 = strides[0], stride1 = strides[1], @@ -2018,10 +2028,11 @@ PyArray_ReduceCountNonzero(PyArrayObject *arr, PyArrayObject *out, result = PyArray_ReduceWrapper(arr, out, NULL, PyArray_DESCR(arr), dtype, NPY_SAME_KIND_CASTING, - axis_flags, 1, skipna, NULL, keepdims, + axis_flags, 1, skipna, NULL, keepdims, 0, &assign_reduce_identity_zero, &reduce_count_nonzero_loop, &reduce_count_nonzero_masked_loop, + NULL, nonzero, 0, "count_nonzero"); Py_DECREF(dtype); if (out == NULL && result != NULL) { diff --git a/numpy/core/src/multiarray/methods.c b/numpy/core/src/multiarray/methods.c index b99425969..eb57e6db4 100644 --- a/numpy/core/src/multiarray/methods.c +++ b/numpy/core/src/multiarray/methods.c @@ -20,6 +20,7 @@ #include "item_selection.h" #include "conversion_utils.h" #include "shape.h" +#include "boolean_ops.h" #include "methods.h" @@ -1993,51 +1994,60 @@ array_dot(PyArrayObject *self, PyObject *args, PyObject *kwds) static PyObject * -array_any(PyArrayObject *self, PyObject *args, PyObject *kwds) +array_any(PyArrayObject *array, PyObject *args, PyObject *kwds) { - int axis = MAX_DIMS; + static char *kwlist[] = {"axis", "out", "skipna", "keepdims", NULL}; + + PyObject *axis_in = NULL; PyArrayObject *out = NULL; - static char *kwlist[] = {"axis", "out", NULL}; + PyArrayObject *ret = NULL; + npy_bool axis_flags[NPY_MAXDIMS]; + int skipna = 0, keepdims = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O&O&", kwlist, - PyArray_AxisConverter, &axis, - PyArray_OutputConverter, &out)) + if (!PyArg_ParseTupleAndKeywords(args, kwds, + "|OO&ii:count_reduce_items", kwlist, + &axis_in, + &PyArray_OutputConverter, &out, + &skipna, + &keepdims)) { return NULL; + } + + if (PyArray_ConvertMultiAxis(axis_in, PyArray_NDIM(array), + axis_flags) != NPY_SUCCEED) { + Py_DECREF(array); + return NULL; + } - return PyArray_Any(self, axis, out); + ret = PyArray_ReduceAny(array, out, axis_flags, skipna, keepdims); + + if (out == NULL) { + return PyArray_Return(ret); + } + else { + return (PyObject *)ret; + } } static PyObject * array_all(PyArrayObject *self, PyObject *args, PyObject *kwds) { - int axis = MAX_DIMS; - PyArrayObject *out = NULL; - static char *kwlist[] = {"axis", "out", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O&O&", kwlist, - PyArray_AxisConverter, &axis, - PyArray_OutputConverter, &out)) - return NULL; - - return PyArray_All(self, axis, out); + NPY_FORWARD_NDARRAY_METHOD("_all"); } - static PyObject * array_stddev(PyArrayObject *self, PyObject *args, PyObject *kwds) { NPY_FORWARD_NDARRAY_METHOD("_std"); } - static PyObject * array_variance(PyArrayObject *self, PyObject *args, PyObject *kwds) { NPY_FORWARD_NDARRAY_METHOD("_var"); } - static PyObject * array_compress(PyArrayObject *self, PyObject *args, PyObject *kwds) { diff --git a/numpy/core/src/multiarray/reduction.c b/numpy/core/src/multiarray/reduction.c index d8978ad3b..b17e3ca6e 100644 --- a/numpy/core/src/multiarray/reduction.c +++ b/numpy/core/src/multiarray/reduction.c @@ -36,7 +36,7 @@ */ static PyArrayObject * allocate_reduce_result(PyArrayObject *arr, npy_bool *axis_flags, - PyArray_Descr *dtype) + PyArray_Descr *dtype, int subok) { npy_intp strides[NPY_MAXDIMS], stride; npy_intp shape[NPY_MAXDIMS], *arr_shape = PyArray_DIMS(arr); @@ -67,9 +67,10 @@ allocate_reduce_result(PyArrayObject *arr, npy_bool *axis_flags, } /* Finally, allocate the array */ - return (PyArrayObject *)PyArray_NewFromDescr(&PyArray_Type, dtype, - ndim, shape, strides, - NULL, 0, NULL); + return (PyArrayObject *)PyArray_NewFromDescr( + subok ? Py_TYPE(arr) : &PyArray_Type, + dtype, ndim, shape, strides, + NULL, 0, subok ? (PyObject *)arr : NULL); } /* @@ -201,6 +202,9 @@ conform_reduce_result(int ndim, npy_bool *axis_flags, * type conversion/validity check for 'out'. When 'need_namask' is true, * raises an exception if 'out' doesn't have an NA mask. * + * If 'subok' is true, creates a result with the subtype of 'operand', + * otherwise creates on with the base ndarray class. + * * If 'out' is NULL, it allocates a new array whose shape matches * that of 'operand', except for at the reduction axes. An NA mask * is added if 'need_namask' is true. If 'dtype' is NULL, the dtype @@ -209,13 +213,14 @@ conform_reduce_result(int ndim, npy_bool *axis_flags, NPY_NO_EXPORT PyArrayObject * PyArray_CreateReduceResult(PyArrayObject *operand, PyArrayObject *out, PyArray_Descr *dtype, npy_bool *axis_flags, - int need_namask, int keepdims, const char *funcname) + int need_namask, int keepdims, int subok, + const char *funcname) { PyArrayObject *result; if (out == NULL) { /* This function steals the reference to 'dtype' */ - result = allocate_reduce_result(operand, axis_flags, dtype); + result = allocate_reduce_result(operand, axis_flags, dtype, subok); /* Allocate an NA mask if necessary */ if (need_namask && result != NULL) { @@ -761,11 +766,20 @@ PyArray_InitializeReduceResult( * breaking API compatibility. Pass in NULL. * keepdims : If true, leaves the reduction dimensions in the result * with size one. + * subok : If true, the result uses the subclass of operand, otherwise + * it is always a base class ndarray. * assign_identity : If NULL, PyArray_InitializeReduceResult is used, otherwise * this function is called to initialize the result to * the reduction's unit. * loop : The loop which does the reduction. * masked_loop : The loop which does the reduction with a mask. + * advanced_masked_loop: If non-NULL, this is a loop which uses a mask from + * both the operand and the result. The 'result' is + * initialized to a usual reduction of the operand's mask, + * but both the operand's mask and the result's mask + * are provided so that the loop may decide to expose + * elements, which normally would not be exposed by the + * normal NA propagation rules, based on the input data. * data : Data which is passed to assign_identity and the inner loop. * buffersize : Buffer size for the iterator. For the default, pass in 0. * funcname : The name of the reduction function, for error messages. @@ -778,9 +792,11 @@ PyArray_ReduceWrapper(PyArrayObject *operand, PyArrayObject *out, NPY_CASTING casting, npy_bool *axis_flags, int reorderable, int skipna, npy_bool *skipwhichna, int keepdims, + int subok, PyArray_AssignReduceIdentityFunc *assign_identity, PyArray_ReduceLoopFunc *loop, PyArray_ReduceLoopFunc *masked_loop, + PyArray_ReduceLoopFunc *advanced_masked_loop, void *data, npy_intp buffersize, const char *funcname) { int use_maskna; @@ -836,7 +852,7 @@ PyArray_ReduceWrapper(PyArrayObject *operand, PyArrayObject *out, Py_INCREF(result_dtype); result = PyArray_CreateReduceResult(operand, out, result_dtype, axis_flags, !skipna && use_maskna, - keepdims, funcname); + keepdims, subok, funcname); if (result == NULL) { goto fail; } @@ -851,8 +867,12 @@ PyArray_ReduceWrapper(PyArrayObject *operand, PyArrayObject *out, goto fail; } - /* Short circuit any calculation if the result is 0-dim NA */ - if (PyArray_SIZE(result) == 1 && + /* + * Short circuit any calculation if the result is a 0-dim NA + * and the advanced masked loop which could expose it isn't + * provided. + */ + if (advanced_masked_loop == NULL && PyArray_SIZE(result) == 1 && !NpyMaskValue_IsExposed( (npy_mask)*PyArray_MASKNA_DATA(result))) { goto finish; @@ -923,8 +943,14 @@ PyArray_ReduceWrapper(PyArrayObject *operand, PyArrayObject *out, else { /* Iterate over the output's mask */ op_flags[0] |= NPY_ITER_USE_MASKNA; - /* The input's mask is already incorporated in the output's mask */ - op_flags[1] |= NPY_ITER_IGNORE_MASKNA; + if (advanced_masked_loop == NULL) { + /* Input's mask is already incorporated in the output's mask */ + op_flags[1] |= NPY_ITER_IGNORE_MASKNA; + } + else { + /* The reduction wants to use the operand's mask as well */ + op_flags[1] |= NPY_ITER_USE_MASKNA; + } } } else { @@ -977,7 +1003,14 @@ PyArray_ReduceWrapper(PyArrayObject *operand, PyArrayObject *out, goto fail; } } - /* Masked reduction */ + /* Masked reduction with both masks */ + else if (!skipna && advanced_masked_loop != NULL) { + if (advanced_masked_loop(iter, dataptr, strideptr, countptr, + iternext, needs_api, skip_first_count, data) < 0) { + goto fail; + } + } + /* Regular masked reduction with just one mask */ else { if (masked_loop == NULL) { PyErr_Format(PyExc_RuntimeError, @@ -1099,7 +1132,7 @@ PyArray_CountReduceItems(PyArrayObject *operand, } result = PyArray_CreateReduceResult(operand, NULL, result_dtype, axis_flags, 0, - keepdims, "count_reduce_items"); + keepdims, 0, "count_reduce_items"); if (result == NULL) { return NULL; } diff --git a/numpy/core/src/multiarray/reduction.h b/numpy/core/src/multiarray/reduction.h index 95193ad28..268fdaf29 100644 --- a/numpy/core/src/multiarray/reduction.h +++ b/numpy/core/src/multiarray/reduction.h @@ -93,6 +93,9 @@ PyArray_InitializeReduceResult( * type conversion/validity check for 'out'. When 'need_namask' is true, * raises an exception if 'out' doesn't have an NA mask. * + * If 'subok' is true, creates a result with the subtype of 'operand', + * otherwise creates on with the base ndarray class. + * * If 'out' is NULL, it allocates a new array whose shape matches * that of 'operand', except for at the reduction axes. An NA mask * is added if 'need_namask' is true. If 'dtype' is NULL, the dtype @@ -101,6 +104,7 @@ PyArray_InitializeReduceResult( NPY_NO_EXPORT PyArrayObject * PyArray_CreateReduceResult(PyArrayObject *operand, PyArrayObject *out, PyArray_Descr *dtype, npy_bool *axis_flags, - int need_namask, int keepdims, const char *funcname); + int need_namask, int keepdims, int subok, + const char *funcname); #endif diff --git a/numpy/core/src/umath/ufunc_object.c b/numpy/core/src/umath/ufunc_object.c index ae82f6130..ae8169166 100644 --- a/numpy/core/src/umath/ufunc_object.c +++ b/numpy/core/src/umath/ufunc_object.c @@ -2862,10 +2862,11 @@ PyUFunc_Reduce(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *out, result = PyArray_ReduceWrapper(arr, out, NULL, dtype, dtype, NPY_UNSAFE_CASTING, axis_flags, reorderable, - skipna, NULL, keepdims, + skipna, NULL, keepdims, 0, assign_identity, reduce_loop, masked_reduce_loop, + NULL, ufunc, buffersize, ufunc_name); Py_DECREF(dtype); diff --git a/numpy/matrixlib/defmatrix.py b/numpy/matrixlib/defmatrix.py index aac123e59..dbf2909fd 100644 --- a/numpy/matrixlib/defmatrix.py +++ b/numpy/matrixlib/defmatrix.py @@ -375,6 +375,15 @@ class matrix(N.ndarray): else: raise ValueError("unsupported axis") + def _collapse(self, axis): + """A convenience function for operations that want to collapse + to a scalar like _align, but are using keepdims=True + """ + if axis is None: + return self[0,0] + else: + return self + # Necessary because base-class tolist expects dimension # reduction by x[0] def tolist(self): @@ -432,7 +441,7 @@ class matrix(N.ndarray): [ 7.]]) """ - return N.ndarray.sum(self, axis, dtype, out)._align(axis) + return N.ndarray.sum(self, axis, dtype, out, keepdims=True)._collapse(axis) def mean(self, axis=None, dtype=None, out=None): """ @@ -466,7 +475,7 @@ class matrix(N.ndarray): [ 9.5]]) """ - return N.ndarray.mean(self, axis, dtype, out)._align(axis) + return N.ndarray.mean(self, axis, dtype, out, keepdims=True)._collapse(axis) def std(self, axis=None, dtype=None, out=None, ddof=0): """ @@ -500,7 +509,7 @@ class matrix(N.ndarray): [ 1.11803399]]) """ - return N.ndarray.std(self, axis, dtype, out, ddof)._align(axis) + return N.ndarray.std(self, axis, dtype, out, ddof, keepdims=True)._collapse(axis) def var(self, axis=None, dtype=None, out=None, ddof=0): """ @@ -534,7 +543,7 @@ class matrix(N.ndarray): [ 1.25]]) """ - return N.ndarray.var(self, axis, dtype, out, ddof)._align(axis) + return N.ndarray.var(self, axis, dtype, out, ddof, keepdims=True)._collapse(axis) def prod(self, axis=None, dtype=None, out=None): """ @@ -567,7 +576,7 @@ class matrix(N.ndarray): [7920]]) """ - return N.ndarray.prod(self, axis, dtype, out)._align(axis) + return N.ndarray.prod(self, axis, dtype, out, keepdims=True)._collapse(axis) def any(self, axis=None, out=None): """ @@ -590,7 +599,7 @@ class matrix(N.ndarray): returns `ndarray` """ - return N.ndarray.any(self, axis, out)._align(axis) + return N.ndarray.any(self, axis, out, keepdims=True)._collapse(axis) def all(self, axis=None, out=None): """ @@ -630,7 +639,7 @@ class matrix(N.ndarray): [False]], dtype=bool) """ - return N.ndarray.all(self, axis, out)._align(axis) + return N.ndarray.all(self, axis, out, keepdims=True)._collapse(axis) def max(self, axis=None, out=None): """ @@ -665,7 +674,7 @@ class matrix(N.ndarray): [11]]) """ - return N.ndarray.max(self, axis, out)._align(axis) + return N.ndarray.max(self, axis, out, keepdims=True)._collapse(axis) def argmax(self, axis=None, out=None): """ @@ -735,7 +744,7 @@ class matrix(N.ndarray): [-11]]) """ - return N.ndarray.min(self, axis, out)._align(axis) + return N.ndarray.min(self, axis, out, keepdims=True)._collapse(axis) def argmin(self, axis=None, out=None): """ |