Repository: numba/numba

Issue:
Support np.delete

<!--

Thanks for opening an issue! To help the Numba team handle your information
efficiently, please first ensure that there is no other issue present that
already describes the issue you have
(search at https://github.com/numba/numba/issues?&q=is%3Aissue).

For more general "how do I do X?" type questions, please speak to us in real
time on https://gitter.im/numba/numba or post to the Numba mailing list
https://groups.google.com/a/continuum.io/forum/#!forum/numba-users.

-->

## Feature request

From https://stackoverflow.com/questions/55103396/delete-element-from-array-numba

as title.

<!--

Please include details of the feature you would like to see, why you would
like to see it/the use case

-->



Discussion:
Hi, I can work on this one! 
@guilhermeleobas great, thanks!

As a new contributor, some instructions for getting set up for development are [here](http://numba.pydata.org/numba-doc/latest/developer/contributing.html#getting-set-up) and are a great place to start.

The use of Numba's extension API `@overload` decorator is strongly recommended for this task, documentation is [here](http://numba.pydata.org/numba-doc/latest/extending/high-level.html#implementing-functions).

It may be useful to take a look at examples of pull requests adding support for a NumPy function that have already been done if guidance is needed... here's a couple:

* `np.roll`: https://github.com/numba/numba/pull/3563/files
* `np.roots`: https://github.com/numba/numba/pull/2135/files

If you get stuck/need a hand, the Numba core developers are also available on [gitter.im](https://gitter.im/numba/numba) real time chat.

Thanks again!

Resolving patch:
diff --git a/numba/targets/arraymath.py b/numba/targets/arraymath.py
--- a/numba/targets/arraymath.py
+++ b/numba/targets/arraymath.py
@@ -2782,6 +2782,56 @@ def np_imag_impl(a):
 #----------------------------------------------------------------------------
 # Misc functions
 
+np_delete_handler_isslice = register_jitable(lambda x : x)
+np_delete_handler_isarray = register_jitable(lambda x : np.asarray(x))
+
+@overload(np.delete)
+def np_delete(arr, obj):
+    # Implementation based on numpy
+    # https://github.com/numpy/numpy/blob/af66e487a57bfd4850f4306e3b85d1dac3c70412/numpy/lib/function_base.py#L4065-L4267
+
+    if not isinstance(arr, (types.Array, types.Sequence)):
+        raise TypingError("arr must be either an Array or a Sequence")
+
+    if isinstance(obj, (types.Array, types.Sequence, types.SliceType)):
+        if isinstance(obj, (types.SliceType)):
+            handler = np_delete_handler_isslice 
+        else:
+            if not isinstance(obj.dtype, types.Integer):
+                raise TypingError('obj should be of Integer dtype')
+            handler = np_delete_handler_isarray
+
+        def np_delete_impl(arr, obj):
+            arr = np.ravel(np.asarray(arr))
+            N = arr.size
+
+            keep = np.ones(N, dtype=np.bool_)
+            obj = handler(obj)
+            keep[obj] = False
+            return arr[keep]
+        return np_delete_impl
+
+    else: # scalar value
+        if not isinstance(obj, types.Integer):
+            raise TypingError('obj should be of Integer dtype')
+
+        def np_delete_scalar_impl(arr, obj):
+            arr = np.ravel(np.asarray(arr))
+            N = arr.size
+            pos = obj
+
+            if (pos < -N or pos >= N):
+                raise IndexError('obj must be less than the len(arr)')
+                # NumPy raises IndexError: index 'i' is out of
+                # bounds for axis 'x' with size 'n'
+
+            if (pos < 0):
+                pos += N
+
+            return np.concatenate((arr[:pos], arr[pos+1:]))
+        return np_delete_scalar_impl
+
+
 @overload(np.diff)
 def np_diff_impl(a, n=1):
     if not isinstance(a, types.Array) or a.ndim == 0:


Test patch:
diff --git a/numba/tests/test_np_functions.py b/numba/tests/test_np_functions.py
--- a/numba/tests/test_np_functions.py
+++ b/numba/tests/test_np_functions.py
@@ -32,6 +32,10 @@ def angle2(x, deg):
     return np.angle(x, deg)
 
 
+def delete(arr, obj):
+    return np.delete(arr, obj)
+
+
 def diff1(a):
     return np.diff(a)
 
@@ -342,6 +346,69 @@ def check(x_types, x_values):
         x_types = [types.complex64, types.complex128]
         check(x_types, x_values)
 
+    def test_delete(self):
+
+        def arrays():
+            # array, obj
+            #
+            # an array-like type
+            yield [1, 2, 3, 4, 5], 3
+            yield [1, 2, 3, 4, 5], [2, 3]
+            # 1d array, scalar
+            yield np.arange(10), 3
+            yield np.arange(10), -3 # Negative obj
+            # 1d array, list
+            yield np.arange(10), [3, 5, 6]
+            yield np.arange(10), [2, 3, 4, 5]
+            # 3d array, scalar
+            yield np.arange(3 * 4 * 5).reshape(3, 4, 5), 2
+            # 3d array, list
+            yield np.arange(3 * 4 * 5).reshape(3, 4, 5), [5, 30, 27, 8]
+            # slices
+            yield [1, 2, 3, 4], slice(1, 3, 1)
+            yield np.arange(10), slice(10)
+
+        pyfunc = delete
+        cfunc = jit(nopython=True)(pyfunc)
+
+        for arr, obj in arrays():
+            expected = pyfunc(arr, obj)
+            got = cfunc(arr, obj)
+            self.assertPreciseEqual(expected, got)
+
+    def test_delete_exceptions(self):
+        pyfunc = delete
+        cfunc = jit(nopython=True)(pyfunc)
+        self.disable_leak_check()
+
+        with self.assertRaises(TypingError) as raises:
+            cfunc([1, 2], 3.14)
+        self.assertIn(
+            'obj should be of Integer dtype',
+            str(raises.exception)
+        )
+
+        with self.assertRaises(TypingError) as raises:
+            cfunc(np.arange(10), [3.5, 5.6, 6.2])
+        self.assertIn(
+            'obj should be of Integer dtype',
+            str(raises.exception)
+        )
+
+        with self.assertRaises(TypingError) as raises:
+            cfunc(2, 3)
+        self.assertIn(
+            'arr must be either an Array or a Sequence',
+            str(raises.exception)
+        )
+
+        with self.assertRaises(IndexError) as raises:
+            cfunc([1, 2], 3)
+        self.assertIn(
+            'obj must be less than the len(arr)',
+            str(raises.exception),
+        )
+
     def diff_arrays(self):
         """
         Some test arrays for np.diff()