Reputation: 67
I would like to sort a 2x2 numpy array by the x coordinates. My goal is to obtain an array sorted from the smallest X value to the highest inside each couple of points and, at the same time, using all the value of the array
The array has been created using this line of code:
rect = np.empty((4, 2, 2))
The actual output of value inside array is:
[[[ -1000 , 97 ] #x0,y0 rect 0
[999 , 98]] #x1,y1 rect 0
[[410 , -1048] #x0,y0 rect 1
[619 , 940]] #x1,y1 rect 1
[[-1000, 226]
[999 , 227]]
[[229 , -983]
[55 , 1008]]]
The desire output is to sort by the smallest value of X inside each couple of points that form a rect and then, sort by the X considering all the rect like this:
[[[ -1000 , 97 ]
[999 , 98]]
[[-1000, 226]
[999 , 227]]
[[55 , 1008]
[229 , -983]]
[[410 , -1048]
[619 , 940]]]
Upvotes: 1
Views: 1546
Reputation: 3385
Goodbye loops and lambdas, welcome speed
import numpy as np
original_array = np.array([[[ -1000 , 97 ],[999 , 98]],
[[410 , -1048],[619 , 940]], #original_array1,y1 rect 1
[[-1000, 226],[999 , 227]],
[[229 , -983],[55 , 1008]]])
#get indices of sorted x0 and x1 (0 means first element of both arrays)
# indices are 2 arrays - [0,0,0,1], [1,1,1,0],
# so you can see that last element needs to reposition
indices = np.argsort(original_array[:,:,0],axis=1)
#create new array
new = np.empty(shape=[4,2,2])
#move on correct posisitions sub arrays
#np.arange only create artifical indices for each rect
new[:,0,:]=original_array[np.arange(original_array.shape[0]),indices[:,0],:]
new[:,1,:]=original_array[np.arange(original_array.shape[0]),indices[:,1],:]
#When subarrays are sorted, sort parent arrays
final_sorted_array = new[new[:,0,0].argsort(),:,:]
print(final_sorted_array)
[[[-1000. 97.]
[ 999. 98.]]
[[-1000. 226.]
[ 999. 227.]]
[[ 55. 1008.]
[ 229. -983.]]
[[ 410. -1048.]
[ 619. 940.]]]
Upvotes: 4
Reputation: 330
If you want to do it without creating additional copies of the array, you can do it using a combination of argsort
and indexing.
import numpy as np
data = np.array(
[[[ -1000 , 97 ],
[999 , 98]],
[[410 , -1048],
[619 , 940]],
[[-1000, 226] ,
[999 , 227]],
[[229 , -983],
[55 , 1008]]])
def sortrect(rect):
x = rect[:, 0]
idx = np.argsort(x)
rect[:] = rect[idx]
for a in data:
sortrect(a)
minx = data[:, 0, 0]
idx = np.argsort(minx)
data[:] = data[idx]
The same thing, without loops but less pedagogical (kudos to Martin for the argsort with axis):
idx0 = np.arange(data.shape[0])[:, np.newaxis]
idx1 = np.argsort(data[:, :, 0], axis=1)
data = data[idx0, idx1]
minx = data[:, 0, 0]
idx = np.argsort(minx)
data[:] = data[idx]
An expression of the form
out = data[idx0, idx1]
means
for all i, j:
out[i, j] = data[idx0[i, j], idx1[i, j]].
See https://docs.scipy.org/doc/numpy/reference/arrays.indexing.html#integer-array-indexing for further details.
Upvotes: 5
Reputation: 1637
You can use the key
parameter of the sort function for this:
l = [[[ -1000 , 97 ],[999 , 98]],
[[410 , -1048], [619 , 940]],
[[-1000, 226],[999 , 227]],
[[229 , -983],[55 , 1008]]]
sorted(l, key=lambda x: (min(x[0][0], x[1][0]), max(x[0][0],x[1][0])))
>>> [[[-1000, 97], [999, 98]],
[[-1000, 226], [999, 227]],
[[229, -983], [55, 1008]],
[[410, -1048], [619, 940]]]
The lambda inside the sorted creates a tuple containing minimum and maximum value of x
And if you are working with Numpy, you can write something that generalize better in higher dimensions:
sorted(l, key=lambda x: sorted(x[..., 0]))
>>> [array([[-1000, 97], [ 999, 98]]),
array([[-1000, 226], [ 999, 227]]),
array([[ 229, -983], [ 55, 1008]]),
array([[ 410, -1048], [ 619, 940]])]
This one works even if you have more than 2 points for defining your shape and will sort by minimum x
value
EDIT: Correction to sort inner points inside a rectangle:
sorted(np.sort(l, axis=1), key=lambda x: tuple(x[..., 0]))
Upvotes: 1