Repository: numpy/numpy

Issue:
argpartition fails on non-ndarray array-likes
While `partition` works, as advertised, array-likeinputs like, e.g. lists:

```
>>> np.partition([5, 4, 3, 2, 1], 2)
array([1, 2, 3, 4, 5])
```

`argpartition` raises an error if its first argument is not an `ndarray`:

```
>>> np.argpartition([5, 4, 3, 2, 1], 2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\WinPython-32bit-sp-dev\python-2.7.5\lib\site-packages\numpy\core\fromnumeric.py", line 680, in argpartition
    return a.argpartition(kth, axis, kind=kind, order=order)
AttributeError: 'list' object has no attribute 'argpartition'
```



Discussion:
If any array-like inputs are to be accepted, then as in `argsort`, this can be implemented:

```
try:
    argpartition = a.argpartition
except AttributeError:
    return _wrapit(a, 'argpartition', kth, axis, kind, order)
return argpartition(kth, axis, kind=kind, order=order)
```

else it can be like `sort` and `partition`:

```
if axis is None:
    a = asanyarray(a).flatten()
    axis = 0
else:
    a = asanyarray(a).copy(order="K")
return a.argpartition(kth, axis, kind=kind, order=order)
```

Just asking out of curiosity, the following example is shown in the documentation : 

```
>>> x = np.array([3, 4, 2, 1])
>>> x[np.argpartition(x, 3)]
array([2, 1, 3, 4])
```

Now, if any array-like inputs are allowed, then the following behavior is needed.

```
>>> x = [3, 4, 2, 1]
>>>np.array(x)[np.argpartition(x, 3)]
array([2, 1, 3, 4])
>>>x[np.argpartition(x, 3)]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: only integer arrays with one element can be converted to an index
```

If this is the procedure to be followed, as in case of `argsort`, I think it should be documented. Please correct me if no such use is needed in general scenarios.

it probably should be like argsort, Don't remember why its not, maybe just a mistake, I can't remember there being a reason. compared to sort, partition can't be overridden from the C side but that shouldn't matter here.

I think the `partition` and `sort` C API functions are defined in `item_selection.c`. Can you please clarify on what `compared to sort, partition can't be overridden from the C side` means ? Could I have a go at fixing this ?

no, that is kind of intentional the C sort functions are part of a public struct, adding partition would break the ABI and just add more stuff that should not be in that place.
So I went with a internal lookupt able that could get hooks for user functions. But I don't think its really worthwhile, I don't think anybody would use it.

@juliantaylor Thanks for the clarification. I am new to working with these kind of projects and wasn't aware of the nature of public struct functions and the way in which they are maintained.


Resolving patch:
diff --git a/numpy/core/fromnumeric.py b/numpy/core/fromnumeric.py
--- a/numpy/core/fromnumeric.py
+++ b/numpy/core/fromnumeric.py
@@ -691,8 +691,16 @@ def argpartition(a, kth, axis=-1, kind='introselect', order=None):
     >>> x[np.argpartition(x, (1, 3))]
     array([1, 2, 3, 4])
 
+    >>> x = [3, 4, 2, 1]
+    >>> np.array(x)[np.argpartition(x, 3)]
+    array([2, 1, 3, 4])
+
     """
-    return a.argpartition(kth, axis, kind=kind, order=order)
+    try:
+        argpartition = a.argpartition
+    except AttributeError:
+        return _wrapit(a, 'argpartition',kth, axis, kind, order)
+    return argpartition(kth, axis, kind=kind, order=order)
 
 
 def sort(a, axis=-1, kind='quicksort', order=None):


Test patch:
diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py
--- a/numpy/core/tests/test_multiarray.py
+++ b/numpy/core/tests/test_multiarray.py
@@ -1748,6 +1748,12 @@ def test_partition_fuzz(self):
                assert_array_equal(np.partition(d, kth)[kth], tgt,
                                   err_msg="data: %r\n kth: %r" % (d, kth))
 
+    def test_argpartition_gh5524(self):
+        #  A test for functionality of argpartition on lists.
+        d = [6,7,3,2,9,0]
+        p = np.argpartition(d,1)
+        self.assert_partitioned(np.array(d)[p],[1])
+
     def test_flatten(self):
         x0 = np.array([[1, 2, 3], [4, 5, 6]], np.int32)
         x1 = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]], np.int32)