Skip to content

Commit

Permalink
ENH: missingdata: Add maskna= parameter to np.copy and ndarray.copy
Browse files Browse the repository at this point in the history
  • Loading branch information
mwiebe authored and charris committed Aug 27, 2011
1 parent 9764760 commit 0e1a4e9
Show file tree
Hide file tree
Showing 8 changed files with 319 additions and 156 deletions.
16 changes: 10 additions & 6 deletions numpy/add_newdocs.py
Expand Up @@ -3247,17 +3247,21 @@ def luf(lamdaexpr, *args, **kwargs):

add_newdoc('numpy.core.multiarray', 'ndarray', ('copy',
"""
a.copy(order='C')
a.copy(order='C', maskna=None)
Return a copy of the array.
Parameters
----------
order : {'C', 'F', 'A'}, optional
By default, the result is stored in C-contiguous (row-major) order in
memory. If `order` is `F`, the result has 'Fortran' (column-major)
order. If order is 'A' ('Any'), then the result has the same order
as the input.
order : {'C', 'F', 'A', 'K'}, optional
Controls the memory layout of the copy. 'C' means C-order,
'F' means F-order, 'A' means 'F' if `a` is Fortran contiguous,
'C' otherwise. 'K' means match the layout of `a` as closely
as possible.
maskna : bool, optional
If specifies, forces the copy to have or to not have an
NA mask. This is a way to remove an NA mask from an array
while making a copy.
See also
--------
Expand Down
7 changes: 4 additions & 3 deletions numpy/core/src/multiarray/convert.c
Expand Up @@ -515,8 +515,9 @@ PyArray_AssignOne(PyArrayObject *dst,
NPY_NO_EXPORT PyObject *
PyArray_NewCopy(PyArrayObject *obj, NPY_ORDER order)
{
PyArrayObject *ret = (PyArrayObject *)PyArray_NewLikeArray(
obj, order, NULL, 1);
PyArrayObject *ret;

ret = (PyArrayObject *)PyArray_NewLikeArray(obj, order, NULL, 1);
if (ret == NULL) {
return NULL;
}
Expand All @@ -528,7 +529,7 @@ PyArray_NewCopy(PyArrayObject *obj, NPY_ORDER order)
}
}

if (PyArray_CopyInto(ret, obj) == -1) {
if (PyArray_AssignArray(ret, obj, NULL, NPY_UNSAFE_CASTING, 0, NULL) < 0) {
Py_DECREF(ret);
return NULL;
}
Expand Down
208 changes: 79 additions & 129 deletions numpy/core/src/multiarray/ctors.c
Expand Up @@ -1856,16 +1856,7 @@ PyArray_FromAny(PyObject *op, PyArray_Descr *newtype, int min_depth,
ret = NULL;
}
else {
if (PyArray_HASMASKNA((PyArrayObject *)arr) &&
(flags & NPY_ARRAY_ALLOWNA) == 0) {
PyErr_SetString(PyExc_ValueError,
"this operation does not support "
"arrays with NA masks");
ret = NULL;
}
else {
ret = (PyArrayObject *)PyArray_FromArray(arr, newtype, flags);
}
ret = (PyArrayObject *)PyArray_FromArray(arr, newtype, flags);
Py_DECREF(arr);
}
}
Expand Down Expand Up @@ -1962,13 +1953,12 @@ PyArray_FromArray(PyArrayObject *arr, PyArray_Descr *newtype, int flags)
int copy = 0;
int arrflags;
PyArray_Descr *oldtype;
PyTypeObject *subtype;
NPY_CASTING casting = NPY_SAFE_CASTING;

oldtype = PyArray_DESCR(arr);
subtype = Py_TYPE(arr);
if (newtype == NULL) {
newtype = oldtype; Py_INCREF(oldtype);
newtype = oldtype;
Py_INCREF(oldtype);
}
itemsize = newtype->elsize;
if (itemsize == 0) {
Expand Down Expand Up @@ -2005,141 +1995,79 @@ PyArray_FromArray(PyArrayObject *arr, PyArray_Descr *newtype, int flags)
return NULL;
}

/* Don't copy if sizes are compatible */
if ((flags & NPY_ARRAY_ENSURECOPY) ||
PyArray_EquivTypes(oldtype, newtype)) {
arrflags = PyArray_FLAGS(arr);
if (PyArray_NDIM(arr) <= 1 && (flags & NPY_ARRAY_F_CONTIGUOUS)) {
flags |= NPY_ARRAY_C_CONTIGUOUS;
}
copy = (flags & NPY_ARRAY_ENSURECOPY) ||
((flags & NPY_ARRAY_C_CONTIGUOUS) &&
(!(arrflags & NPY_ARRAY_C_CONTIGUOUS)))
|| ((flags & NPY_ARRAY_ALIGNED) &&
(!(arrflags & NPY_ARRAY_ALIGNED)))
|| (PyArray_NDIM(arr) > 1 &&
((flags & NPY_ARRAY_F_CONTIGUOUS) &&
(!(arrflags & NPY_ARRAY_F_CONTIGUOUS))))
|| ((flags & NPY_ARRAY_WRITEABLE) &&
(!(arrflags & NPY_ARRAY_WRITEABLE)));

if (copy) {
if ((flags & NPY_ARRAY_UPDATEIFCOPY) &&
(!PyArray_ISWRITEABLE(arr))) {
Py_DECREF(newtype);
PyErr_SetString(PyExc_ValueError,
"cannot copy back to a read-only array");
return NULL;
}
if ((flags & NPY_ARRAY_ENSUREARRAY)) {
subtype = &PyArray_Type;
}
ret = (PyArrayObject *)
PyArray_NewFromDescr(subtype, newtype,
PyArray_NDIM(arr),
PyArray_DIMS(arr),
NULL, NULL,
flags & NPY_ARRAY_F_CONTIGUOUS,
(PyObject *)arr);
if (ret == NULL) {
return NULL;
}

/* Allocate an NA mask if necessary from the input */
if (PyArray_HASMASKNA(arr)) {
if (PyArray_AllocateMaskNA(ret, 1, 0, 1) < 0) {
Py_DECREF(ret);
return NULL;
}
}

if (PyArray_CopyInto(ret, arr) < 0) {
Py_DECREF(ret);
return NULL;
}
arrflags = PyArray_FLAGS(arr);
if (PyArray_NDIM(arr) <= 1 && (flags & NPY_ARRAY_F_CONTIGUOUS)) {
flags |= NPY_ARRAY_C_CONTIGUOUS;
}
copy = (flags & NPY_ARRAY_ENSURECOPY) ||
((flags & NPY_ARRAY_C_CONTIGUOUS) &&
(!(arrflags & NPY_ARRAY_C_CONTIGUOUS)))
|| ((flags & NPY_ARRAY_ALIGNED) &&
(!(arrflags & NPY_ARRAY_ALIGNED)))
|| (PyArray_NDIM(arr) > 1 &&
((flags & NPY_ARRAY_F_CONTIGUOUS) &&
(!(arrflags & NPY_ARRAY_F_CONTIGUOUS))))
|| ((flags & NPY_ARRAY_WRITEABLE) &&
(!(arrflags & NPY_ARRAY_WRITEABLE))) ||
!PyArray_EquivTypes(oldtype, newtype);

/* Allocate an NA mask if requested but wasn't from the input */
if ((flags & (NPY_ARRAY_MASKNA | NPY_ARRAY_OWNMASKNA)) != 0 &&
!PyArray_HASMASKNA(ret)) {
if (PyArray_AllocateMaskNA(ret, 1, 0, 1) < 0) {
Py_DECREF(ret);
return NULL;
}
}
if (copy) {
NPY_ORDER order = NPY_KEEPORDER;
int subok = 1;

if (flags & NPY_ARRAY_UPDATEIFCOPY) {
/*
* Don't use PyArray_SetBaseObject, because that compresses
* the chain of bases.
*/
Py_INCREF(arr);
((PyArrayObject_fieldaccess *)ret)->base = (PyObject *)arr;
PyArray_ENABLEFLAGS(ret, NPY_ARRAY_UPDATEIFCOPY);
PyArray_CLEARFLAGS(arr, NPY_ARRAY_WRITEABLE);
}
/* Set the order for the copy being made based on the flags */
if (flags & NPY_ARRAY_F_CONTIGUOUS) {
order = NPY_FORTRANORDER;
}
/*
* If no copy then just increase the reference
* count and return the input
*/
else {
Py_DECREF(newtype);
if ((flags & NPY_ARRAY_ENSUREARRAY) &&
!PyArray_CheckExact(arr)) {
PyArray_Descr *dtype = PyArray_DESCR(arr);
Py_INCREF(dtype);
ret = (PyArrayObject *)
PyArray_NewFromDescr(&PyArray_Type,
dtype,
PyArray_NDIM(arr),
PyArray_DIMS(arr),
PyArray_STRIDES(arr),
PyArray_DATA(arr),
PyArray_FLAGS(arr),
NULL);
if (ret == NULL) {
return NULL;
}
if (PyArray_SetBaseObject(ret, (PyObject *)arr)) {
Py_DECREF(ret);
return NULL;
}
}
else {
ret = arr;
}
Py_INCREF(arr);
else if (flags & NPY_ARRAY_C_CONTIGUOUS) {
order = NPY_CORDER;
}
}

/*
* The desired output type is different than the input
* array type and copy was not specified
*/
else {
if ((flags & NPY_ARRAY_UPDATEIFCOPY) &&
(!PyArray_ISWRITEABLE(arr))) {
Py_DECREF(newtype);
PyErr_SetString(PyExc_ValueError,
"cannot copy back to a read-only array B");
"cannot copy back to a read-only array");
return NULL;
}
if ((flags & NPY_ARRAY_ENSUREARRAY)) {
subtype = &PyArray_Type;
subok = 0;
}
ret = (PyArrayObject *)
PyArray_NewFromDescr(subtype, newtype,
PyArray_NDIM(arr), PyArray_DIMS(arr),
NULL, NULL,
flags & NPY_ARRAY_F_CONTIGUOUS,
(PyObject *)arr);
ret = (PyArrayObject *)PyArray_NewLikeArray(arr, order,
newtype, subok);
if (ret == NULL) {
return NULL;
}
if (PyArray_CastTo(ret, arr) < 0) {

/*
* Allocate an NA mask if necessary from the input,
* is NAs are being allowed.
*/
if (PyArray_HASMASKNA(arr) && (flags & NPY_ARRAY_ALLOWNA)) {
if (PyArray_AllocateMaskNA(ret, 1, 0, 1) < 0) {
Py_DECREF(ret);
return NULL;
}
}

/*
* If a ALLOWNA was not enabled, and 'arr' has an NA mask,
* this will raise an error if 'arr' contains any NA values.
*/
if (PyArray_CopyInto(ret, arr) < 0) {
Py_DECREF(ret);
return NULL;
}

/* Allocate an NA mask if requested but wasn't from the input */
if ((flags & (NPY_ARRAY_MASKNA | NPY_ARRAY_OWNMASKNA)) != 0 &&
!PyArray_HASMASKNA(ret)) {
if (PyArray_AllocateMaskNA(ret, 1, 0, 1) < 0) {
Py_DECREF(ret);
return NULL;
}
}

if (flags & NPY_ARRAY_UPDATEIFCOPY) {
/*
* Don't use PyArray_SetBaseObject, because that compresses
Expand All @@ -2151,6 +2079,28 @@ PyArray_FromArray(PyArrayObject *arr, PyArray_Descr *newtype, int flags)
PyArray_CLEARFLAGS(arr, NPY_ARRAY_WRITEABLE);
}
}
/*
* If no copy then just increase the reference
* count and return the input
*/
else {
Py_DECREF(newtype);
if ((flags & NPY_ARRAY_ENSUREARRAY) &&
!PyArray_CheckExact(arr)) {
PyArray_Descr *dtype = PyArray_DESCR(arr);
Py_INCREF(dtype);

ret = (PyArrayObject *)PyArray_View(arr, NULL, &PyArray_Type);
if (ret == NULL) {
return NULL;
}
}
else {
ret = arr;
}
Py_INCREF(arr);
}

return (PyObject *)ret;
}

Expand Down
47 changes: 43 additions & 4 deletions numpy/core/src/multiarray/methods.c
Expand Up @@ -1009,14 +1009,53 @@ static PyObject *
array_copy(PyArrayObject *self, PyObject *args, PyObject *kwds)
{
PyArray_ORDER order = NPY_CORDER;
static char *kwlist[] = {"order", NULL};
PyObject *maskna_in = Py_None;
int maskna = -1;
static char *kwlist[] = {"order", "maskna", NULL};
PyArrayObject *ret;

if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O&", kwlist,
PyArray_OrderConverter, &order)) {
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O&O", kwlist,
PyArray_OrderConverter, &order,
&maskna_in)) {
return NULL;
}

return PyArray_NewCopy(self, order);
/* Treat None the same as not providing the parameter */
if (maskna_in != Py_None) {
maskna = PyObject_IsTrue(maskna_in);
if (maskna == -1) {
return NULL;
}
}

/* If maskna=False was passed and self has an NA mask, strip it away */
if (maskna == 0 && PyArray_HASMASKNA(self)) {
/* An array with no NA mask */
ret = (PyArrayObject *)PyArray_NewLikeArray(self, order, NULL, 1);
if (ret == NULL) {
return NULL;
}

/* AssignArray validates that 'self' contains no NA values */
if (PyArray_AssignArray(ret, self, NULL, NPY_UNSAFE_CASTING,
0, NULL) < 0) {
Py_DECREF(ret);
return NULL;
}
}
else {
ret = (PyArrayObject *)PyArray_NewCopy(self, order);

/* Add the NA mask if requested */
if (ret != NULL && maskna == 1) {
if (PyArray_AllocateMaskNA(ret, 1, 0, 1) < 0) {
Py_DECREF(ret);
return NULL;
}
}
}

return (PyObject *)ret;
}

#include <stdio.h>
Expand Down

0 comments on commit 0e1a4e9

Please sign in to comment.