Home > Blockchain >  Expand a matrix of rankings (1 ~ 4) to a bigger binary matrix
Expand a matrix of rankings (1 ~ 4) to a bigger binary matrix

Time:07-17

I have a matrix which I want to convert to one with binary output (0 vs 1). The matrix to be converted contains four rows of rankings (1 to 4):

mat1.data <- c(4,   3,  3,  3,  3,  2,  2,  1,  1,  1,
               3,   4,  2,  4,  2,  3,  1,  3,  3,  2,
               2,   2,  4,  1,  1,  1,  4,  4,  2,  4,
               1,   1,  1,  2,  4,  4,  3,  2,  4,  3)
mat1 <- matrix(mat1.data,nrow=4,ncol=10,byrow=TRUE)
mat1
     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,]    4    3    3    3    3    2    2    1    1     1
[2,]    3    4    2    4    2    3    1    3    3     2
[3,]    2    2    4    1    1    1    4    4    2     4
[4,]    1    1    1    2    4    4    3    2    4     3

For each row in the input matrix, I want to create four binary rows - one row for each value of the ranks (1-4). In the binary matrix, each row-wise entry is 1 on positions where the focal rank occurs in the input matrix, and 0 otherwise. Each row from the original matrix should produce 10*4=40 entries in the output matrix.

For example, for the first row of in the input matrix...

4   3   3   3   3   2   2   1   1   1

...the output should be:

0   0   0   0   0   0   0   1   1   1 # Rank 1 in input
0   0   0   0   0   1   1   0   0   0 # Rank 2 in input
0   1   1   1   1   0   0   0   0   0 # Rank 3 in input
1   0   0   0   0   0   0   0   0   0 # Rank 4 in input

Continue with this process, the expected output for all four rows of rankings should look like this:

0   0   0   0   0   0   0   1   1   1 #first row of rankings starts
0   0   0   0   0   1   1   0   0   0
0   1   1   1   1   0   0   0   0   0
1   0   0   0   0   0   0   0   0   0 #first row of rankings ends
0   0   0   0   0   0   1   0   0   0 #second row of rankings starts
0   0   1   0   1   0   0   0   0   1
1   0   0   0   0   1   0   1   1   0
0   1   0   1   0   0   0   0   0   0 #second row of rankings ends
0   0   0   1   1   1   0   0   0   0 #third row of rankings starts
1   1   0   0   0   0   0   0   1   0
0   0   0   0   0   0   0   0   0   0
0   0   1   0   0   0   1   1   0   1 #third row of rankings ends
1   1   1   0   0   0   0   0   0   0 #fourth row of rankings starts
0   0   0   1   0   0   0   1   0   0
0   0   0   0   0   0   1   0   0   1
0   0   0   0   1   1   0   0   1   0 #fourth row of rankings ends

How do I do achieve this? I have a larger dataset and so a more efficient method is preferred but any help will be greatly appreciated!

CodePudding user response:

matrix(sapply(mat1, \(i) replace(numeric(4), i, 1)), ncol = ncol(mat1))
#      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
# [1,]    0    0    0    0    0    0    0    1    1     1
# [2,]    0    0    0    0    0    1    1    0    0     0
# [3,]    0    1    1    1    1    0    0    0    0     0
# [4,]    1    0    0    0    0    0    0    0    0     0
# [5,]    0    0    0    0    0    0    1    0    0     0
# [6,]    0    0    1    0    1    0    0    0    0     1
# [7,]    1    0    0    0    0    1    0    1    1     0
# [8,]    0    1    0    1    0    0    0    0    0     0
# [9,]    0    0    0    1    1    1    0    0    0     0
#[10,]    1    1    0    0    0    0    0    0    1     0
#[11,]    0    0    0    0    0    0    0    0    0     0
#[12,]    0    0    1    0    0    0    1    1    0     1
#[13,]    1    1    1    0    0    0    0    0    0     0
#[14,]    0    0    0    1    0    0    0    1    0     0
#[15,]    0    0    0    0    0    0    1    0    0     1
#[16,]    0    0    0    0    1    1    0    0    1     0

It takes 2 steps, and piping syntax may look clearer:

sapply(mat1, \(i) replace(numeric(4), i, 1)) |>  ## each value to binary vector
  matrix(ncol = ncol(mat1))  ## reshape

Actually, I don't need that anonymous function \(i). I can pass replace, and its arguments, to sapply directly.

matrix(sapply(mat1, replace, x = numeric(4), values = 1), ncol = ncol(mat1))

sapply(mat1, replace, x = numeric(4), values = 1) |> matrix(ncol = ncol(mat1))

Misc

user20650 and I discussed a little bit in comments, and here is a "vectorized" approach using outer:

matrix( outer(1:4, c(mat1), "=="), ncol = ncol(mat1))

Henrik's answer is a more memory-efficient "vectorized" approach, but it over-complicates the index computation. Here is something simpler:

out <- matrix(0, nrow(mat1) * 4, ncol(mat1))
pos1 <- seq(0, length(mat1) - 1) * 4   c(mat1)
out[pos1] <- 1

All methods so far create a dense output matrix. This is OK because the percentage of nonzero elements is 25%, which is not typically sparse. However, in case we want a sparse one, it is also straightforward:

## in fact, this is what Henrik aims to compute
ij <- arrayInd(pos1, c(4 * nrow(mat1), ncol(mat1)))
## sparse matrix
Matrix::sparseMatrix(i = ij[, 1], j = ij[, 2], x = rep(1, length(mat1)))
#16 x 10 sparse Matrix of class "dgCMatrix"
#                         
# [1,] . . . . . . . 1 1 1
# [2,] . . . . . 1 1 . . .
# [3,] . 1 1 1 1 . . . . .
# [4,] 1 . . . . . . . . .
# [5,] . . . . . . 1 . . .
# [6,] . . 1 . 1 . . . . 1
# [7,] 1 . . . . 1 . 1 1 .
# [8,] . 1 . 1 . . . . . .
# [9,] . . . 1 1 1 . . . .
#[10,] 1 1 . . . . . . 1 .
#[11,] . . . . . . . . . .
#[12,] . . 1 . . . 1 1 . 1
#[13,] 1 1 1 . . . . . . .
#[14,] . . . 1 . . . 1 . .
#[15,] . . . . . . 1 . . 1
#[16,] . . . . 1 1 . . 1 .

CodePudding user response:

Using row, col, and matrix indexing:

m = matrix(0, nr = 4 * nrow(mat1), nc = ncol(mat1))
m[cbind(c(row(mat1)   seq(0, by = (4 - 1), len = nrow(mat1))   (mat1 - 1)), 
        c(col(mat1)))] = 1

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

CodePudding user response:

Probably we can benefit from using kronecker rep like below

>  (kronecker(mat1, matrix(rep(1, 4))) == rep(1:4, nrow(mat1)))
      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
 [1,]    0    0    0    0    0    0    0    1    1     1
 [2,]    0    0    0    0    0    1    1    0    0     0
 [3,]    0    1    1    1    1    0    0    0    0     0
 [4,]    1    0    0    0    0    0    0    0    0     0
 [5,]    0    0    0    0    0    0    1    0    0     0
 [6,]    0    0    1    0    1    0    0    0    0     1
 [7,]    1    0    0    0    0    1    0    1    1     0
 [8,]    0    1    0    1    0    0    0    0    0     0
 [9,]    0    0    0    1    1    1    0    0    0     0
[10,]    1    1    0    0    0    0    0    0    1     0
[11,]    0    0    0    0    0    0    0    0    0     0
[12,]    0    0    1    0    0    0    1    1    0     1
[13,]    1    1    1    0    0    0    0    0    0     0
[14,]    0    0    0    1    0    0    0    1    0     0
[15,]    0    0    0    0    0    0    1    0    0     1
[16,]    0    0    0    0    1    1    0    0    1     0
  • Related