diff options
author | Mark Wiebe <mwiebe@enthought.com> | 2011-07-25 18:07:53 -0500 |
---|---|---|
committer | Charles Harris <charlesr.harris@gmail.com> | 2011-08-27 07:26:46 -0600 |
commit | 02b42b5823019052b28e68b01c733b4f281eda59 (patch) | |
tree | 304ced231f9b8cd2bc1e19fd10f42c3576660187 | |
parent | b2e8328b4a951ae63d2c03b6055d222603bcb58d (diff) | |
download | numpy-02b42b5823019052b28e68b01c733b4f281eda59.tar.gz |
ENH: missingdata: Have some basic assignment and indexing with NA working
-rw-r--r-- | doc/neps/missing-data.rst | 16 | ||||
-rw-r--r-- | numpy/core/arrayprint.py | 2 | ||||
-rw-r--r-- | numpy/core/include/numpy/ndarraytypes.h | 8 | ||||
-rw-r--r-- | numpy/core/src/multiarray/arrayobject.c | 42 | ||||
-rw-r--r-- | numpy/core/src/multiarray/ctors.c | 56 | ||||
-rw-r--r-- | numpy/core/src/multiarray/ctors.h | 16 | ||||
-rw-r--r-- | numpy/core/src/multiarray/flagsobject.c | 27 | ||||
-rw-r--r-- | numpy/core/src/multiarray/getset.c | 4 | ||||
-rw-r--r-- | numpy/core/src/multiarray/mapping.c | 169 | ||||
-rw-r--r-- | numpy/core/src/multiarray/methods.c | 3 | ||||
-rw-r--r-- | numpy/core/src/multiarray/na_mask.c | 64 | ||||
-rw-r--r-- | numpy/core/src/multiarray/na_singleton.c | 124 | ||||
-rw-r--r-- | numpy/core/src/multiarray/na_singleton.h | 19 | ||||
-rw-r--r-- | numpy/core/src/multiarray/scalarapi.c | 14 | ||||
-rw-r--r-- | numpy/core/src/multiarray/shape.c | 3 | ||||
-rw-r--r-- | numpy/core/tests/test_na.py | 20 |
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() |