Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

F# - Sort a matrix containing tuples

I do not find a way to sort the values included in the columns of the following matrix of tuples :

Matrix<float * float> =
  matrix [[(1.0, 145.0); (1.0, 45.0); (1.0, 130.0); (1.0, 30.0); (1.0, 130.0)]
          [(2.0, 45.0); (2.0, 45.0); (2.0, 30.0); (2.0, 30.0); (2.0, 30.0)]
          [(3.0, 130.0); (3.0, 30.0); (3.0, 145.0); (3.0, 45.0); (3.0, 130.0)]
          [(4.0, 30.0); (4.0, 30.0); (4.0, 45.0); (4.0, 45.0); (4.0, 30.0)]
          [(5.0, 130.0); (5.0, 30.0); (5.0, 130.0); (5.0, 30.0); (5.0, 145.0)]]

I would like to sort each column depending on the second element of the tuple. For example here the answer would be :

   matrix [[(1.0, 145.0); (1.0, 45.0); (3.0, 145.0); (3.0, 45.0); (5.0, 145.0)]
          [(3.0, 130.0); (2.0, 45.0); (1.0, 130.0); (4.0, 45.0); (1.0, 130.0)]
          [(5.0, 130.0); (3.0, 30.0); (5.0, 130.0); (1.0, 30.0); (3.0, 130.0)]
          [(2.0, 45.0); (4.0, 30.0); (4.0, 45.0); (2.0, 30.0); (2.0, 30.0)]
          [(4.0, 30.0); (5.0, 30.0); (2.0, 30.0); (5.0, 30.0); (4.0, 30.0)]]

Thank you in advance !

like image 472
katter75 Avatar asked Jun 25 '26 05:06

katter75


1 Answers

In my experience, when working with arrays (2D and/or matrix) I found that working with arrays internally is often the fastest way to go.

For example, combining Daniel's and Ankur's approaches in a mutable way:

let mutableSortByCol f (m:Matrix<'T>) =
    let columns = [| for c in 0 .. m.NumCols - 1 -> 
                         m.Column c |> Vector.Generic.toArray |]

    for c in 0 .. m.NumCols - 1 do 
        columns.[c] |> Array.sortInPlaceBy f

    Matrix.Generic.init (m.NumRows) (m.NumCols) (fun r c -> columns.[c].[r])

I converted the matrix to an array of columns ('a[][], not 'a[,]), and performed an in-place sort on each column. After that, I filled a new matrix with the sorted result. Note that the original matrix remains unmodified: the columns array is populated with copies of the column vectors (Vector.toArray creates a new array).

This approach is faster because it needs no transposes, sorts columns in place, and needs no conversion to and from intermediate list structures by keeping everything array-oriented. I suspect it could be made even faster if the Matrix module supported conversion to/from 'a[][] as well, although it's perhaps not really suited for matrices.

Also, in case you didn't know: you can make use of F#'s structural comparison of tuples to sort by second element descending, first element ascending:

Example:

> mutableSortByCol (fun (a,b) -> (-b,a)) M;;
val it : Matrix<float * float> =
  matrix [[(1.0, 145.0); (1.0, 45.0); (3.0, 145.0); (3.0, 45.0); (5.0, 145.0)]
          [(3.0, 130.0); (2.0, 45.0); (1.0, 130.0); (4.0, 45.0); (1.0, 130.0)]
          [(5.0, 130.0); (3.0, 30.0); (5.0, 130.0); (1.0, 30.0); (3.0, 130.0)]
          [(2.0, 45.0); (4.0, 30.0); (4.0, 45.0); (2.0, 30.0); (2.0, 30.0)]
          [(4.0, 30.0); (5.0, 30.0); (2.0, 30.0); (5.0, 30.0); (4.0, 30.0)]]
like image 161
cfern Avatar answered Jun 28 '26 17:06

cfern



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!