user1940040
user1940040

Reputation:

How to sort numpy arrays in arrays?

This question relates closely to this one. One could say it can be solved with np.lexsort or sorting on np.recarray but not in a trivial and pythonic way.

I have a numpy array, for instance this one:

array([[[  2,  7,  1],
        [ 35,  9,  1],
        [ 22, 12,  4]],

       [[ 12,  2,  3],
        [  3,  7,  8],
        [ 12,  1, 10]]])

I would love to get that one as an output:

array([[[  2,  7,  1],
        [ 22, 12,  4],
        [ 35,  9,  1]],

       [[  3,  7,  8],
        [ 12,  1, 10],
        [ 12,  2,  3]]])

that is, to order each inner array according to their 1st, 2nd and 3rd values. Note that I don't want to sort by column (no table-like sorting). This is not wanted:

array([[[  2,  7,  1],
        [ 22,  9,  1],
        [ 35, 12,  4]],

       [[  3,  1,  3],
        [ 12,  2,  8],
        [ 12,  7, 10]]])

In other terms, what I want is np.sort_complex but for for higher dimensional complex-like type.

To me, the more sensible way is to create a np.recarray from my 3D arrays. The problem is that I don't know how to do that in a cheap way. How would you quickly transform one of the arrays displayed here in:

array([[(  2,  7,  1),
        ( 35,  9,  1),
        ( 22, 12,  4)],

       [( 12,  2,  3),
        (  3,  7,  8),
        ( 12,  1, 10)]], dtype=???)

with a correct dtype ([("c1", "f8"), ("c2", "f8"), ("c3", "f8")]-like but taking the higher dimension into account)?

np.lexarray behaves quite strangely with high dimension arrays and I could not make it to work.

np.argsort is not the answer either, because it does not sort in a stable way (no "draw" always a 1st, a 2nd and a 3rd).

I came out with a "solution" in pure python which is painfuly slow, any other idea?

Upvotes: 3

Views: 301

Answers (2)

Saullo G. P. Castro
Saullo G. P. Castro

Reputation: 58865

You can use a small list comprehension and lexsort():

np.array([b[np.lexsort(b[:,::-1].T)] for b in a])
#array([[[ 2,  7,  1],
#        [22, 12,  4],
#        [35,  9,  1]],
# 
#       [[ 3,  7,  8],
#        [12,  1, 10],
#        [12,  2,  3]]])

Upvotes: 0

Jaime
Jaime

Reputation: 67417

There are a lot of things that can go wrong, more on that later, but in your case you can sort the array as you want in a very easy manner:

>>> a = np.array([[[  2,  7,  1],
...                [ 35,  9,  1],
...                [ 22, 12,  4]],
... 
...               [[ 12,  2,  3],
...                [  3,  7,  8],
...                [ 12,  1, 10]]])
>>> a_view = a.view(dtype=[('', a.dtype)]*a.shape[-1])
>>> a_view.sort(axis=1)
>>> a
array([[[ 2,  7,  1],
        [22, 12,  4],
        [35,  9,  1]],

       [[ 3,  7,  8],
        [12,  1, 10],
        [12,  2,  3]]])

For this to work, the axis you want to use to resolve draws has to be the last array, and the array must be contiguous. So to get this right regardless of what the history of a is, doing a = np.ascontiguousarray(a) is probably the safe thing to do.

Upvotes: 3

Related Questions