Reputation:
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
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
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