summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Wiebe <mwwiebe@gmail.com>2011-08-22 14:01:55 -0700
committerCharles Harris <charlesr.harris@gmail.com>2011-08-27 07:26:59 -0600
commit0e1a4e9525b2c1e4abae97a6927cf59b5b2d534b (patch)
tree9eb3c920de5dbe2eb1de5580a826ab047705a402
parent976476081b78279154950d2392aff8ee9290b60f (diff)
downloadnumpy-0e1a4e9525b2c1e4abae97a6927cf59b5b2d534b.tar.gz
ENH: missingdata: Add maskna= parameter to np.copy and ndarray.copy
-rw-r--r--numpy/add_newdocs.py16
-rw-r--r--numpy/core/src/multiarray/convert.c7
-rw-r--r--numpy/core/src/multiarray/ctors.c208
-rw-r--r--numpy/core/src/multiarray/methods.c47
-rw-r--r--numpy/core/src/multiarray/multiarraymodule.c55
-rw-r--r--numpy/core/tests/test_api.py65
-rw-r--r--numpy/core/tests/test_maskna.py64
-rw-r--r--numpy/lib/function_base.py13
8 files changed, 319 insertions, 156 deletions
diff --git a/numpy/add_newdocs.py b/numpy/add_newdocs.py
index 711b7de52..7bd24b45e 100644
--- a/numpy/add_newdocs.py
+++ b/numpy/add_newdocs.py
@@ -3247,17 +3247,21 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('conjugate',
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
--------
diff --git a/numpy/core/src/multiarray/convert.c b/numpy/core/src/multiarray/convert.c
index 5640f1978..bbadd3847 100644
--- a/numpy/core/src/multiarray/convert.c
+++ b/numpy/core/src/multiarray/convert.c
@@ -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;
}
@@ -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;
}
diff --git a/numpy/core/src/multiarray/ctors.c b/numpy/core/src/multiarray/ctors.c
index 03c035615..2b1edd000 100644
--- a/numpy/core/src/multiarray/ctors.c
+++ b/numpy/core/src/multiarray/ctors.c
@@ -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);
}
}
@@ -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) {
@@ -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
@@ -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;
}
diff --git a/numpy/core/src/multiarray/methods.c b/numpy/core/src/multiarray/methods.c
index 754d7161f..8ea186ead 100644
--- a/numpy/core/src/multiarray/methods.c
+++ b/numpy/core/src/multiarray/methods.c
@@ -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>
diff --git a/numpy/core/src/multiarray/multiarraymodule.c b/numpy/core/src/multiarray/multiarraymodule.c
index 566036146..74ef05d50 100644
--- a/numpy/core/src/multiarray/multiarraymodule.c
+++ b/numpy/core/src/multiarray/multiarraymodule.c
@@ -1610,8 +1610,9 @@ _array_fromobject(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kws)
int ndmin = 0, nd;
PyArray_Descr *type = NULL;
PyArray_Descr *oldtype = NULL;
- NPY_ORDER order = NPY_ANYORDER;
- int flags = 0, maskna = 0, ownmaskna = 0;
+ NPY_ORDER order = NPY_KEEPORDER;
+ int flags = 0, maskna = -1, ownmaskna = 0;
+ PyObject *maskna_in = Py_None;
static char *kwd[]= {"object", "dtype", "copy", "order", "subok",
"ndmin", "maskna", "ownmaskna", NULL};
@@ -1621,21 +1622,39 @@ _array_fromobject(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kws)
"only 2 non-keyword arguments accepted");
return NULL;
}
- if(!PyArg_ParseTupleAndKeywords(args, kws, "O|O&O&O&O&iii", kwd,
+ if(!PyArg_ParseTupleAndKeywords(args, kws, "O|O&O&O&O&iOi", kwd,
&op,
PyArray_DescrConverter2, &type,
PyArray_BoolConverter, &copy,
PyArray_OrderConverter, &order,
PyArray_BoolConverter, &subok,
&ndmin,
- &maskna,
+ &maskna_in,
&ownmaskna)) {
goto clean_type;
}
+ /*
+ * Treat None the same as not providing the parameter, set
+ * maskna to -1 (unprovided), 0 (False), or 1 (True).
+ */
+ if (maskna_in != Py_None) {
+ maskna = PyObject_IsTrue(maskna_in);
+ if (maskna == -1) {
+ return NULL;
+ }
+ }
+
/* 'ownmaskna' forces 'maskna' to be True */
if (ownmaskna) {
- maskna = 1;
+ if (maskna == 0) {
+ PyErr_SetString(PyExc_ValueError,
+ "cannot specify maskna=False and ownmaskna=True");
+ return NULL;
+ }
+ else {
+ maskna = 1;
+ }
}
if (ndmin > NPY_MAXDIMS) {
@@ -1647,8 +1666,9 @@ _array_fromobject(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kws)
/* fast exit if simple call */
if (((subok && PyArray_Check(op)) ||
(!subok && PyArray_CheckExact(op))) &&
- ((maskna && PyArray_HASMASKNA((PyArrayObject *)op)) ||
- (!maskna && !PyArray_HASMASKNA((PyArrayObject *)op)))) {
+ ((maskna == -1) ||
+ (maskna == 1 && PyArray_HASMASKNA((PyArrayObject *)op)) ||
+ (maskna == 0 && !PyArray_HASMASKNA((PyArrayObject *)op)))) {
oparr = (PyArrayObject *)op;
if (type == NULL) {
if (!copy && STRIDING_OK(oparr, order)) {
@@ -1715,14 +1735,25 @@ _array_fromobject(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kws)
if (!subok) {
flags |= NPY_ARRAY_ENSUREARRAY;
}
- if (maskna) {
- flags |= NPY_ARRAY_MASKNA;
+
+ /* If maskna is the default, allow NA to pass through */
+ if (maskna == -1) {
+ flags |= NPY_ARRAY_ALLOWNA;
}
- if (ownmaskna) {
- flags |= NPY_ARRAY_OWNMASKNA;
+ /* If maskna is True, force there to be an NA mask */
+ else if (maskna == 1) {
+ flags |= NPY_ARRAY_MASKNA | NPY_ARRAY_ALLOWNA;
+ if (ownmaskna) {
+ flags |= NPY_ARRAY_OWNMASKNA;
+ }
}
+ /*
+ * Otherwise maskna is False, so we don't specify NPY_ARRAY_ALLOWNA.
+ * An array with an NA mask will cause a copy into an array
+ * without an NA mask
+ */
- flags |= (NPY_ARRAY_FORCECAST | NPY_ARRAY_ALLOWNA);
+ flags |= NPY_ARRAY_FORCECAST;
Py_XINCREF(type);
ret = (PyArrayObject *)PyArray_CheckFromAny(op, type,
0, 0, flags, NULL);
diff --git a/numpy/core/tests/test_api.py b/numpy/core/tests/test_api.py
index 2c98810fa..55d3c4ea8 100644
--- a/numpy/core/tests/test_api.py
+++ b/numpy/core/tests/test_api.py
@@ -240,5 +240,70 @@ def test_copyto_maskna():
assert_equal(a_orig, [[0,3,3],[3,4,12]])
assert_equal(np.isna(a), [[0,1,0],[1,1,1]])
+def test_copy_order():
+ a = np.arange(24).reshape(2,3,4)
+ b = a.copy(order='F')
+ c = np.arange(24).reshape(2,4,3).swapaxes(1,2)
+
+ def check_copy_result(x, y, ccontig, fcontig, strides=False):
+ assert_(not (x is y))
+ assert_equal(x, y)
+ assert_equal(res.flags.c_contiguous, ccontig)
+ assert_equal(res.flags.f_contiguous, fcontig)
+ if strides:
+ assert_equal(x.strides, y.strides)
+ else:
+ assert_(x.strides != y.strides)
+
+ # Validate the initial state of a, b, and c
+ assert_(a.flags.c_contiguous)
+ assert_(not a.flags.f_contiguous)
+ assert_(not b.flags.c_contiguous)
+ assert_(b.flags.f_contiguous)
+ assert_(not c.flags.c_contiguous)
+ assert_(not c.flags.f_contiguous)
+
+ # Copy with order='C'
+ res = a.copy(order='C')
+ check_copy_result(res, a, ccontig=True, fcontig=False, strides=True)
+ res = b.copy(order='C')
+ check_copy_result(res, b, ccontig=True, fcontig=False, strides=False)
+ res = c.copy(order='C')
+ check_copy_result(res, c, ccontig=True, fcontig=False, strides=False)
+ res = np.copy(a, order='C')
+ check_copy_result(res, a, ccontig=True, fcontig=False, strides=True)
+ res = np.copy(b, order='C')
+ check_copy_result(res, b, ccontig=True, fcontig=False, strides=False)
+ res = np.copy(c, order='C')
+ check_copy_result(res, c, ccontig=True, fcontig=False, strides=False)
+
+ # Copy with order='F'
+ res = a.copy(order='F')
+ check_copy_result(res, a, ccontig=False, fcontig=True, strides=False)
+ res = b.copy(order='F')
+ check_copy_result(res, b, ccontig=False, fcontig=True, strides=True)
+ res = c.copy(order='F')
+ check_copy_result(res, c, ccontig=False, fcontig=True, strides=False)
+ res = np.copy(a, order='F')
+ check_copy_result(res, a, ccontig=False, fcontig=True, strides=False)
+ res = np.copy(b, order='F')
+ check_copy_result(res, b, ccontig=False, fcontig=True, strides=True)
+ res = np.copy(c, order='F')
+ check_copy_result(res, c, ccontig=False, fcontig=True, strides=False)
+
+ # Copy with order='K'
+ res = a.copy(order='K')
+ check_copy_result(res, a, ccontig=True, fcontig=False, strides=True)
+ res = b.copy(order='K')
+ check_copy_result(res, b, ccontig=False, fcontig=True, strides=True)
+ res = c.copy(order='K')
+ check_copy_result(res, c, ccontig=False, fcontig=False, strides=True)
+ res = np.copy(a, order='K')
+ check_copy_result(res, a, ccontig=True, fcontig=False, strides=True)
+ res = np.copy(b, order='K')
+ check_copy_result(res, b, ccontig=False, fcontig=True, strides=True)
+ res = np.copy(c, order='K')
+ check_copy_result(res, c, ccontig=False, fcontig=False, strides=True)
+
if __name__ == "__main__":
run_module_suite()
diff --git a/numpy/core/tests/test_maskna.py b/numpy/core/tests/test_maskna.py
index 8a644c080..ba10a2ce4 100644
--- a/numpy/core/tests/test_maskna.py
+++ b/numpy/core/tests/test_maskna.py
@@ -83,6 +83,70 @@ def test_array_maskna_construction():
assert_(a.flags.maskna)
assert_equal(np.isna(a), True)
+def test_array_maskna_copy():
+ a = np.array([1,2,3])
+ b = np.array([2,3,4], maskna=True)
+ c = np.array([3,4,np.NA], maskna=True)
+
+ # Make a copy, adding a mask
+ res = a.copy(maskna=True)
+ assert_equal(res, a)
+ assert_(res.flags.maskna)
+ assert_(res.flags.ownmaskna)
+
+ res = np.copy(a, maskna=True)
+ assert_equal(res, a)
+ assert_(res.flags.maskna)
+ assert_(res.flags.ownmaskna)
+
+ # Make a copy, removing a mask
+ res = b.copy(maskna=False)
+ assert_equal(res, b)
+ assert_(not res.flags.maskna)
+ assert_(not res.flags.ownmaskna)
+
+ res = np.copy(b, maskna=False)
+ assert_equal(res, b)
+ assert_(not res.flags.maskna)
+ assert_(not res.flags.ownmaskna)
+
+ # Copy with removing a mask doesn't work if there are NAs
+ assert_raises(ValueError, c.copy, maskna=False)
+ assert_raises(ValueError, np.copy, c, maskna=False)
+
+ # Make a copy, preserving non-masked
+ res = a.copy()
+ assert_equal(res, a)
+ assert_(not res.flags.maskna)
+ assert_(not res.flags.ownmaskna)
+
+ res = np.copy(a)
+ assert_equal(res, a)
+ assert_(not res.flags.maskna)
+ assert_(not res.flags.ownmaskna)
+
+ # Make a copy, preserving masked
+ res = b.copy()
+ assert_equal(res, b)
+ assert_(res.flags.maskna)
+ assert_(res.flags.ownmaskna)
+
+ res = np.copy(b)
+ assert_equal(res, b)
+ assert_(res.flags.maskna)
+ assert_(res.flags.ownmaskna)
+
+ # Make a copy, preserving masked with an NA
+ res = c.copy()
+ assert_array_equal(res, c)
+ assert_(res.flags.maskna)
+ assert_(res.flags.ownmaskna)
+
+ res = np.copy(c)
+ assert_array_equal(res, c)
+ assert_(res.flags.maskna)
+ assert_(res.flags.ownmaskna)
+
def test_array_maskna_repr():
# Test some simple reprs with NA in them
a = np.array(np.NA, maskna=True)
diff --git a/numpy/lib/function_base.py b/numpy/lib/function_base.py
index 411a86955..b269d98a1 100644
--- a/numpy/lib/function_base.py
+++ b/numpy/lib/function_base.py
@@ -778,7 +778,7 @@ def select(condlist, choicelist, default=0):
S = S*ones(asarray(pfac).shape, S.dtype)
return choose(S, tuple(choicelist))
-def copy(a):
+def copy(a, order='C', maskna=None):
"""
Return an array copy of the given object.
@@ -786,6 +786,15 @@ def copy(a):
----------
a : array_like
Input data.
+ 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.
Returns
-------
@@ -815,7 +824,7 @@ def copy(a):
False
"""
- return array(a, copy=True)
+ return array(a, order=order, copy=True, maskna=maskna)
# Basic operations