Giorgos Lazaridis
Giorgos Lazaridis

Reputation: 63

Sort array in Python without modifying specific element positions

I have a numpy array in Python which is n-by-n (in the example is 3-by-3)and contains zero values in all the diagonal positions. e.g

array([[ 0.   , -0.65  ,  1.3 ,   0.56],
       [ 0.45 ,  0.    ,  0.54,   43   ],
       [ 0.5  ,  0.12  ,  0.  ,   7    ]
       [ 0.2  ,  0.3  ,  0.4  ,   0    ]])

Is it possible to sort the array without modifying the diagonal positions so as to look like the one below? Because all of the sorting functions will take into account the "zeros" that exist in the diagonal positions and will change their relative position.

array([[ 0.   , 1.3    ,  0.56  ,   -0.65],
       [ 43   ,  0.    ,  0.54  ,   0.45   ],
       [ 7    ,  0.5   ,  0.    ,   0.12    ]
       [ 0.4  ,  0.3  ,  0.2    ,   0    ]])

If the above operation cannot be done, then the N maximum values and their corresponding indexes in each row could suffice.

Till now i have tried sort and argsort but with no result.

Upvotes: 5

Views: 376

Answers (2)

xnx
xnx

Reputation: 25538

I'm a little late to this question, but if you're looking for a NumPy-only solution, you could substitute inf for your diagonal, sort in your chosen order, and then shuffle the inf column back to the diagonal:

In [189]: a = np.array([[ 0.   , -0.65  ,  1.3 ,   0.56],
   .....:        [ 0.45 ,  0.    ,  0.54,   43   ],
   .....:        [ 0.5  ,  0.12  ,  0.  ,   7    ],
   .....:        [ 0.2  ,  0.3  ,  0.4  ,   0    ]])

In [190]: np.fill_diagonal(a,np.inf)

In [191]: a.sort()

In [192]: a = a[:,::-1]

In [193]: for i in range(1,len(a)):
   .....:         a[i,:i+1] = np.roll(a[i,:i+1], i)
   .....:     

In [194]: np.fill_diagonal(a, 0)

In [195]: a
Out[195]: 
array([[  0.  ,   1.3 ,   0.56,  -0.65],
   [ 43.  ,   0.  ,   0.54,   0.45],
   [  7.  ,   0.5 ,   0.  ,   0.12],
   [  0.4 ,   0.3 ,   0.2 ,   0.  ]])

Upvotes: 2

kevingessner
kevingessner

Reputation: 18985

The easiest approach is to remove the zeroes, sort, then add the zeroes back along the diagonal:

>>> a = [[0,1,2],[3,0,0],[5,6,0]]
>>> no_zeroes = [r[:i] + r[i+1:] for i, r in enumerate(a)]
>>> no_zeroes
[[1, 2], [3, 0], [5, 6]]
>>> sorted_no_zeroes = [sorted(r, reverse=True) for r in no_zeroes]
>>> sorted_no_zeroes
[[2, 1], [3, 0], [6, 5]]
>>> sorted_with_zeroes = [r[:i] + [0] + r[i:] for i, r in enumerate(sorted_no_zeroes)]
>>> sorted_with_zeroes
[[0, 2, 1], [3, 0, 0], [6, 5, 0]]

Wrapped into a function:

>>> def sort_ignoring_zeroes(a):
...  s = [sorted(r[:i] + r[i+1:], reverse=True) for i, r in enumerate(a)]
...  return [r[:i] + [0] + r[i:] for i, r in enumerate(s)]
...
>>> sort_ignoring_zeroes(
          [[ 0.   , 1.3    ,  0.56  ,   -0.65],
...        [ 43   ,  0.    ,  0.54  ,   0.45],
...        [ 7    ,  0.5   ,  0.    ,   0.12]])
[[0, 1.3, 0.56, -0.65],
 [43, 0, 0.54, 0.45],
 [7, 0.5, 0, 0.12]]
>>>

Upvotes: 1

Related Questions