Repository: numpy/numpy

Issue:
numpy.unique inconsistent behavior on array of tuples
I have noticed that numpy.unique have inconsistent behavior on array of tuples depending on whether return_inverse flag is set:

```
import numpy
a = [(1, 2), (1, 2), (2, 3)]
a1 = numpy.unique(a)
a2, a_inverse = numpy.unique(a, return_inverse=True)
print(a1)
print(a2)
```

The output is:

```
[[1 2]
 [2 3]]
[1 2 3]
```

It seems setting `return_inverse` will cause the array of tuples to be flattened, otherwise the array will not be flattened.  Could this be fixed so numpy.unique would have consistent behavior (ideally, not flatten array of tuples)?  Thanks!

python version: 2.7.6
numpy version: 1.8.1



Discussion:
A list of tuples is interpreted by numpy as a 2D array, so no, can't do. The inconsistency comes from `np.unique` having a special path for non-arrays without extra arguments, [see here](https://github.com/numpy/numpy/blob/v1.8.1/numpy/lib/arraysetops.py#L162), perhaps we need to rethink why `np.sort` is doing what it is doing. But it is the second output that is the correct one according to the documentation, not the first!

You can nevertheless get the performance you seem to be expecting by creating an array of object dtype:

```
In [10]: a = np.empty((3,), dtype=object)

In [11]: a[0] = (1,2)

In [12]: a[1] = (1, 2)

In [13]: a[2] = (2, 3)

In [14]: np.unique(a, return_inverse=True)
Out[14]: (array([(1, 2), (2, 3)], dtype=object), array([0, 0, 1]))
```

Thanks Jaime.  That explains a lot!

In [here](https://github.com/numpy/numpy/blob/v1.8.1/numpy/lib/arraysetops.py#L165), maybe instead of

```
        if not return_inverse and not return_index:
            return np.sort(list(set(ar)))
```

change it to

```
        if not return_inverse and not return_index:
            return np.sort(list(set(np.asarray(ar).flatten())))
```

I think the correct approach is to go for a 

```
return np.sort(list(set(ar)), axis=None)
```

will send a PR later today if no one beats me to it.


Resolving patch:
diff --git a/numpy/lib/arraysetops.py b/numpy/lib/arraysetops.py
--- a/numpy/lib/arraysetops.py
+++ b/numpy/lib/arraysetops.py
@@ -167,13 +167,7 @@ def unique(ar, return_index=False, return_inverse=False, return_counts=False):
     array([1, 2, 6, 4, 2, 3, 2])
 
     """
-    try:
-        ar = ar.flatten()
-    except AttributeError:
-        if not return_inverse and not return_index and not return_counts:
-            return np.sort(list((set(ar))))
-        else:
-            ar = np.asanyarray(ar).flatten()
+    ar = np.asanyarray(ar).flatten()
 
     optional_indices = return_index or return_inverse
     optional_returns = optional_indices or return_counts


Test patch:
diff --git a/numpy/lib/tests/test_arraysetops.py b/numpy/lib/tests/test_arraysetops.py
--- a/numpy/lib/tests/test_arraysetops.py
+++ b/numpy/lib/tests/test_arraysetops.py
@@ -97,6 +97,16 @@ def check_all(a, b, i1, i2, c, dt):
         aa = [1.+0.j, 1- 1.j, 1]
         assert_array_equal(np.unique(aa), [ 1.-1.j,  1.+0.j])
 
+        # test for ticket #4785
+        a = [(1, 2), (1, 2), (2, 3)]
+        unq = [1, 2, 3]
+        inv = [0, 1, 0, 1, 1, 2]
+        a1 = unique(a)
+        assert_array_equal(a1, unq)
+        a2, a2_inv = unique(a, return_inverse=True)
+        assert_array_equal(a2, unq)
+        assert_array_equal(a2_inv, inv)
+
     def test_intersect1d(self):
         # unique inputs
         a = np.array([5, 7, 1, 2])