Minez97
Minez97

Reputation: 67

Sorting a multi-dimensional numpy array

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

Answers (3)

Martin
Martin

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

BookYourLuck
BookYourLuck

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

Thibault D.
Thibault D.

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

Related Questions