QuMiVe
QuMiVe

Reputation: 137

How to rearrange matrices?

I need to create a function, that will rearrange any square matrix based on the values in the matrix.

So if I have matrix like this:

M <- matrix(1:16, ncol = 4)
M
#>      [,1] [,2] [,3] [,4]
#> [1,]    1    5    9   13
#> [2,]    2    6   10   14
#> [3,]    3    7   11   15
#> [4,]    4    8   12   16

After rearrangement it needs to look like this:

      [,1] [,2] [,3] [,4]
[1,]    1    3    6   10
[2,]    2    5    9   13
[3,]    4    8   12   15
[4,]    7   11   14   16

So it is sorted from lowest (left upper corner) to highest (right lower corner), but the numbers are sorted on diagonal (is that the right word?) not in rows or columns.

I know how to to this "manually", but I can't figure out any rules that this rearrangement operates by.

Upvotes: 3

Views: 725

Answers (2)

G. Grothendieck
G. Grothendieck

Reputation: 270358

1) row(m) + col(m) is constant along reverse diagonals so:

M <- replace(m, order(row(m) + col(m)), m)

gving:

> M
     [,1] [,2] [,3] [,4]
[1,]    1    3    6   10
[2,]    2    5    9   13
[3,]    4    8   12   15
[4,]    7   11   14   16

It is not clear whether sorted on the diagonal means just that they are unravelled from the storage order onto the reverse diagonals or that they are actually sorted after that within each reverse diagonal. In the example in the question the two interpretations give the same answer; however, if you did wish to sort the result within reverse diagonal afterwards using different data then apply this:

ave(M, row(M) + col(M), FUN = sort)

2) A longer version:

M2 <- matrix(m[order(unlist(tapply(seq_along(m), row(m) + col(m), c)))], nrow(m))

Upvotes: 9

MSR
MSR

Reputation: 2901

Here's a function columns_to_diagonals in base R that ought to do what you're after. It uses split and unsplit with the appropriate factors.

columns_to_diagonals <- function(M) {
  n <- ncol(M)
  f <- matrix(rep(1:(2*n-1), c(1:n, (n-1):1)), ncol = n)
  m <- split(M, f)
  d <- row(M) + col(M)
  matrix(unsplit(m, d), ncol = n)
}

First, we may test this on your original case:

M <- matrix(1:16, ncol = 4)
columns_to_diagonals(M)
#>      [,1] [,2] [,3] [,4]
#> [1,]    1    3    6   10
#> [2,]    2    5    9   13
#> [3,]    4    8   12   15
#> [4,]    7   11   14   16

And then a larger, randomly permutated matrix, to check that this looks fine as well:

M <- matrix(sample(1:25), ncol = 5)
M
#>      [,1] [,2] [,3] [,4] [,5]
#> [1,]    4   15   12   10   21
#> [2,]   19    7    5   23    6
#> [3,]    9   17    2    8    1
#> [4,]    3   11   16   25   14
#> [5,]   22   18   20   13   24
columns_to_diagonals(M)
#>      [,1] [,2] [,3] [,4] [,5]
#> [1,]    4    9   15   18   20
#> [2,]   19   22   11   16   25
#> [3,]    3   17    2    8    6
#> [4,]    7    5   23   21   14
#> [5,]   12   10   13    1   24

Created on 2019-12-15 by the reprex package (v0.2.1)

Upvotes: 4

Related Questions