Ranking over each matrix column's sort in julia

Viewed 102

I have a matrix (m) of scores for 4 students on 3 different exams.

4 3 1
3 2 5
8 4 6
1 5 2

I want to know, for each student, the exams they did best to worse on. Desired output:

1 2 3
2 3 1
1 3 2
3 1 2

Now, I'm new to the language (and coding in general), so I read GeeksforGeeks' page on sorting in Julia and tried

mapslices(sortperm, -m; dims = 2)

However, this gives something subtly different: a matrix of each row being the index of the sorting.

1 2 3
3 1 2
1 3 2
2 3 1

Perhaps it was obvious, but I now realize this is not actually what I want, but I cannot find a built-in function/fast way to complete this operation. Any ideas? Preferably something which doesn't iterate through items in the matrix/row, as in reality my matrix is very, very large. Thanks!

2 Answers

Figured out something which works!

Run m_index_rank = mapslices(sortperm, -m; dims = 2) on the matrix and get a ranking for each row through index. Then, realizing this is, in each row, an inverse permutation away from the desired output, run mapslices(invperm, m_index_rank; dims = 2) for the desired result.

In one line, this is mapslices(r -> invperm(sortperm(r, rev=true)), m; dims=2) over the desired matrix m. dims = 2 is to carry out the operation row-wise.

I'm marking this resolved for now, but please let me know if there are cleaner/faster ways to do this.

Edit: Replaced my syntactically clunky mapslices(invperm, mapslices(sortperm, -m; dims = 2); dims = 2) with a more natural one, thanks to @phipsgabler

Such functionality is provided by StatsBase.jl. Here is an example:

julia> using StatsBase

julia> m = [4 3 1
            3 2 5
            8 4 6
            1 5 2]
4×3 Array{Int64,2}:
 4  3  1
 3  2  5
 8  4  6
 1  5  2

julia> mapslices(x -> ordinalrank(x, rev=true), m, dims = 2)
4×3 Array{Int64,2}:
 1  2  3
 2  3  1
 1  3  2
 3  1  2

You might want to use other rank, depending on how you want to split ties, see here for details.

Related