summaryrefslogtreecommitdiff
path: root/numpy
diff options
context:
space:
mode:
authorMark Wiebe <mwwiebe@gmail.com>2011-08-26 10:22:20 -0700
committerCharles Harris <charlesr.harris@gmail.com>2011-08-27 07:27:02 -0600
commit858ee47017be6f18b3e3c557f3fc9f7cc02bfe93 (patch)
tree8b3cc3b63513f89142752373356f8ec6697f5e91 /numpy
parent99774bee1e138bbe2c52a4cc6f54975279f436e3 (diff)
downloadnumpy-858ee47017be6f18b3e3c557f3fc9f7cc02bfe93.tar.gz
ENH: nditer: Change the Python nditer exposure to automatically add NPY_ITER_USE_MASKNA
Diffstat (limited to 'numpy')
-rw-r--r--numpy/core/src/multiarray/nditer_pywrap.c34
-rw-r--r--numpy/core/tests/test_nditer.py21
2 files changed, 50 insertions, 5 deletions
diff --git a/numpy/core/src/multiarray/nditer_pywrap.c b/numpy/core/src/multiarray/nditer_pywrap.c
index 2e86e4753..e5061c999 100644
--- a/numpy/core/src/multiarray/nditer_pywrap.c
+++ b/numpy/core/src/multiarray/nditer_pywrap.c
@@ -613,6 +613,7 @@ npyiter_convert_ops(PyObject *op_in, PyObject *op_flags_in,
int *nop_out)
{
int iop, nop;
+ int any_maskna;
/* nop and op */
if (PyTuple_Check(op_in) || PyList_Check(op_in)) {
@@ -666,11 +667,6 @@ npyiter_convert_ops(PyObject *op_in, PyObject *op_flags_in,
}
else {
op_flags[iop] = NPY_ITER_READONLY;
-
- /* Enable MASKNA iteration if the op needs it */
- if (PyArray_HASMASKNA(op[iop])) {
- op_flags[iop] |= NPY_ITER_USE_MASKNA;
- }
}
}
}
@@ -713,6 +709,33 @@ npyiter_convert_ops(PyObject *op_in, PyObject *op_flags_in,
}
}
+ /*
+ * Because the Python exposure of nditer knows how to deal with
+ * NA-masked arrays, we automatically add NPY_ITER_USE_MASKNA
+ * flags for convenience.
+ */
+ any_maskna = 0;
+ for (iop = 0; iop < nop; ++iop) {
+ /* Enable MASKNA iteration if the op needs it */
+ if (op[iop] != NULL && PyArray_HASMASKNA(op[iop])) {
+ op_flags[iop] |= NPY_ITER_USE_MASKNA;
+ any_maskna = 1;
+ }
+ }
+ /*
+ * If any operands had an NA-mask, add it to the 'allocate' ones too.
+ * This causes the Python exposure nditer to have slightly less control
+ * than the C NpyIter usage, but is generally going to be what people
+ * want.
+ */
+ if (any_maskna) {
+ for (iop = 0; iop < nop; ++iop) {
+ if (op[iop] == NULL) {
+ op_flags[iop] |= NPY_ITER_USE_MASKNA;
+ }
+ }
+ }
+
return 1;
}
@@ -808,6 +831,7 @@ npyiter_init(NewNpyArrayIterObject *self, PyObject *args, PyObject *kwds)
itershape.ptr = NULL;
}
+
self->iter = NpyIter_AdvancedNew(nop, op, flags, order, casting, op_flags,
op_request_dtypes,
oa_ndim, oa_ndim > 0 ? op_axes : NULL,
diff --git a/numpy/core/tests/test_nditer.py b/numpy/core/tests/test_nditer.py
index b13adf6d6..b0badc2d9 100644
--- a/numpy/core/tests/test_nditer.py
+++ b/numpy/core/tests/test_nditer.py
@@ -2533,5 +2533,26 @@ def test_iter_maskna():
['readonly','use_maskna'],
['readonly','arraymask']])
+def test_iter_maskna_default_use_maskna():
+ # The Python exposure of nditer adds the USE_MASKNA flag automatically
+ a = np.array([3, 5, np.NA, 2, 1])
+ b = np.array([1, 1.0, 4.5, 2, 0])
+
+ # The output should automatically get an NA mask
+ it = np.nditer([a,b,None])
+ for x,y,z in it:
+ z[...] = x+y
+ assert_(it.operands[2].flags.maskna)
+ assert_array_equal(it.operands[2], a+b)
+
+ # This holds even when we specify the op_flags
+ it = np.nditer([a,b.copy(),None], op_flags=[['readonly'],
+ ['readwrite'], ['writeonly', 'allocate']])
+ for x,y,z in it:
+ y[...] = y[...] + 1
+ z[...] = x+y
+ assert_(it.operands[2].flags.maskna)
+ assert_array_equal(it.operands[2], a+b+1)
+
if __name__ == "__main__":
run_module_suite()