Search code examples
pythonarrayssortingmultidimensional-array

How do I sort a 2D array in Python by multiple columns?


I have a two-dimensional (2D) Python array (array of arrays):

arr = [[130, 175, 75, 152],
 [96, 132, 122, 112],
 [174, 218, 141, 196],
 [661, 701, 21, 683],
 [707, 746, 375, 724],
 [957, 998, 305, 980],
 [768, 806, 26, 788],
 [957, 998, 394, 974],
 [768, 806, 286, 787],
 [174, 218, 328, 194],
 [894, 933, 80, 914],
 [130, 175, 182, 152],
 [96, 132, 329, 114],
 [894, 933, 166, 913]]

and I want to sort it hierarchically first by column 1, then by column 2, then by column 3, like a pivot table or multiple column sort in Excel.

The result should be:

[[96, 132, 122, 112],
 [96, 132, 329, 114],
 [130, 175, 75, 152],
 [130, 175, 182, 152],
 [174, 218, 141, 196],
 [174, 218, 328, 194],
 [661, 701, 21, 683],
 [707, 746, 375, 724],
 [768, 806, 26, 788],
 [768, 806, 286, 787],
 [894, 933, 80, 914],
 [894, 933, 166, 913],
 [957, 998, 305, 980],
 [957, 998, 394, 974]]

I used arr.sort() but I am not sure what goes on underneath and would like something more deterministic.

  1. How do I do that numerically (not lexicographically)?
  2. How would I do it on any column combination (in a general sense)?

Solution

    1. Python already sorts numbers numerically. If you want lexigraphic sort, you'd need to convert numbers to strings.

    2. Easiest way to sort by multiple columns is by employing the fact that Python guarantees sorts are stable - which means order of elements is kept in case they are tied in regards to sorting key. Using that, you can sort from least significant to most significant key, resulting in expected output:

    res = sorted(
            sorted(
                sorted(
                    sorted(arr, key = lambda x: x[3]),
                    key = lambda x: x[2]),
                key = lambda x: x[1]),
            key = lambda x: x[0])
    

    Output:

    [96, 132, 122, 112]
    [96, 132, 329, 114]
    [130, 175, 75, 152]
    [130, 175, 182, 152]
    [174, 218, 141, 196]
    [174, 218, 328, 194]
    [661, 701, 21, 683]
    [707, 746, 375, 724]
    [768, 806, 26, 788]
    [768, 806, 286, 787]
    [894, 933, 80, 914]
    [894, 933, 166, 913]
    [957, 998, 305, 980]
    [957, 998, 394, 974]