Skip to content

Commit

Permalink
ENH: nditer: Change the Python nditer exposure to automatically add N…
Browse files Browse the repository at this point in the history
…PY_ITER_USE_MASKNA
  • Loading branch information
mwiebe authored and charris committed Aug 27, 2011
1 parent 99774be commit 858ee47
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 5 deletions.
34 changes: 29 additions & 5 deletions numpy/core/src/multiarray/nditer_pywrap.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down Expand Up @@ -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;
}
}
}
}
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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,
Expand Down
21 changes: 21 additions & 0 deletions numpy/core/tests/test_nditer.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()

0 comments on commit 858ee47

Please sign in to comment.