3

I have a 3d list of lists or numpy array and I need to sort it, by the smallest first item index. This are the last two tentatives I did on this program. Sorry, I am quite sure it is an easy/silly question, but as a newbie in programming 'way of thinking', it is kind of hard for me. First Try:

 lstsArray = [[[54,21,31], [1,2,3], [15,25,35]],
              [[12,22,32], [3,2,1], [16,26,36]],
              [[34,24,38], [0.1,1,1], [17,27,37]]]
 val = np.array(lstsArray)
 menor = 120e26
 for item in val:
     for i in item:
         if menor >= i[0] and i[0] >= min(i):
             menor = i[0]
 print(menor)
 lstA = list(val)
 a = sorted(lstA, key=itemgetter(menor))
 print(a)

Second Try

for i in val:
    for j in i:
        print(sorted((i), key =itemgetter(j[0])))

Desired Output

[[[0.1,1,1],[1,2,3],[3,2,1]],
 [[12,22,32],[15,25,35],[16,26,36]],
 [[17,27,37],[34,24,38],[54,21,31]]] 
Angel Lira
  • 293
  • 3
  • 12
  • So basically, flatten, sort, then put it back in the original structure. Do you know it's 3x3 or is it arbitrary structure? Does it have to be numpy? There might be something useful in https://stackoverflow.com/questions/32416594/python-unflatten-list-based-on-second-list – Kenny Ostrom Mar 19 '20 at 16:40
  • Hi Kenny, it has to be numpy, because the amount of data quite large. Then, I think numpy is faster, right?! – Angel Lira Mar 19 '20 at 17:55
  • `numpy` isn't always faster than Python list methods, especially if you have to first convert a list to array. With this sample, the `0.1` value means that the whole `array` will be float dtype. – hpaulj Mar 19 '20 at 19:16
  • @MadPhysicist, I'm going to reopen this because the dtypes and nesting in this list raise some issues that go beyond a simple use of `argsort`. – hpaulj Mar 19 '20 at 19:34
  • @hpaulj. I think your answer amply justifies reopening. I wouldn't expect OP to go from any of the duplicate answers to your proposal on their own – Mad Physicist Mar 19 '20 at 20:50

1 Answers1

0

Your list, and array made from it. Note the floats in the array:

In [124]: lstsArray = [[[54,21,31], [1,2,3], [15,25,35]], 
     ...:               [[12,22,32], [3,2,1], [16,26,36]], 
     ...:               [[34,24,38], [0.1,1,1], [17,27,37]]]                                                         
In [125]: val=np.array(lstsArray)                                                                                    
In [126]: val                                                                                                        
Out[126]: 
array([[[54. , 21. , 31. ],
        [ 1. ,  2. ,  3. ],
        [15. , 25. , 35. ]],

       [[12. , 22. , 32. ],
        [ 3. ,  2. ,  1. ],
        [16. , 26. , 36. ]],

       [[34. , 24. , 38. ],
        [ 0.1,  1. ,  1. ],
        [17. , 27. , 37. ]]])

This is a (3,3,3) shaped array. But your sorting ignores the initial (3,3) layout, so let's go ahead and reshape it:

In [133]: val = np.array(lstsArray).reshape(-1,3)                                                                    
In [134]: val                                                                                                        
Out[134]: 
array([[54. , 21. , 31. ],
       [ 1. ,  2. ,  3. ],
       [15. , 25. , 35. ],
       [12. , 22. , 32. ],
       [ 3. ,  2. ,  1. ],
       [16. , 26. , 36. ],
       [34. , 24. , 38. ],
       [ 0.1,  1. ,  1. ],
       [17. , 27. , 37. ]])

Now we can easily reshape on the first column value. argsort gives the sort order:

In [135]: idx = np.argsort(val[:,0])                                                                                 
In [136]: idx                                                                                                        
Out[136]: array([7, 1, 4, 3, 2, 5, 8, 6, 0])
In [137]: val[idx]                                                                                                   
Out[137]: 
array([[ 0.1,  1. ,  1. ],
       [ 1. ,  2. ,  3. ],
       [ 3. ,  2. ,  1. ],
       [12. , 22. , 32. ],
       [15. , 25. , 35. ],
       [16. , 26. , 36. ],
       [17. , 27. , 37. ],
       [34. , 24. , 38. ],
       [54. , 21. , 31. ]])

and to get it back to 3d:

In [138]: val[idx].reshape(3,3,3)                                                                                    
Out[138]: 
array([[[ 0.1,  1. ,  1. ],
        [ 1. ,  2. ,  3. ],
        [ 3. ,  2. ,  1. ]],

       [[12. , 22. , 32. ],
        [15. , 25. , 35. ],
        [16. , 26. , 36. ]],

       [[17. , 27. , 37. ],
        [34. , 24. , 38. ],
        [54. , 21. , 31. ]]])

or in list display:

In [139]: val[idx].reshape(3,3,3).tolist()                                                                           
Out[139]: 
[[[0.1, 1.0, 1.0], [1.0, 2.0, 3.0], [3.0, 2.0, 1.0]],
 [[12.0, 22.0, 32.0], [15.0, 25.0, 35.0], [16.0, 26.0, 36.0]],
 [[17.0, 27.0, 37.0], [34.0, 24.0, 38.0], [54.0, 21.0, 31.0]]]

But if the list had just one level of nesting:

In [140]: alist = val.tolist()                                                                                       
In [141]: alist                                                                                                      
Out[141]: 
[[54.0, 21.0, 31.0],
 [1.0, 2.0, 3.0],
 [15.0, 25.0, 35.0],
 [12.0, 22.0, 32.0],
 [3.0, 2.0, 1.0],
 [16.0, 26.0, 36.0],
 [34.0, 24.0, 38.0],
 [0.1, 1.0, 1.0],
 [17.0, 27.0, 37.0]]

the python sorted works quite nicely:

In [142]: sorted(alist, key=lambda x:x[0])   # or itemgetter                                                                           
Out[142]: 
[[0.1, 1.0, 1.0],
 [1.0, 2.0, 3.0],
 [3.0, 2.0, 1.0],
 [12.0, 22.0, 32.0],
 [15.0, 25.0, 35.0],
 [16.0, 26.0, 36.0],
 [17.0, 27.0, 37.0],
 [34.0, 24.0, 38.0],
 [54.0, 21.0, 31.0]]

The fact that you have a double nested list, but want the sort to ignore one layer, complicates the list processing. That's where numpy reshape helps a lot.

For now I won't test the relative speeds of these approaches.

hpaulj
  • 221,503
  • 14
  • 230
  • 353