summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/neps/missing-data.rst16
-rw-r--r--numpy/core/arrayprint.py2
-rw-r--r--numpy/core/include/numpy/ndarraytypes.h8
-rw-r--r--numpy/core/src/multiarray/arrayobject.c42
-rw-r--r--numpy/core/src/multiarray/ctors.c56
-rw-r--r--numpy/core/src/multiarray/ctors.h16
-rw-r--r--numpy/core/src/multiarray/flagsobject.c27
-rw-r--r--numpy/core/src/multiarray/getset.c4
-rw-r--r--numpy/core/src/multiarray/mapping.c169
-rw-r--r--numpy/core/src/multiarray/methods.c3
-rw-r--r--numpy/core/src/multiarray/na_mask.c64
-rw-r--r--numpy/core/src/multiarray/na_singleton.c124
-rw-r--r--numpy/core/src/multiarray/na_singleton.h19
-rw-r--r--numpy/core/src/multiarray/scalarapi.c14
-rw-r--r--numpy/core/src/multiarray/shape.c3
-rw-r--r--numpy/core/tests/test_na.py20
16 files changed, 480 insertions, 107 deletions
diff --git a/doc/neps/missing-data.rst b/doc/neps/missing-data.rst
index 6aedbadc7..038ea9370 100644
--- a/doc/neps/missing-data.rst
+++ b/doc/neps/missing-data.rst
@@ -823,7 +823,7 @@ This gives us the following additions to the PyArrayObject::
* If no mask: NULL
* If mask : bool/uint8/structured dtype of mask dtypes
*/
- PyArray_Descr *maskna_descr;
+ PyArray_Descr *maskna_dtype;
/*
* Raw data buffer for mask. If the array has the flag
* NPY_ARRAY_OWNMASKNA enabled, it owns this memory and
@@ -840,7 +840,7 @@ This gives us the following additions to the PyArrayObject::
These fields can be accessed through the inline functions::
PyArray_Descr *
- PyArray_MASKNA_DESCR(PyArrayObject *arr);
+ PyArray_MASKNA_DTYPE(PyArrayObject *arr);
npy_mask *
PyArray_MASKNA_DATA(PyArrayObject *arr);
@@ -851,8 +851,10 @@ These fields can be accessed through the inline functions::
npy_bool
PyArray_HASMASKNA(PyArrayObject *arr);
-There are 1 or 2 flags which must be added to the array flags::
+There are 2 or 3 flags which must be added to the array flags, both
+for requesting NA masks and for testing for them::
+ NPY_ARRAY_MASKNA
NPY_ARRAY_OWNMASKNA
/* To possibly add in a later revision */
NPY_ARRAY_HARDMASKNA
@@ -873,6 +875,10 @@ PyArray_ContainsNA(PyArrayObject* obj)
true if the array has NA support AND there is an
NA anywhere in the array.
+int PyArray_AllocateMaskNA(PyArrayObject* arr, npy_bool ownmaskna, npy_bool multina)
+ Allocates an NA mask for the array, ensuring ownership if requested
+ and using NPY_MASK instead of NPY_BOOL for the dtype if multina is True.
+
Mask Binary Format
==================
@@ -968,7 +974,7 @@ We add several new per-operand flags:
NPY_ITER_USE_MASKNA
If the operand has an NA dtype, an NA mask, or both, this adds a new
virtual operand to the end of the operand list which iterates
- over the mask of the particular operand.
+ over the mask for the particular operand.
NPY_ITER_IGNORE_MASKNA
If an operand has an NA mask, by default the iterator will raise
@@ -1002,7 +1008,7 @@ to 12.5% overhead for a separately kept mask.
Acknowledgments
***************
-In addition to feedback Travis Oliphant and others at Enthought,
+In addition to feedback from Travis Oliphant and others at Enthought,
this NEP has been revised based on a great deal of feedback from
the NumPy-Discussion mailing list. The people participating in
the discussion are::
diff --git a/numpy/core/arrayprint.py b/numpy/core/arrayprint.py
index 508056a26..8f82ddcd7 100644
--- a/numpy/core/arrayprint.py
+++ b/numpy/core/arrayprint.py
@@ -477,6 +477,8 @@ def _formatArray(a, format_function, rank, max_line_len,
s, line = _extendLine(s, line, summary_insert1, max_line_len, next_line_prefix)
for i in xrange(trailing_items, 1, -1):
+ print "bad index: ", i
+ print "length: ", len(a)
word = format_function(a[-i]) + separator
s, line = _extendLine(s, line, word, max_line_len, next_line_prefix)
diff --git a/numpy/core/include/numpy/ndarraytypes.h b/numpy/core/include/numpy/ndarraytypes.h
index 1b3d0b2a1..1d500d777 100644
--- a/numpy/core/include/numpy/ndarraytypes.h
+++ b/numpy/core/include/numpy/ndarraytypes.h
@@ -668,7 +668,7 @@ typedef struct tagPyArrayObject_fieldaccess {
* NPY_ARRAY_OWNMASKNA enabled, it owns this memory and
* must call PyArray_free on it when destroyed.
*/
- npy_mask *maskna_data;
+ char *maskna_data;
/*
* Just like dimensions and strides point into the same memory
* buffer, we now just make that buffer 3x the nd instead of 2x
@@ -1056,6 +1056,8 @@ typedef void (NpyIter_GetMultiIndexFunc)(NpyIter *iter,
#define NPY_ITER_WRITEMASKED 0x10000000
/* This array is the mask for all WRITEMASKED operands */
#define NPY_ITER_ARRAYMASK 0x20000000
+/* Split this operand up into data and an NA mask */
+#define NPY_ITER_USE_MASKNA 0x40000000
#define NPY_ITER_GLOBAL_FLAGS 0x0000ffff
#define NPY_ITER_PER_OP_FLAGS 0xffff0000
@@ -1572,7 +1574,7 @@ PyArray_MASKNA_DTYPE(PyArrayObject *arr)
return ((PyArrayObject_fieldaccess *)arr)->maskna_dtype;
}
-static NPY_INLINE npy_mask *
+static NPY_INLINE char *
PyArray_MASKNA_DATA(PyArrayObject *arr)
{
return ((PyArrayObject_fieldaccess *)arr)->maskna_data;
@@ -1588,7 +1590,7 @@ PyArray_MASKNA_STRIDES(PyArrayObject *arr)
static NPY_INLINE npy_bool
PyArray_HASMASKNA(PyArrayObject *arr)
{
- return ((PyArrayObject_fieldaccess *)arr)->maskna_data != NULL;
+ return (((PyArrayObject_fieldaccess *)arr)->flags & NPY_ARRAY_MASKNA) != 0;
}
diff --git a/numpy/core/src/multiarray/arrayobject.c b/numpy/core/src/multiarray/arrayobject.c
index e7ed8ba02..987826512 100644
--- a/numpy/core/src/multiarray/arrayobject.c
+++ b/numpy/core/src/multiarray/arrayobject.c
@@ -50,6 +50,7 @@ maintainer email: oliphant.travis@ieee.org
#include "getset.h"
#include "sequence.h"
#include "buffer.h"
+#include "na_mask.h"
/*NUMPY_API
Compute the size of an array (in number of items)
@@ -120,7 +121,7 @@ PyArray_SetBaseObject(PyArrayObject *arr, PyObject *obj)
NPY_NO_EXPORT int
PyArray_CopyObject(PyArrayObject *dest, PyObject *src_object)
{
- int ret;
+ int ret, contains_na = 0;
PyArrayObject *src;
PyArray_Descr *dtype = NULL;
int ndim = 0;
@@ -155,8 +156,17 @@ PyArray_CopyObject(PyArrayObject *dest, PyObject *src_object)
* Get either an array object we can copy from, or its parameters
* if there isn't a convenient array available.
*/
- if (PyArray_GetArrayParamsFromObject(src_object, PyArray_DESCR(dest),
- 0, &dtype, &ndim, dims, &src, NULL) < 0) {
+ if (PyArray_GetArrayParamsFromObjectEx(src_object, PyArray_DESCR(dest),
+ 0, &dtype, &ndim, dims, &contains_na, &src, NULL) < 0) {
+ Py_DECREF(src_object);
+ return -1;
+ }
+
+ if (contains_na && !(PyArray_HasNASupport(dest) ||
+ PyArray_DESCR(dest)->type_num == NPY_OBJECT)) {
+ PyErr_SetString(PyExc_ValueError,
+ "Cannot set NumPy array values to NA values without first "
+ "enabling NA support in the array");
Py_DECREF(src_object);
return -1;
}
@@ -173,11 +183,27 @@ PyArray_CopyObject(PyArrayObject *dest, PyObject *src_object)
return -1;
}
}
+ /* Assigning NA affects the mask if it exists */
+ else if (PyArray_HasNASupport(dest) && NpyNA_Check(src_object)) {
+ if (PyArray_AssignNA(dest, (NpyNA *)src_object) < 0) {
+ Py_DECREF(src_object);
+ return -1;
+ }
+
+ Py_DECREF(src_object);
+ return 0;
+ }
+ /* Otherwise use the dtype's setitem function */
else {
if (PyArray_SIZE(dest) == 1) {
Py_DECREF(dtype);
- return PyArray_DESCR(dest)->f->setitem(src_object,
- PyArray_DATA(dest), dest);
+ ret = PyArray_DESCR(dest)->f->setitem(src_object,
+ PyArray_DATA(dest), dest);
+ /* Unmask the value if necessary */
+ if (ret == 0 && PyArray_HASMASKNA(dest)) {
+ PyArray_MASKNA_DATA(dest)[0] = 1;
+ }
+ return ret;
}
else {
src = (PyArrayObject *)PyArray_NewFromDescr(&PyArray_Type,
@@ -216,6 +242,12 @@ PyArray_CopyObject(PyArrayObject *dest, PyObject *src_object)
Py_DECREF(src_object);
return -1;
}
+ if (PyArray_HASMASKNA(dest)) {
+ if (PyArray_AllocateMaskNA(dest, 0, 0) < 0) {
+ Py_DECREF(src_object);
+ return -1;
+ }
+ }
if (PyArray_AssignFromSequence(src, src_object) < 0) {
Py_DECREF(src);
Py_DECREF(src_object);
diff --git a/numpy/core/src/multiarray/ctors.c b/numpy/core/src/multiarray/ctors.c
index 7d2c0c488..a07a31f22 100644
--- a/numpy/core/src/multiarray/ctors.c
+++ b/numpy/core/src/multiarray/ctors.c
@@ -24,6 +24,7 @@
#include "methods.h"
#include "_datetime.h"
#include "datetime_strings.h"
+#include "na_singleton.h"
/*
* Reading from a file or a string.
@@ -595,7 +596,8 @@ PyArray_MaskedMoveInto(PyArrayObject *dst, PyArrayObject *src,
/* adapted from Numarray */
static int
-setArrayFromSequence(PyArrayObject *a, PyObject *s, int dim, npy_intp offset)
+setArrayFromSequence(PyArrayObject *a, PyObject *s,
+ int dim, npy_intp offset, npy_intp maskoffset)
{
Py_ssize_t i, slen;
int res = -1;
@@ -645,25 +647,45 @@ setArrayFromSequence(PyArrayObject *a, PyObject *s, int dim, npy_intp offset)
/* Broadcast the one element from the sequence to all the outputs */
if (slen == 1) {
PyObject *o;
- npy_intp alen = PyArray_DIMS(a)[dim];
+ NpyNA *na = NULL;
+ char maskvalue = 0;
+ npy_intp alen = PyArray_DIM(a, dim);
o = PySequence_GetItem(s, 0);
if (o == NULL) {
goto fail;
}
+
+ /* Check if the value being assigned is NA */
+ if (PyArray_HASMASKNA(a)) {
+ na = NpyNA_FromObject(o, 1);
+ if (na != NULL) {
+ maskvalue = (char)NpyNA_AsMaskValue(na);
+ }
+ }
+
for (i = 0; i < alen; i++) {
if ((PyArray_NDIM(a) - dim) > 1) {
- res = setArrayFromSequence(a, o, dim+1, offset);
+ res = setArrayFromSequence(a, o, dim+1, offset, maskoffset);
}
else {
- res = PyArray_DESCR(a)->f->setitem(o, (PyArray_DATA(a) + offset), a);
+ if (na == NULL) {
+ res = PyArray_DESCR(a)->f->setitem(o,
+ (PyArray_DATA(a) + offset), a);
+ }
+ else {
+ *(PyArray_MASKNA_DATA(a) + maskoffset) = maskvalue;
+ }
}
if (res < 0) {
Py_DECREF(o);
+ Py_XDECREF(na);
goto fail;
}
offset += PyArray_STRIDES(a)[dim];
+ maskoffset += PyArray_MASKNA_STRIDES(a)[dim];
}
+ Py_XDECREF(na);
Py_DECREF(o);
}
/* Copy element by element */
@@ -674,16 +696,34 @@ setArrayFromSequence(PyArrayObject *a, PyObject *s, int dim, npy_intp offset)
goto fail;
}
if ((PyArray_NDIM(a) - dim) > 1) {
- res = setArrayFromSequence(a, o, dim+1, offset);
+ res = setArrayFromSequence(a, o, dim+1, offset, maskoffset);
}
else {
- res = PyArray_DESCR(a)->f->setitem(o, (PyArray_DATA(a) + offset), a);
+ NpyNA *na = NULL;
+ char maskvalue = 0;
+
+ /* Check if the value being assigned is NA */
+ if (PyArray_HASMASKNA(a)) {
+ na = NpyNA_FromObject(o, 1);
+ if (na != NULL) {
+ maskvalue = (char)NpyNA_AsMaskValue(na);
+ }
+ }
+
+ if (na == NULL) {
+ res = PyArray_DESCR(a)->f->setitem(o,
+ (PyArray_DATA(a) + offset), a);
+ }
+ else {
+ *(PyArray_MASKNA_DATA(a) + maskoffset) = maskvalue;
+ }
}
Py_DECREF(o);
if (res < 0) {
goto fail;
}
offset += PyArray_STRIDES(a)[dim];
+ maskoffset += PyArray_MASKNA_STRIDES(a)[dim];
}
}
@@ -708,7 +748,7 @@ PyArray_AssignFromSequence(PyArrayObject *self, PyObject *v)
"assignment to 0-d array");
return -1;
}
- return setArrayFromSequence(self, v, 0, 0);
+ return setArrayFromSequence(self, v, 0, 0, 0);
}
/*
@@ -1147,6 +1187,8 @@ PyArray_NewFromDescr(PyTypeObject *subtype, PyArray_Descr *descr, int nd,
fa->descr = descr;
fa->base = (PyObject *)NULL;
fa->weakreflist = (PyObject *)NULL;
+ fa->maskna_dtype = NULL;
+ fa->maskna_data = NULL;
if (nd > 0) {
fa->dimensions = PyDimMem_NEW(3*nd);
diff --git a/numpy/core/src/multiarray/ctors.h b/numpy/core/src/multiarray/ctors.h
index ed7b72980..3745c439c 100644
--- a/numpy/core/src/multiarray/ctors.h
+++ b/numpy/core/src/multiarray/ctors.h
@@ -72,4 +72,20 @@ byte_swap_vector(void *p, intp n, int size);
NPY_NO_EXPORT int
PyArray_AssignFromSequence(PyArrayObject *self, PyObject *v);
+/*
+ * A slight generalization of PyArray_GetArrayParamsFromObject,
+ * which also returns whether the input data contains any numpy.NA
+ * values.
+ *
+ * This isn't exposed in the public API.
+ */
+NPY_NO_EXPORT int
+PyArray_GetArrayParamsFromObjectEx(PyObject *op,
+ PyArray_Descr *requested_dtype,
+ npy_bool writeable,
+ PyArray_Descr **out_dtype,
+ int *out_ndim, npy_intp *out_dims,
+ int *out_contains_na,
+ PyArrayObject **out_arr, PyObject *context);
+
#endif
diff --git a/numpy/core/src/multiarray/flagsobject.c b/numpy/core/src/multiarray/flagsobject.c
index 72acfd6ee..30fcc6066 100644
--- a/numpy/core/src/multiarray/flagsobject.c
+++ b/numpy/core/src/multiarray/flagsobject.c
@@ -283,7 +283,7 @@ arrayflags_maskna_set(PyArrayFlagsObject *self, PyObject *obj)
}
if (PyObject_IsTrue(obj)) {
- return PyArray_AllocateMaskNA(self->arr, 0, 0);
+ return PyArray_AllocateMaskNA((PyArrayObject *)self->arr, 0, 0);
}
else {
if (self->flags & NPY_ARRAY_MASKNA) {
@@ -320,7 +320,7 @@ arrayflags_ownmaskna_set(PyArrayFlagsObject *self, PyObject *obj)
}
if (PyObject_IsTrue(obj)) {
- return PyArray_AllocateMaskNA(self->arr, 1, 0);
+ return PyArray_AllocateMaskNA((PyArrayObject *)self->arr, 1, 0);
}
else {
if (self->flags & NPY_ARRAY_OWNMASKNA) {
@@ -644,16 +644,19 @@ arrayflags_print(PyArrayFlagsObject *self)
{
int fl = self->flags;
- return PyUString_FromFormat(" %s : %s\n %s : %s\n %s : %s\n"\
- " %s : %s\n %s : %s\n %s : %s",
- "C_CONTIGUOUS", _torf_(fl, NPY_ARRAY_C_CONTIGUOUS),
- "F_CONTIGUOUS", _torf_(fl, NPY_ARRAY_F_CONTIGUOUS),
- "OWNDATA", _torf_(fl, NPY_ARRAY_OWNDATA),
- "MASKNA", _torf_(fl, NPY_ARRAY_MASKNA),
- "OWNMASKNA", _torf_(fl, NPY_ARRAY_OWNMASKNA),
- "WRITEABLE", _torf_(fl, NPY_ARRAY_WRITEABLE),
- "ALIGNED", _torf_(fl, NPY_ARRAY_ALIGNED),
- "UPDATEIFCOPY", _torf_(fl, NPY_ARRAY_UPDATEIFCOPY));
+ return PyUString_FromFormat(
+ " %s : %s\n %s : %s\n"
+ " %s : %s\n %s : %s\n"
+ " %s : %s\n %s : %s\n"
+ " %s : %s\n %s : %s",
+ "C_CONTIGUOUS", _torf_(fl, NPY_ARRAY_C_CONTIGUOUS),
+ "F_CONTIGUOUS", _torf_(fl, NPY_ARRAY_F_CONTIGUOUS),
+ "OWNDATA", _torf_(fl, NPY_ARRAY_OWNDATA),
+ "MASKNA", _torf_(fl, NPY_ARRAY_MASKNA),
+ "OWNMASKNA", _torf_(fl, NPY_ARRAY_OWNMASKNA),
+ "WRITEABLE", _torf_(fl, NPY_ARRAY_WRITEABLE),
+ "ALIGNED", _torf_(fl, NPY_ARRAY_ALIGNED),
+ "UPDATEIFCOPY", _torf_(fl, NPY_ARRAY_UPDATEIFCOPY));
}
diff --git a/numpy/core/src/multiarray/getset.c b/numpy/core/src/multiarray/getset.c
index 6503d8f12..c077eb23f 100644
--- a/numpy/core/src/multiarray/getset.c
+++ b/numpy/core/src/multiarray/getset.c
@@ -64,15 +64,17 @@ array_shape_set(PyArrayObject *self, PyObject *val)
((PyArrayObject_fieldaccess *)self)->nd = nd;
if (nd > 0) {
/* create new dimensions and strides */
- ((PyArrayObject_fieldaccess *)self)->dimensions = PyDimMem_NEW(2*nd);
+ ((PyArrayObject_fieldaccess *)self)->dimensions = PyDimMem_NEW(3*nd);
if (PyArray_DIMS(self) == NULL) {
Py_DECREF(ret);
PyErr_SetString(PyExc_MemoryError,"");
return -1;
}
((PyArrayObject_fieldaccess *)self)->strides = PyArray_DIMS(self) + nd;
+ ((PyArrayObject_fieldaccess *)self)->maskna_strides = PyArray_DIMS(self) + 2*nd;
memcpy(PyArray_DIMS(self), PyArray_DIMS(ret), nd*sizeof(intp));
memcpy(PyArray_STRIDES(self), PyArray_STRIDES(ret), nd*sizeof(intp));
+ memcpy(PyArray_MASKNA_STRIDES(self), PyArray_MASKNA_STRIDES(ret), nd*sizeof(intp));
}
else {
((PyArrayObject_fieldaccess *)self)->dimensions = NULL;
diff --git a/numpy/core/src/multiarray/mapping.c b/numpy/core/src/multiarray/mapping.c
index 5cd6531d4..0f5028c28 100644
--- a/numpy/core/src/multiarray/mapping.c
+++ b/numpy/core/src/multiarray/mapping.c
@@ -15,6 +15,7 @@
#include "common.h"
#include "iterators.h"
#include "mapping.h"
+#include "na_singleton.h"
#define SOBJ_NOTFANCY 0
#define SOBJ_ISFANCY 1
@@ -41,66 +42,133 @@ array_length(PyArrayObject *self)
}
NPY_NO_EXPORT PyObject *
-array_big_item(PyArrayObject *self, intp i)
+array_big_item(PyArrayObject *self, npy_intp i)
{
char *item;
- PyArrayObject *r;
+ PyArrayObject *ret;
+ npy_intp dim0;
if(PyArray_NDIM(self) == 0) {
PyErr_SetString(PyExc_IndexError,
"0-d arrays can't be indexed");
return NULL;
}
- if ((item = index2ptr(self, i)) == NULL) {
+
+ printf("array_big_item\n");
+
+ /* Bounds check and get the data pointer */
+ dim0 = PyArray_DIM(self, 0);
+ if (i < 0) {
+ i += dim0;
+ }
+ if (i < 0 || i >= dim0) {
+ PyErr_SetString(PyExc_IndexError,"index out of bounds");
return NULL;
}
+ item = PyArray_DATA(self) + i * PyArray_STRIDE(self, 0);
+
+ /* Create the view array */
Py_INCREF(PyArray_DESCR(self));
- r = (PyArrayObject *)PyArray_NewFromDescr(Py_TYPE(self),
+ ret = (PyArrayObject *)PyArray_NewFromDescr(Py_TYPE(self),
PyArray_DESCR(self),
PyArray_NDIM(self)-1,
PyArray_DIMS(self)+1,
PyArray_STRIDES(self)+1, item,
- PyArray_FLAGS(self),
+ PyArray_FLAGS(self) & ~(NPY_ARRAY_MASKNA | NPY_ARRAY_OWNMASKNA),
(PyObject *)self);
- if (r == NULL) {
+ if (ret == NULL) {
return NULL;
}
+
+ /* Take a view of the mask if it exists */
+ if (PyArray_HASMASKNA(self)) {
+ PyArrayObject_fieldaccess *fa = (PyArrayObject_fieldaccess *)ret;
+
+ fa->maskna_dtype = PyArray_MASKNA_DTYPE(self);
+ Py_INCREF(fa->maskna_dtype);
+ fa->maskna_data = PyArray_MASKNA_DATA(self) +
+ i * PyArray_MASKNA_STRIDES(self)[0];
+ if (fa->nd > 0) {
+ memcpy(fa->maskna_strides, &(PyArray_MASKNA_STRIDES(self)[1]),
+ (fa->nd - 1) * sizeof(npy_intp));
+ }
+ fa->flags |= NPY_ARRAY_MASKNA;
+ }
+
+ /* Set the base object */
Py_INCREF(self);
- if (PyArray_SetBaseObject(r, (PyObject *)self) < 0) {
- Py_DECREF(r);
+ if (PyArray_SetBaseObject(ret, (PyObject *)self) < 0) {
+ Py_DECREF(ret);
return NULL;
}
- PyArray_UpdateFlags(r, NPY_ARRAY_C_CONTIGUOUS | NPY_ARRAY_F_CONTIGUOUS);
- return (PyObject *)r;
+
+ PyArray_UpdateFlags(ret, NPY_ARRAY_C_CONTIGUOUS | NPY_ARRAY_F_CONTIGUOUS);
+ return (PyObject *)ret;
}
NPY_NO_EXPORT int
_array_ass_item(PyArrayObject *self, Py_ssize_t i, PyObject *v)
{
- return array_ass_big_item(self, (intp) i, v);
+ return array_ass_big_item(self, (npy_intp) i, v);
}
+
/* contains optimization for 1-d arrays */
NPY_NO_EXPORT PyObject *
array_item_nice(PyArrayObject *self, Py_ssize_t i)
{
if (PyArray_NDIM(self) == 1) {
char *item;
- if ((item = index2ptr(self, i)) == NULL) {
+ npy_intp dim0;
+
+ /* Bounds check and get the data pointer */
+ dim0 = PyArray_DIM(self, 0);
+ if (i < 0) {
+ i += dim0;
+ }
+ if (i < 0 || i >= dim0) {
+ PyErr_SetString(PyExc_IndexError,"index out of bounds");
return NULL;
}
+ item = PyArray_DATA(self) + i * PyArray_STRIDE(self, 0);
+
+ if (PyArray_HASMASKNA(self)) {
+ npy_mask maskvalue;
+
+ maskvalue = (npy_mask)*(PyArray_MASKNA_DATA(self) +
+ i * PyArray_MASKNA_STRIDES(self)[0]);
+ if (!NpyMaskValue_IsExposed(maskvalue)) {
+ NpyNA_fields *fna;
+
+ fna = (NpyNA_fields *)NpyNA_Type.tp_new(&NpyNA_Type, NULL, NULL);
+ if (fna == NULL) {
+ return NULL;
+ }
+
+ fna->dtype = PyArray_DESCR(self);
+ Py_INCREF(fna->dtype);
+
+ if (PyArray_MASKNA_DTYPE(self)->type_num == NPY_MASK) {
+ fna->payload = NpyMaskValue_GetPayload(maskvalue);
+ }
+
+ return (PyObject *)fna;
+ }
+ }
+
return PyArray_Scalar(item, PyArray_DESCR(self), (PyObject *)self);
}
else {
return PyArray_Return(
- (PyArrayObject *) array_big_item(self, (intp) i));
+ (PyArrayObject *) array_big_item(self, (npy_intp) i));
}
}
NPY_NO_EXPORT int
-array_ass_big_item(PyArrayObject *self, intp i, PyObject *v)
+array_ass_big_item(PyArrayObject *self, npy_intp i, PyObject *v)
{
PyArrayObject *tmp;
char *item;
+ npy_intp dim0;
int ret;
if (v == NULL) {
@@ -108,11 +176,13 @@ array_ass_big_item(PyArrayObject *self, intp i, PyObject *v)
"can't delete array elements");
return -1;
}
+
if (!PyArray_ISWRITEABLE(self)) {
PyErr_SetString(PyExc_RuntimeError,
"array is not writeable");
return -1;
}
+
if (PyArray_NDIM(self) == 0) {
PyErr_SetString(PyExc_IndexError,
"0-d arrays can't be indexed.");
@@ -120,8 +190,10 @@ array_ass_big_item(PyArrayObject *self, intp i, PyObject *v)
}
- if (PyArray_NDIM(self) > 1) {
- if((tmp = (PyArrayObject *)array_big_item(self, i)) == NULL) {
+ /* For multi-dimensional arrays and NA masked arrays, use CopyObject */
+ if (PyArray_NDIM(self) > 1 || PyArray_HASMASKNA(self)) {
+ tmp = (PyArrayObject *)array_big_item(self, i);
+ if(tmp == NULL) {
return -1;
}
ret = PyArray_CopyObject(tmp, v);
@@ -129,13 +201,18 @@ array_ass_big_item(PyArrayObject *self, intp i, PyObject *v)
return ret;
}
- if ((item = index2ptr(self, i)) == NULL) {
- return -1;
+ /* Bounds check and get the data pointer */
+ dim0 = PyArray_DIM(self, 0);
+ if (i < 0) {
+ i += dim0;
}
- if (PyArray_DESCR(self)->f->setitem(v, item, self) == -1) {
+ if (i < 0 || i >= dim0) {
+ PyErr_SetString(PyExc_IndexError,"index out of bounds");
return -1;
}
- return 0;
+ item = PyArray_DATA(self) + i * PyArray_STRIDE(self, 0);
+
+ return PyArray_DESCR(self)->f->setitem(v, item, self);
}
/* -------------------------------------------------------------- */
@@ -147,7 +224,7 @@ _swap_axes(PyArrayMapIterObject *mit, PyArrayObject **ret, int getmap)
int n1, n2, n3, val, bnd;
int i;
PyArray_Dims permute;
- intp d[MAX_DIMS];
+ npy_intp d[MAX_DIMS];
PyArrayObject *arr;
permute.ptr = d;
@@ -390,7 +467,7 @@ NPY_NO_EXPORT PyObject *
add_new_axes_0d(PyArrayObject *arr, int newaxis_count)
{
PyArrayObject *other;
- intp dimensions[MAX_DIMS];
+ npy_intp dimensions[MAX_DIMS];
int i;
for (i = 0; i < newaxis_count; ++i) {
@@ -652,7 +729,7 @@ array_subscript(PyArrayObject *self, PyObject *op)
return (PyObject *)self;
}
else {
- intp oned = 0;
+ npy_intp oned = 0;
Py_INCREF(PyArray_DESCR(self));
return PyArray_NewFromDescr(Py_TYPE(self),
PyArray_DESCR(self),
@@ -714,7 +791,7 @@ array_ass_sub_simple(PyArrayObject *self, PyObject *index, PyObject *op)
{
int ret;
PyArrayObject *tmp;
- intp value;
+ npy_intp value;
value = PyArray_PyIntAsIntp(index);
if (!error_converting(value)) {
@@ -766,11 +843,11 @@ array_ass_sub_simple(PyArrayObject *self, PyObject *index, PyObject *op)
otherwise fill vals with converted integers
*/
static int
-_tuple_of_integers(PyObject *seq, intp *vals, int maxvals)
+_tuple_of_integers(PyObject *seq, npy_intp *vals, int maxvals)
{
int i;
PyObject *obj;
- intp temp;
+ npy_intp temp;
for(i=0; i<maxvals; i++) {
obj = PyTuple_GET_ITEM(seq, i);
@@ -793,7 +870,7 @@ array_ass_sub(PyArrayObject *self, PyObject *index, PyObject *op)
{
int ret, oned, fancy;
PyArrayMapIterObject *mit;
- intp vals[MAX_DIMS];
+ npy_intp vals[MAX_DIMS];
if (op == NULL) {
PyErr_SetString(PyExc_ValueError,
@@ -809,7 +886,7 @@ array_ass_sub(PyArrayObject *self, PyObject *index, PyObject *op)
if (PyInt_Check(index) || PyArray_IsScalar(index, Integer) ||
PyLong_Check(index) || (PyIndex_Check(index) &&
!PySequence_Check(index))) {
- intp value;
+ npy_intp value;
value = PyArray_PyIntAsIntp(index);
if (PyErr_Occurred()) {
PyErr_Clear();
@@ -957,12 +1034,12 @@ array_subscript_nice(PyArrayObject *self, PyObject *op)
{
PyArrayObject *mp;
- intp vals[MAX_DIMS];
+ npy_intp vals[MAX_DIMS];
if (PyInt_Check(op) || PyArray_IsScalar(op, Integer) ||
PyLong_Check(op) || (PyIndex_Check(op) &&
!PySequence_Check(op))) {
- intp value;
+ npy_intp value;
value = PyArray_PyIntAsIntp(op);
if (PyErr_Occurred()) {
PyErr_Clear();
@@ -1082,10 +1159,10 @@ _nonzero_indices(PyObject *myBool, PyArrayIterObject **iters)
PyArray_Descr *typecode;
PyArrayObject *ba = NULL, *new = NULL;
int nd, j;
- intp size, i, count;
+ npy_intp size, i, count;
Bool *ptr;
- intp coords[MAX_DIMS], dims_m1[MAX_DIMS];
- intp *dptr[MAX_DIMS];
+ npy_intp coords[MAX_DIMS], dims_m1[MAX_DIMS];
+ npy_intp *dptr[MAX_DIMS];
typecode=PyArray_DescrFromType(NPY_BOOL);
ba = (PyArrayObject *)PyArray_FromAny(myBool, typecode, 0, 0,
@@ -1122,7 +1199,7 @@ _nonzero_indices(PyObject *myBool, PyArrayIterObject **iters)
if (iters[j] == NULL) {
goto fail;
}
- dptr[j] = (intp *)PyArray_DATA(iters[j]->ao);
+ dptr[j] = (npy_intp *)PyArray_DATA(iters[j]->ao);
coords[j] = 0;
dims_m1[j] = PyArray_DIMS(ba)[j]-1;
}
@@ -1201,7 +1278,7 @@ _convert_obj(PyObject *obj, PyArrayIterObject **iter)
NPY_NO_EXPORT void
PyArray_MapIterReset(PyArrayMapIterObject *mit)
{
- int i,j; intp coord[MAX_DIMS];
+ int i,j; npy_intp coord[MAX_DIMS];
PyArrayIterObject *it;
PyArray_CopySwapFunc *copyswap;
@@ -1210,7 +1287,7 @@ PyArray_MapIterReset(PyArrayMapIterObject *mit)
copyswap = PyArray_DESCR(mit->iters[0]->ao)->f->copyswap;
if (mit->subspace != NULL) {
- memcpy(coord, mit->bscoord, sizeof(intp)*PyArray_NDIM(mit->ait->ao));
+ memcpy(coord, mit->bscoord, sizeof(npy_intp)*PyArray_NDIM(mit->ait->ao));
PyArray_ITER_RESET(mit->subspace);
for (i = 0; i < mit->numiter; i++) {
it = mit->iters[i];
@@ -1249,7 +1326,7 @@ NPY_NO_EXPORT void
PyArray_MapIterNext(PyArrayMapIterObject *mit)
{
int i, j;
- intp coord[MAX_DIMS];
+ npy_intp coord[MAX_DIMS];
PyArrayIterObject *it;
PyArray_CopySwapFunc *copyswap;
@@ -1264,7 +1341,7 @@ PyArray_MapIterNext(PyArrayMapIterObject *mit)
if (mit->subspace->index >= mit->subspace->size) {
/* reset coord to coordinates of beginning of the subspace */
memcpy(coord, mit->bscoord,
- sizeof(intp)*PyArray_NDIM(mit->ait->ao));
+ sizeof(npy_intp)*PyArray_NDIM(mit->ait->ao));
PyArray_ITER_RESET(mit->subspace);
for (i = 0; i < mit->numiter; i++) {
it = mit->iters[i];
@@ -1314,8 +1391,8 @@ PyArray_MapIterBind(PyArrayMapIterObject *mit, PyArrayObject *arr)
PyObject *sub, *obj = NULL;
int i, j, n, curraxis, ellipexp, noellip;
PyArrayIterObject *it;
- intp dimsize;
- intp *indptr;
+ npy_intp dimsize;
+ npy_intp *indptr;
subnd = PyArray_NDIM(arr) - mit->numiter;
if (subnd < 0) {
@@ -1387,7 +1464,7 @@ PyArray_MapIterBind(PyArrayMapIterObject *mit, PyArrayObject *arr)
j = 0;
/* Only expand the first ellipsis */
noellip = 1;
- memset(mit->bscoord, 0, sizeof(intp)*PyArray_NDIM(arr));
+ memset(mit->bscoord, 0, sizeof(npy_intp)*PyArray_NDIM(arr));
for (i = 0; i < n; i++) {
/*
* We need to fill in the starting coordinates for
@@ -1402,8 +1479,8 @@ PyArray_MapIterBind(PyArrayMapIterObject *mit, PyArrayObject *arr)
noellip = 0;
}
else {
- intp start = 0;
- intp stop, step;
+ npy_intp start = 0;
+ npy_intp stop, step;
/* Should be slice object or another Ellipsis */
if (obj == Py_Ellipsis) {
mit->bscoord[curraxis] = 0;
@@ -1441,12 +1518,12 @@ PyArray_MapIterBind(PyArrayMapIterObject *mit, PyArrayObject *arr)
}
for (i = 0; i < mit->numiter; i++) {
- intp indval;
+ npy_intp indval;
it = mit->iters[i];
PyArray_ITER_RESET(it);
dimsize = PyArray_DIMS(arr)[mit->iteraxes[i]];
while (it->index < it->size) {
- indptr = ((intp *)it->dataptr);
+ indptr = ((npy_intp *)it->dataptr);
indval = *indptr;
if (indval < 0) {
indval += dimsize;
@@ -1567,7 +1644,7 @@ PyArray_MapIterNew(PyObject *indexobj, int oned, int fancy)
goto fail;
}
mit->nd = PyArray_NDIM(arr);
- memcpy(mit->dimensions, PyArray_DIMS(arr), mit->nd*sizeof(intp));
+ memcpy(mit->dimensions, PyArray_DIMS(arr), mit->nd*sizeof(npy_intp));
mit->size = PyArray_SIZE(arr);
Py_DECREF(arr);
Py_DECREF(mit->indexobj);
diff --git a/numpy/core/src/multiarray/methods.c b/numpy/core/src/multiarray/methods.c
index 28fb8f8cf..09ba4773d 100644
--- a/numpy/core/src/multiarray/methods.c
+++ b/numpy/core/src/multiarray/methods.c
@@ -1573,8 +1573,9 @@ array_setstate(PyArrayObject *self, PyObject *args)
fa->nd = nd;
if (nd > 0) {
- fa->dimensions = PyDimMem_NEW(nd * 2);
+ fa->dimensions = PyDimMem_NEW(3*nd);
fa->strides = PyArray_DIMS(self) + nd;
+ fa->maskna_strides = PyArray_DIMS(self) + 2*nd;
memcpy(PyArray_DIMS(self), dimensions, sizeof(intp)*nd);
_array_fill_strides(PyArray_STRIDES(self), dimensions, nd,
PyArray_DESCR(self)->elsize,
diff --git a/numpy/core/src/multiarray/na_mask.c b/numpy/core/src/multiarray/na_mask.c
index a7eab49c9..0e192ba42 100644
--- a/numpy/core/src/multiarray/na_mask.c
+++ b/numpy/core/src/multiarray/na_mask.c
@@ -19,6 +19,7 @@
#include "shape.h"
#include "lowlevel_strided_loops.h"
+#include "na_singleton.h"
/*NUMPY_API
*
@@ -177,8 +178,69 @@ PyArray_AllocateMaskNA(PyArrayObject *arr, npy_bool ownmaskna, npy_bool multina)
/* Set the NA mask data in the array */
Py_XDECREF(maskna_dtype);
fa->maskna_dtype = maskna_dtype;
- fa->maskna_data = (npy_mask *)maskna_data;
+ fa->maskna_data = maskna_data;
fa->flags |= (NPY_ARRAY_MASKNA | NPY_ARRAY_OWNMASKNA);
return 0;
}
+
+/*
+ * Assigns the given NA value to all the elements in the array. If
+ * 'arr' has a mask, masks all the elements of the array.
+ *
+ * In the future, when 'arr' has an NA dtype, will assign the
+ * appropriate NA bitpatterns to the elements.
+ *
+ * Returns -1 on failure, 0 on success.
+ */
+NPY_NO_EXPORT int
+PyArray_AssignNA(PyArrayObject *arr, NpyNA *na)
+{
+ NpyNA_fields *fna = (NpyNA_fields *)na;
+ char maskvalue;
+ PyArray_Descr *maskdtype;
+ npy_intp strides[NPY_MAXDIMS];
+
+ if (!PyArray_HASMASKNA(arr)) {
+ PyErr_SetString(PyExc_ValueError,
+ "Cannot assign an NA to an "
+ "array with no NA support");
+ return -1;
+ }
+
+ /* Turn the payload into a mask value */
+ if (fna->payload == NPY_NA_NOPAYLOAD) {
+ maskvalue = 0;
+ }
+ else if (PyArray_MASKNA_DTYPE(arr)->type_num !=
+ NPY_MASK) {
+ /* TODO: also handle struct-NA mask dtypes */
+ PyErr_SetString(PyExc_ValueError,
+ "Cannot assign an NA with a payload to an "
+ "NA-array with a boolean mask, requires a multi-NA mask");
+ return -1;
+ }
+ else {
+ maskvalue = (char)NpyMaskValue_Create(0, fna->payload);
+ }
+
+ /* Copy the mask value to arr's mask */
+ maskdtype = PyArray_DescrFromType(maskvalue == 0 ? NPY_BOOL
+ : NPY_MASK);
+ if (maskdtype == NULL) {
+ return -1;
+ }
+ memset(strides, 0, PyArray_NDIM(arr) * sizeof(npy_intp));
+ if (PyArray_CastRawNDimArrays(PyArray_NDIM(arr),
+ PyArray_DIMS(arr),
+ &maskvalue, PyArray_MASKNA_DATA(arr),
+ strides, PyArray_MASKNA_STRIDES(arr),
+ maskdtype, PyArray_MASKNA_DTYPE(arr),
+ 0) < 0) {
+ Py_DECREF(maskdtype);
+ return -1;
+ }
+
+ Py_DECREF(maskdtype);
+ return 0;
+}
diff --git a/numpy/core/src/multiarray/na_singleton.c b/numpy/core/src/multiarray/na_singleton.c
index f7097b340..fde42f7e5 100644
--- a/numpy/core/src/multiarray/na_singleton.c
+++ b/numpy/core/src/multiarray/na_singleton.c
@@ -25,9 +25,9 @@
static PyObject *
na_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds)
{
- NpyNA_fieldaccess *self;
+ NpyNA_fields *self;
- self = (NpyNA_fieldaccess *)subtype->tp_alloc(subtype, 0);
+ self = (NpyNA_fields *)subtype->tp_alloc(subtype, 0);
if (self != NULL) {
self->payload = NPY_NA_NOPAYLOAD;
self->dtype = NULL;
@@ -38,7 +38,7 @@ na_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds)
}
static int
-na_init(NpyNA_fieldaccess *self, PyObject *args, PyObject *kwds)
+na_init(NpyNA_fields *self, PyObject *args, PyObject *kwds)
{
static char *kwlist[] = {"payload", "dtype", NULL};
int payload = NPY_MAX_INT;
@@ -79,9 +79,9 @@ na_init(NpyNA_fieldaccess *self, PyObject *args, PyObject *kwds)
static PyObject *
na_call(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwds)
{
- NpyNA_fieldaccess *ret;
+ NpyNA_fields *ret;
- ret = (NpyNA_fieldaccess *)na_new(&NpyNA_Type, NULL, NULL);
+ ret = (NpyNA_fields *)na_new(&NpyNA_Type, NULL, NULL);
if (ret != NULL) {
if (na_init(ret, args, kwds) < 0) {
Py_DECREF(ret);
@@ -93,7 +93,7 @@ na_call(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwds)
}
static void
-na_dealloc(NpyNA_fieldaccess *self)
+na_dealloc(NpyNA_fields *self)
{
Py_XDECREF(self->dtype);
self->dtype = NULL;
@@ -101,7 +101,7 @@ na_dealloc(NpyNA_fieldaccess *self)
}
static PyObject *
-na_repr(NpyNA_fieldaccess *self)
+na_repr(NpyNA_fields *self)
{
if (self->dtype == NULL) {
if (self->payload == NPY_NA_NOPAYLOAD) {
@@ -132,7 +132,7 @@ na_repr(NpyNA_fieldaccess *self)
* the dtype. It is always either "NA" or "NA(payload)".
*/
static PyObject *
-na_str(NpyNA_fieldaccess *self)
+na_str(NpyNA_fields *self)
{
if (self->payload == NPY_NA_NOPAYLOAD) {
return PyUString_FromString("NA");
@@ -146,7 +146,7 @@ na_str(NpyNA_fieldaccess *self)
* Any comparison with NA produces an NA.
*/
static PyObject *
-na_richcompare(NpyNA_fieldaccess *self, PyObject *other, int cmp_op)
+na_richcompare(NpyNA_fields *self, PyObject *other, int cmp_op)
{
/* If an ndarray is compared directly with NA, let the array handle it */
if (PyArray_Check(other)) {
@@ -161,7 +161,7 @@ na_richcompare(NpyNA_fieldaccess *self, PyObject *other, int cmp_op)
}
static PyObject *
-na_payload_get(NpyNA_fieldaccess *self)
+na_payload_get(NpyNA_fields *self)
{
if (self->payload == NPY_NA_NOPAYLOAD) {
Py_INCREF(Py_None);
@@ -173,7 +173,7 @@ na_payload_get(NpyNA_fieldaccess *self)
}
static int
-na_payload_set(NpyNA_fieldaccess *self, PyObject *value)
+na_payload_set(NpyNA_fields *self, PyObject *value)
{
long payload;
@@ -217,7 +217,7 @@ na_payload_set(NpyNA_fieldaccess *self, PyObject *value)
}
static PyObject *
-na_dtype_get(NpyNA_fieldaccess *self)
+na_dtype_get(NpyNA_fields *self)
{
if (self->dtype == NULL) {
Py_INCREF(Py_None);
@@ -230,7 +230,7 @@ na_dtype_get(NpyNA_fieldaccess *self)
}
static int
-na_dtype_set(NpyNA_fieldaccess *self, PyObject *value)
+na_dtype_set(NpyNA_fields *self, PyObject *value)
{
PyArray_Descr *dtype = NULL;
@@ -270,12 +270,12 @@ static PyGetSetDef na_getsets[] = {
NPY_NO_EXPORT NpyNA *
NpyNA_CombineNA(NpyNA *na1, NpyNA *na2)
{
- NpyNA_fieldaccess *ret, *fna1, *fna2;
+ NpyNA_fields *ret, *fna1, *fna2;
- fna1 = (NpyNA_fieldaccess *)na1;
- fna2 = (NpyNA_fieldaccess *)na2;
+ fna1 = (NpyNA_fields *)na1;
+ fna2 = (NpyNA_fields *)na2;
- ret = (NpyNA_fieldaccess *)na_new(&NpyNA_Type, NULL, NULL);
+ ret = (NpyNA_fields *)na_new(&NpyNA_Type, NULL, NULL);
if (ret == NULL) {
return NULL;
}
@@ -312,10 +312,10 @@ NpyNA_CombineNA(NpyNA *na1, NpyNA *na2)
NPY_NO_EXPORT NpyNA *
NpyNA_CombineNAWithObject(NpyNA *na, PyObject *obj)
{
- NpyNA_fieldaccess *ret, *fna;
+ NpyNA_fields *ret, *fna;
PyArray_Descr *dtype = NULL;
- fna = (NpyNA_fieldaccess *)na;
+ fna = (NpyNA_fields *)na;
/* If 'obj' is NA, handle it specially */
if (NpyNA_Check(obj)) {
@@ -344,7 +344,7 @@ NpyNA_CombineNAWithObject(NpyNA *na, PyObject *obj)
}
}
- ret = (NpyNA_fieldaccess *)na_new(&NpyNA_Type, NULL, NULL);
+ ret = (NpyNA_fields *)na_new(&NpyNA_Type, NULL, NULL);
if (ret == NULL) {
return NULL;
}
@@ -368,6 +368,86 @@ NpyNA_CombineNAWithObject(NpyNA *na, PyObject *obj)
return (NpyNA *)ret;
}
+/*
+ * Converts an object into an NA if possible.
+ *
+ * If 'suppress_error' is enabled, doesn't raise an error when something
+ * isn't NA.
+ */
+NPY_NO_EXPORT NpyNA *
+NpyNA_FromObject(PyObject *obj, int suppress_error)
+{
+ /* Pass through existing NAs */
+ if (NpyNA_Check(obj)) {
+ Py_INCREF(obj);
+ return (NpyNA *)obj;
+ }
+ /* Convert zero-dimensional masked elements into NAs */
+ else if (PyArray_Check(obj)) {
+ if (PyArray_NDIM((PyArrayObject *)obj) == 0 &&
+ !PyArray_HASFIELDS((PyArrayObject *)obj)) {
+ if (PyArray_HASMASKNA((PyArrayObject *)obj)) {
+ npy_mask maskvalue;
+ NpyNA_fields *fna;
+
+ maskvalue = (npy_mask)*PyArray_MASKNA_DATA(
+ (PyArrayObject *)obj);
+ if (NpyMaskValue_IsExposed(maskvalue)) {
+ if (!suppress_error) {
+ PyErr_SetString(PyExc_ValueError,
+ "Cannot convert zero-dimensional array with "
+ "valid value into NA");
+ }
+ return NULL;
+ }
+
+ fna = (NpyNA_fields *)na_new(&NpyNA_Type, NULL, NULL);
+ if (fna == NULL) {
+ return NULL;
+ }
+
+ fna->dtype = PyArray_DESCR((PyArrayObject *)obj);
+ Py_INCREF(fna->dtype);
+
+ if (PyArray_MASKNA_DTYPE((PyArrayObject *)obj)->type_num ==
+ NPY_MASK) {
+ fna->payload = NpyMaskValue_GetPayload(maskvalue);
+ }
+
+ return (NpyNA *)fna;
+ }
+ }
+ else {
+ if (!suppress_error) {
+ PyErr_SetString(PyExc_ValueError,
+ "Cannot convert array with one or more dimensions "
+ "into an NA");
+ }
+ return NULL;
+ }
+ }
+
+ if (!suppress_error) {
+ PyErr_SetString(PyExc_ValueError, "Cannot convert object into an NA");
+ }
+ return NULL;
+}
+
+/*
+ * Returns a mask value corresponding to the NA.
+ */
+NPY_NO_EXPORT npy_mask
+NpyNA_AsMaskValue(NpyNA *na)
+{
+ NpyNA_fields *fna = (NpyNA_fields *)na;
+
+ if (fna->payload == NPY_NA_NOPAYLOAD) {
+ return 0;
+ }
+ else {
+ return NpyMaskValue_Create(0, fna->payload);
+ }
+}
/* An NA unary op simply passes along the same NA */
static PyObject *
@@ -560,7 +640,7 @@ NPY_NO_EXPORT PyTypeObject NpyNA_Type = {
0, /* ob_size */
#endif
"numpy.NAType", /* tp_name */
- sizeof(NpyNA_fieldaccess), /* tp_basicsize */
+ sizeof(NpyNA_fields), /* tp_basicsize */
0, /* tp_itemsize */
/* methods */
(destructor)na_dealloc, /* tp_dealloc */
@@ -614,7 +694,7 @@ NPY_NO_EXPORT PyTypeObject NpyNA_Type = {
#endif
};
-NPY_NO_EXPORT NpyNA_fieldaccess _Npy_NASingleton = {
+NPY_NO_EXPORT NpyNA_fields _Npy_NASingleton = {
PyObject_HEAD_INIT(&NpyNA_Type)
NPY_NA_NOPAYLOAD, /* payload */
NULL, /* dtype */
diff --git a/numpy/core/src/multiarray/na_singleton.h b/numpy/core/src/multiarray/na_singleton.h
index dbf918e0e..b911c2364 100644
--- a/numpy/core/src/multiarray/na_singleton.h
+++ b/numpy/core/src/multiarray/na_singleton.h
@@ -10,9 +10,9 @@ typedef struct {
PyArray_Descr *dtype;
/* Internal flag, whether this is the singleton numpy.NA or not */
int is_singleton;
-} NpyNA_fieldaccess;
+} NpyNA_fields;
-NPY_NO_EXPORT NpyNA_fieldaccess _Npy_NASingleton;
+NPY_NO_EXPORT NpyNA_fields _Npy_NASingleton;
#define Npy_NA ((PyObject *)&_Npy_NASingleton)
#define NPY_NA_NOPAYLOAD (255)
@@ -39,4 +39,19 @@ NpyNA_CombineNA(NpyNA *na1, NpyNA *na2);
NPY_NO_EXPORT NpyNA *
NpyNA_CombineNAWithObject(NpyNA *na, PyObject *obj);
+/*
+ * Converts an object into an NA if possible.
+ *
+ * If 'suppress_error' is enabled, doesn't raise an error when something
+ * isn't NA.
+ */
+NPY_NO_EXPORT NpyNA *
+NpyNA_FromObject(PyObject *obj, int suppress_error);
+
+/*
+ * Returns a mask value corresponding to the NA.
+ */
+NPY_NO_EXPORT npy_mask
+NpyNA_AsMaskValue(NpyNA *na);
+
#endif
diff --git a/numpy/core/src/multiarray/scalarapi.c b/numpy/core/src/multiarray/scalarapi.c
index fa783dabf..2b118a5bd 100644
--- a/numpy/core/src/multiarray/scalarapi.c
+++ b/numpy/core/src/multiarray/scalarapi.c
@@ -19,6 +19,7 @@
#include "scalartypes.h"
#include "common.h"
+#include "na_singleton.h"
static PyArray_Descr *
_descr_from_subtype(PyObject *type)
@@ -814,7 +815,18 @@ PyArray_Return(PyArrayObject *mp)
}
if (PyArray_NDIM(mp) == 0) {
PyObject *ret;
- ret = PyArray_ToScalar(PyArray_DATA(mp), mp);
+ if (PyArray_HASMASKNA(mp)) {
+ npy_mask maskvalue = (npy_mask)(*PyArray_MASKNA_DATA(mp));
+ if (NpyMaskValue_IsExposed(maskvalue)) {
+ ret = PyArray_ToScalar(PyArray_DATA(mp), mp);
+ }
+ else {
+ ret = (PyObject *)NpyNA_FromObject((PyObject *)mp, 0);
+ }
+ }
+ else {
+ ret = PyArray_ToScalar(PyArray_DATA(mp), mp);
+ }
Py_DECREF(mp);
return ret;
}
diff --git a/numpy/core/src/multiarray/shape.c b/numpy/core/src/multiarray/shape.c
index 532e8a2ba..a90d2574e 100644
--- a/numpy/core/src/multiarray/shape.c
+++ b/numpy/core/src/multiarray/shape.c
@@ -143,7 +143,7 @@ PyArray_Resize(PyArrayObject *self, PyArray_Dims *newshape, int refcheck,
/* Different number of dimensions. */
((PyArrayObject_fieldaccess *)self)->nd = new_nd;
/* Need new dimensions and strides arrays */
- dimptr = PyDimMem_RENEW(PyArray_DIMS(self), 2*new_nd);
+ dimptr = PyDimMem_RENEW(PyArray_DIMS(self), 3*new_nd);
if (dimptr == NULL) {
PyErr_SetString(PyExc_MemoryError,
"cannot allocate memory for array");
@@ -151,6 +151,7 @@ PyArray_Resize(PyArrayObject *self, PyArray_Dims *newshape, int refcheck,
}
((PyArrayObject_fieldaccess *)self)->dimensions = dimptr;
((PyArrayObject_fieldaccess *)self)->strides = dimptr + new_nd;
+ ((PyArrayObject_fieldaccess *)self)->maskna_strides = dimptr + 2*new_nd;
}
/* make new_strides variable */
diff --git a/numpy/core/tests/test_na.py b/numpy/core/tests/test_na.py
index 676377ca1..06d7953dd 100644
--- a/numpy/core/tests/test_na.py
+++ b/numpy/core/tests/test_na.py
@@ -149,5 +149,25 @@ def test_na_other_operations():
assert_equal((np.NA | False).dtype, np.array(False).dtype)
assert_equal((np.NA & True).dtype, np.array(True).dtype)
+def test_na_mask_flag():
+ a = np.arange(3)
+ assert_(not a.flags.maskna)
+ assert_(not a.flags.ownmaskna)
+ assert_(not a.flags['MASKNA'])
+ assert_(not a.flags['OWNMASKNA'])
+ # Add a mask by setting the flag
+ a.flags.maskna = True
+ assert_(a.flags.maskna)
+ assert_(a.flags.ownmaskna)
+ assert_(a.flags['MASKNA'])
+ assert_(a.flags['OWNMASKNA'])
+ # Can't remove the mask once it's created
+ def setmaskna(x, v):
+ x.maskna = v
+ assert_raises(ValueError, setmaskna, a.flags, False)
+ def setownmaskna(x, v):
+ x.ownmaskna = v
+ assert_raises(ValueError, setownmaskna, a.flags, False)
+
if __name__ == "__main__":
run_module_suite()