diff options
Diffstat (limited to 'numpy')
-rw-r--r-- | numpy/core/src/multiarray/compiled_base.c | 224 | ||||
-rw-r--r-- | numpy/lib/function_base.py | 3 | ||||
-rw-r--r-- | numpy/lib/tests/test_function_base.py | 5 |
3 files changed, 78 insertions, 154 deletions
diff --git a/numpy/core/src/multiarray/compiled_base.c b/numpy/core/src/multiarray/compiled_base.c index 932b94f15..711a0ab91 100644 --- a/numpy/core/src/multiarray/compiled_base.c +++ b/numpy/core/src/multiarray/compiled_base.c @@ -302,193 +302,111 @@ arr_digitize(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwds) } /* - * Insert values from an input array into an output array, at positions - * indicated by a mask. If the arrays are of dtype object (indicated by - * the objarray flag), take care of reference counting. - * - * This function implements the copying logic of arr_insert() defined - * below. - */ -static void -arr_insert_loop(char *mptr, char *vptr, char *input_data, char *zero, - char *avals_data, int melsize, int delsize, int objarray, - int totmask, int numvals, int nd, npy_intp *instrides, - npy_intp *inshape) -{ - int mindx, rem_indx, indx, i, copied; - - /* - * Walk through mask array, when non-zero is encountered - * copy next value in the vals array to the input array. - * If we get through the value array, repeat it as necessary. - */ - copied = 0; - for (mindx = 0; mindx < totmask; mindx++) { - if (memcmp(mptr,zero,melsize) != 0) { - /* compute indx into input array */ - rem_indx = mindx; - indx = 0; - for (i = nd - 1; i > 0; --i) { - indx += (rem_indx % inshape[i]) * instrides[i]; - rem_indx /= inshape[i]; - } - indx += rem_indx * instrides[0]; - /* fprintf(stderr, "mindx = %d, indx=%d\n", mindx, indx); */ - /* Copy value element over to input array */ - memcpy(input_data+indx,vptr,delsize); - if (objarray) { - Py_INCREF(*((PyObject **)vptr)); - } - vptr += delsize; - copied += 1; - /* If we move past value data. Reset */ - if (copied >= numvals) { - vptr = avals_data; - copied = 0; - } - } - mptr += melsize; - } -} - -/* * Returns input array with values inserted sequentially into places * indicated by the mask */ NPY_NO_EXPORT PyObject * arr_insert(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwdict) { - PyObject *mask = NULL, *vals = NULL; - PyArrayObject *ainput = NULL, *amask = NULL, *avals = NULL, *tmp = NULL; - int numvals, totmask, sameshape; - char *input_data, *mptr, *vptr, *zero = NULL; - int melsize, delsize, nd, objarray, k; - npy_intp *instrides, *inshape; + char *src, *dest; + npy_bool *mask_data; + PyArray_Descr *dtype; + PyArray_CopySwapFunc *copyswap; + PyObject *array0, *mask0, *values0; + PyArrayObject *array, *mask, *values; + npy_intp i, j, chunk, nm, ni, nv; static char *kwlist[] = {"input", "mask", "vals", NULL}; + NPY_BEGIN_THREADS_DEF; + values = mask = NULL; - if (!PyArg_ParseTupleAndKeywords(args, kwdict, "O&OO", kwlist, - PyArray_Converter, &ainput, - &mask, &vals)) { - goto fail; + if (!PyArg_ParseTupleAndKeywords(args, kwdict, "O!OO:place", kwlist, + &PyArray_Type, &array0, &mask0, &values0)) { + return NULL; } - amask = (PyArrayObject *)PyArray_FROM_OF(mask, NPY_ARRAY_CARRAY); - if (amask == NULL) { + array = (PyArrayObject *)PyArray_FromArray((PyArrayObject *)array0, NULL, + NPY_ARRAY_CARRAY | NPY_ARRAY_UPDATEIFCOPY); + if (array == NULL) { goto fail; } - /* Cast an object array */ - if (PyArray_DESCR(amask)->type_num == NPY_OBJECT) { - tmp = (PyArrayObject *)PyArray_Cast(amask, NPY_INTP); - if (tmp == NULL) { - goto fail; - } - Py_DECREF(amask); - amask = tmp; - } - sameshape = 1; - if (PyArray_NDIM(amask) == PyArray_NDIM(ainput)) { - for (k = 0; k < PyArray_NDIM(amask); k++) { - if (PyArray_DIMS(amask)[k] != PyArray_DIMS(ainput)[k]) { - sameshape = 0; - } - } - } - else { - /* Test to see if amask is 1d */ - if (PyArray_NDIM(amask) != 1) { - sameshape = 0; - } - else if ((PyArray_SIZE(ainput)) != PyArray_SIZE(amask)) { - sameshape = 0; - } - } - if (!sameshape) { - PyErr_SetString(PyExc_TypeError, - "mask array must be 1-d or same shape as input array"); + ni = PyArray_SIZE(array); + dest = PyArray_DATA(array); + chunk = PyArray_DESCR(array)->elsize; + mask = (PyArrayObject *)PyArray_FROM_OTF(mask0, NPY_BOOL, + NPY_ARRAY_CARRAY | NPY_ARRAY_FORCECAST); + if (mask == NULL) { goto fail; } - avals = (PyArrayObject *)PyArray_FromObject(vals, - PyArray_DESCR(ainput)->type_num, 0, 1); - if (avals == NULL) { + nm = PyArray_SIZE(mask); + if (nm != ni) { + PyErr_SetString(PyExc_ValueError, + "place: mask and data must be " + "the same size"); goto fail; } - numvals = PyArray_SIZE(avals); - nd = PyArray_NDIM(ainput); - input_data = PyArray_DATA(ainput); - mptr = PyArray_DATA(amask); - melsize = PyArray_DESCR(amask)->elsize; - vptr = PyArray_DATA(avals); - delsize = PyArray_DESCR(avals)->elsize; - zero = PyArray_Zero(amask); - if (zero == NULL) { + + mask_data = PyArray_DATA(mask); + dtype = PyArray_DESCR(array); + Py_INCREF(dtype); + + values = (PyArrayObject *)PyArray_FromAny(values0, dtype, + 0, 0, NPY_ARRAY_CARRAY, NULL); + if (values == NULL) { goto fail; } - objarray = (PyArray_DESCR(ainput)->type_num == NPY_OBJECT); - if (!numvals) { - /* nothing to insert! fail unless none of mask is true */ - const char *iter = mptr; - const char *const last = iter + PyArray_NBYTES(amask); - while (iter != last && !memcmp(iter, zero, melsize)) { - iter += melsize; + nv = PyArray_SIZE(values); /* zero if null array */ + if (nv <= 0) { + npy_bool allFalse = 1; + i = 0; + + while (allFalse && i < ni) { + if (mask_data[i]) { + allFalse = 0; + } else { + i++; + } } - if (iter != last) { + if (!allFalse) { PyErr_SetString(PyExc_ValueError, - "Cannot insert from an empty array!"); + "Cannot insert from an empty array!"); goto fail; + } else { + Py_XDECREF(values); + Py_XDECREF(mask); + Py_RETURN_NONE; } - goto finish; } - /* Handle zero-dimensional case separately */ - if (nd == 0) { - if (memcmp(mptr,zero,melsize) != 0) { - /* Copy value element over to input array */ - memcpy(input_data,vptr,delsize); - if (objarray) { - Py_INCREF(*((PyObject **)vptr)); + src = PyArray_DATA(values); + j = 0; + + copyswap = PyArray_DESCR(array)->f->copyswap; + NPY_BEGIN_THREADS_DESCR(PyArray_DESCR(array)); + for (i = 0; i < ni; i++) { + if (mask_data[i]) { + if (j >= nv) { + j = 0; } - } - Py_DECREF(amask); - Py_DECREF(avals); - PyDataMem_FREE(zero); - Py_DECREF(ainput); - Py_RETURN_NONE; - } - totmask = (int) PyArray_SIZE(amask); - instrides = PyArray_STRIDES(ainput); - inshape = PyArray_DIMS(ainput); - if (objarray) { - /* object array, need to refcount, can't release the GIL */ - arr_insert_loop(mptr, vptr, input_data, zero, PyArray_DATA(avals), - melsize, delsize, objarray, totmask, numvals, nd, - instrides, inshape); - } - else { - /* No increfs take place in arr_insert_loop, so release the GIL */ - NPY_BEGIN_ALLOW_THREADS; - arr_insert_loop(mptr, vptr, input_data, zero, PyArray_DATA(avals), - melsize, delsize, objarray, totmask, numvals, nd, - instrides, inshape); - NPY_END_ALLOW_THREADS; + copyswap(dest + i*chunk, src + j*chunk, 0, array); + j++; + } } + NPY_END_THREADS; -finish: - Py_DECREF(amask); - Py_DECREF(avals); - PyDataMem_FREE(zero); - Py_DECREF(ainput); + Py_XDECREF(values); + Py_XDECREF(mask); + Py_DECREF(array); Py_RETURN_NONE; -fail: - PyDataMem_FREE(zero); - Py_XDECREF(ainput); - Py_XDECREF(amask); - Py_XDECREF(avals); + fail: + Py_XDECREF(mask); + Py_XDECREF(array); + Py_XDECREF(values); return NULL; } diff --git a/numpy/lib/function_base.py b/numpy/lib/function_base.py index c155babef..648eb5019 100644 --- a/numpy/lib/function_base.py +++ b/numpy/lib/function_base.py @@ -2026,7 +2026,8 @@ def place(arr, mask, vals): vals : 1-D sequence Values to put into `a`. Only the first N elements are used, where N is the number of True values in `mask`. If `vals` is smaller - than N it will be repeated. + than N, it will be repeated, and if elements of `a` are to be masked, + this sequence must be non-empty. See Also -------- diff --git a/numpy/lib/tests/test_function_base.py b/numpy/lib/tests/test_function_base.py index e04a497c1..945992fc0 100644 --- a/numpy/lib/tests/test_function_base.py +++ b/numpy/lib/tests/test_function_base.py @@ -805,6 +805,11 @@ class TestExtins(TestCase): assert_raises_regex(ValueError, "Cannot insert from an empty array", lambda: place(a, [0, 0, 0, 0, 0, 1, 0], [])) + # See Issue #6974 + a = np.array(['12', '34']) + place(a, [0, 1], '9') + assert_array_equal(a, ['12', '9']) + def test_both(self): a = rand(10) mask = a > 0.5 |